阅读视图

发现新文章,点击刷新页面。

Vue模板知识点

前言

Vue的两大特征是模板化、组件化。模板的优点是提高开发效率,按照规定的结果书写代码就能够快速完成页面开发。缺点也很明显,就是固定的模板结构牺牲了一定的灵活性,Vue提供了一系列的API来增加模板的灵活性。

一、动态数据

Mustache

 <template>
   <span>{{msg}}</span>
   <span>{{count > 99 ? 99: count}}</span>
 </template>

Mustache不仅支持数据、还支持method、computed、逻辑运算等。这是因为Vue在解析模板的时候会对Mustache里面的内容进行判断,包装成不同的解析函数,如果是数据直接返回,如果是逻辑运和函数则取运算结果。

computed和watch

思想上,computed注重运算结果,watch注重过程。watch直接进行拦截监听,数据变化时运行一些逻辑,computed则需要先进行依赖收集,对依赖进行监听,当依赖发变化的时候触发重新计算。computed是vue自发的进行依赖的收集监听。并在依赖变化时执行对应的渲染函数触发视图的更新,而watch则可以添加更多开发者自定义的逻辑。

特点:computed自发的,不可以控制,watch灵活可控,wathch有immediate、once、deep等。

<template>
 <div>选择了{{ choose }}件商品</div>
 <div>总价是:{{ totalPrice }}</div>
 <button @click="addChoose">add</button>
</template>

<script>
export default {
 name: "App",
 data() {
   return {
     choose: 0,
     price: 19.9,
   };
 },
 methods: {
   addChoose() {
     this.choose++;
   },
 },
 computed: {
   totalPrice() {
     return this.choose * this.price;
   },
 },
 watch: {
   choose(newVal, oldVal) {
     console.log(`choose从${oldVal}变成了${newVal}`);
   },
 },
};
</script>

watch中使用deep、immediate、once

  watch: {
   goodsInfo: {
     handler(newVal, oldVal) {},
     deep: true, // 深度监听,监听子属性
     immediate: true, // 初次赋值的时候也触发监听回调
     once: true, // 只监听一次
   },
 },

二、 动态结构(slot)

插槽: 父组件定义结构,子组件指定结构的位置。插槽分为三种:

  • 默认插槽
  • 具名插槽
  • 作用域插槽

2.1 默认插槽

// 父组件
<template>
 <HelloWorld>
   <span>这是传递给子组件的默认插槽</span>
 </HelloWorld>
</template>

<script>
import HelloWorld from "./HelloWorld";
export default {
 name: "App",
 components: {
   HelloWorld,
 },
};
</script>

// 子组件
<template>
 <div class="child-component">
   <slot></slot>
 </div>
</template>

<script>
export default {
 name: "HelloWorld",
};
</script>

2.2 具名插槽

默认插槽只能定义一个结构,如果想定义多个结构就要使用具名插槽。其中v-slot:header可以缩写为#header。

// 父组件
<template>
 <HelloWorld>
   <template v-slot:header> <span>header</span> </template>
   <template v-slot:content> <span>header</span> </template>
   <template #footer> <span>header</span> </template>
 </HelloWorld>
</template>

<script>
import HelloWorld from "./HelloWorld";
export default {
 name: "App",
 components: {
   HelloWorld,
 },
};
</script>

// 子组件
<template>
 <div>
   <span>HelloWorld</span>
   <slot name="header"></slot>
   <slot name="content"></slot>
   <slot name="footer"></slot>
 </div>
</template>

<script>
export default {
 name: "HelloWorld",
 data() {
   return {
     user: {
       name: "jack",
       age: 21,
     },
     date: Date.now(),
   };
 },
};
</script>

默认插槽只有一个所以没必要给它起名字,但实际上默认插槽也有自己的名字:default

   <slot></slot>

   // 等同于上面写法
   <slot name="default"></slot>

2.3 作用域插槽

作用域插槽也分为匿名作用域插槽和具名作用域插槽, 作用域插槽就是在默认插槽和具名插槽的基础上增加了“数据”的能力,即在父组件定义的插槽结构中可以使用子组件提供的数据。

  1. 默认作用域插槽
// 父组件
<template>
  <HelloWorld v-slot:default="slotProps">
    {{ slotProps.user.name }}
    {{ slotProps.user.age }}
    {{ slotProps.date }}
  </HelloWorld>
</template>

<script>
import HelloWorld from "./HelloWorld";
export default {
  name: "App",
  components: {
    HelloWorld,
  },
};
</script>

// 子组件
<template>
  <div>
    <slot :user="user" :date="date"></slot>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      user: {
        name: "jack",
        age: 21,
      },
      date: Date.now(),
    };
  },
};
</script>

针对默认具名插槽下面的三种书写是等效的:

  <HelloWorld v-slot:default="slotProps">
    {{ slotProps.user.name }}
    {{ slotProps.user.age }}
    {{ slotProps.date }}
  </HelloWorld>

   <HelloWorld v-slot="slotProps">
    {{ slotProps.user.name }}
    {{ slotProps.user.age }}
    {{ slotProps.date }}
  </HelloWorld>

    <HelloWorld #default="slotProps">
    {{ slotProps.user.name }}
    {{ slotProps.user.age }}
    {{ slotProps.date }}
  </HelloWorld>
  1. 具名作用域插槽
// 父组件
<template>
  <HelloWorld>
    <template #user="userProps">
      {{ userProps.user.name }}
      {{ userProps.user.age }}
    </template>
    <template #date="dateProps">
      {{ dateProps.date }}
    </template>
  </HelloWorld>
</template>

<script>
import HelloWorld from "./HelloWorld";
export default {
  name: "App",
  components: {
    HelloWorld,
  },
};
</script>

// 子组件
<template>
  <div>
    <slot name="user" :user="user"></slot>
    <span>content</span>
    <slot name="date" :date="date"></slot>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      user: {
        name: "jack",
        age: 21,
      },
      date: Date.now(),
    };
  },
};
</script>

三、模板的编译过程

Vue是如何将 .vue文件渲染成html的?

  1. 对tempalte进行编译,结合依赖收集和作用域插槽,生成渲染函数。
  2. 调用渲染函数,生成虚拟dom。
  3. 将虚拟dom渲染成真实dom。

虽然在vue中可以直接写render函数,但最好还是使用vue模板,因为这更符合vue的模板理念,更重要的是vue在对模板的编译过程中做了很多优化,比如dom diff算法,事件处理,渲染函数缓存等。

使用render函数的写法如下:

<script setup>
import { h } from 'vue';
</script>

<script>
export default {
  name: 'HelloWorld',
  render() {
    return h('div', 'Hello, World!');
  }
};
</script>
❌