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 作用域插槽
作用域插槽也分为匿名作用域插槽和具名作用域插槽, 作用域插槽就是在默认插槽和具名插槽的基础上增加了“数据”的能力,即在父组件定义的插槽结构中可以使用子组件提供的数据。
- 默认作用域插槽
// 父组件
<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>
- 具名作用域插槽
// 父组件
<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的?
- 对tempalte进行编译,结合依赖收集和作用域插槽,生成渲染函数。
- 调用渲染函数,生成虚拟dom。
- 将虚拟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>