4.Vue3使用
# 1.vue3 要来了,vue2 就过时了吗
Vue3 升级内容
- 全部用 ts 重写 (响应式、vdom、模板编译等)
- 性能提升,代码量减少
- 会调整部分 API
vue 2.x 马上就要过时了吗?
- vue 3 从正式发布到推广开来,还需要一段时间
- vue 2.x 应用范围非常广,有大量项目需要维护、升级
- Proxy 存在浏览器兼容性问题,且不能 polyfill
# Vue3 比 Vue2 有什么优势 ?
- 性能更好
- 体积更小
- 更好的 ts 支持
- 更好的代码组织
- 更好的逻辑抽离
- 更多新功能
# 2.vue3 和 vue2 的生命周期有什么区别
Vue3生命周期分为两种:
- Options API 生命周期
- Composition API 生命周期
注意:Vue3仍然兼容Options API,但以Composition API为主,开发时不要混用
# 2.1 Options API 生命周期
- beforeDestory 改为 beforeUnmount
- destroyed 改为 unmounted
- 其它沿用Vue2的生命周期
# 2.2 Composition API生命周期
<script>
//组合式开发,生命周期函数需要先import,分别解构取出来再用于setup函数中
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount,}
export default {
name:'LifeCycles',
props:{
msg: String
},
// setup函数等于 beforeCreate 和 created这两个生命周期
setup(){
console.log('setup')
//Vue3的剩余六个生命周期
onBeforeMount(()=>{
console.log('onBeforeMount')
})
onMounted(()=>{
console.log('onMounted')
})
onBeforeUpdate(()=>{
console.log('onBeforeUpdate')
})
onUpdated(()=>{
console.log('onUpdated')
})
onBeforeUnmount(()=>{
console.log('onBeforeUnmount')
})
onUnmounted(()=>{
conosle.log('onUnmounted')
})
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 3.Composition API 对比 Options API
# 3.1 Composition API 带来了什么?
- 更好的代码组织
- 更好的逻辑复用
- 更好的类型推导
# 3.2 Composition API 和 Options API 如何选择?
- 不建议共用两种
- 小型简单选 Options API
- 中大型复杂选 CompositionAPI
# 3.3 别误解 Composition API
- Composition API 属于高阶技巧,不是基础必会
- Composition API 是为解决复杂业务逻辑而设计
- Composition API 就像 Hooks 在 React 中的地位
# 4. ref、toRef和toRefs
# 4.1 ref
- 生成值类型的响应式数据
- 可用于模板和 reactive
- 通过
.value
修改值(除了在模板和reactive里不需要)
示例demo
<template>
<p>refdemo {{ageRef}} {{state.neme}}</p>
</template>
<script>
import { ref, reactive } from "vue";
export default {
name: "Ref",
setup() {
const ageRef = ref(20)//值类型 响应式
const nameRef = ref('uploadhub')
const state = reactive({
name: nameRef
})
setTimeout(()=>
console.log('ageRef', ageRef. value)
ageRef.value=25//.value修改值
nameRef.value='uploadhub-A'
3,1500);
return {
ageRef,
state
},
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# ref还可用于获取模板里的DOM元素
<template>
<p ref="elemRef">我是一行文字</p>
</template>
<script>
import { ref, onMounted } from "vue";
export default {
name: "RefTemplate",
setup() {
const elemRef = ref(null);
//onMounted后才能获取
onMounted(() => {
console.log("ref template", elemRef.value.innerHTML, elemRef.value);
});
return {
elemRef,
};
},
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 4.2 toRef
- 针对一个响应式对象(reactive封装)的内在属性(prop)
- 创建一个toRef,具有响应式
- toRef指向该属性,两者保持引用关系
- toRef 如果用于普通对象(非响应式对象),产出的结果不具备响应式
const xxxRef = toRef(reactive对象, 某个属性k)
# 4.3 toRefs
- 将响应式对象(reactive封装)转换为普通对象
- 对象的每个prop都是对应的ref
- 两者保持引用关系
<template>
<p>toRefs demo {{age}} {{name}}</p>
</template>
<script>
import { ref, toRef, toRefs, reactive from ' vue'
export default {
name:'ToRefs',
setup(){
const state = reactive({
age:16,
name:'uploadhub'
})
const stateAsRefs = toRefs(state)//将响应式对象,变成普通对象
//取出来的age属性以ageRef为名
// const { age: ageRef, name: nameRef }= stateAsRefs//每个属性,都是ref对象
// return {
// ageRef,
// nameRef
// }
setTimeout(()=>{
state.age=25
},1500)
return stateAsRefs
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 5.ref、toRef 和 toRefs 的最佳使用方式
- 用reactive做对象的响应式,用ref做值类型响应式
- setup 中返回
toRefs(state)
,或者toRef(state, 'xxx')
- ref的变量命名都用
xxxRef
的格式 - 合成函数返回响应式对象时,使用toRefs
# 5.1 合成函数返回响应式对象
示例demo
function useFeatureX(){
const state = reactive({
X:1,
y:2
})
//逻辑运行状态,省略N行
//返回时转换为ref相关的结果,总之得保持响应式状态
return toRefs(state)
}
export default {
setup(){
//可以在不失去响应性的情况下破坏结构
const { x, y } = useFeatureX()
return {
X,
Y
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 6.为何需要ref?
- 返回值类型,会丢失响应式
- 如在setup、computed、合成函数等场景,都有可能返回值类型(proxy 只能对引用类型实现响应式)
- Vue如不定义ref,用户将自造ref,反而混乱
示例demo
<template>
<p> why ref demo {{stage.age}} {{age1}}</p>
</template>
<script>
import { ref, toRef, toRefs, reactive, computed } from 'vue
function useFeatureX(){
const state = reactive({
x:1,
y:2
})
// return toRefs(state)
return state
}
export default {
name:'WhyRef',
setup(){
const { x, y } = useFeaturex()
const state = reactive({
age:20,
name:'双越'
})
//age1也是响应式的
const age1 = computed(()=>{
return state.age +1
})
setTimeout(()=>{
state.age=25
},1500)
return {
state,
age1
}
})
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 7.为何ref需要value属性
- ref是一个对象(为了不丢失响应式,牢记值类型没有响应式),value属性存储值
- 通过value属性的get和set实现响应式
- 用于模板、reactive时,不需要``.value`,其他情况都需要
# 8.为什么需要 toRef 和 toRefs
- 初衷:不丢失响应式的情况下,把对象数据分解/扩散
- 前提:针对的是响应式对象(reactive封装的),而非普通对象
- 注意:不创造响应式,而是延续响应式
# 9.Vue3 升级了哪些重要功能
- createApp
- emits 属性
- 生命周期
- 多事件
- Fragment
- 移除
.sync
语法糖 - 异步组件的写法
- 移除 filter
- Teleport
- Suspense
- Composition API
# 9.1 createApp
// vue2.x
const app = new Vue({/* 选项 */})
// vue3
const app = Vue.createApp({/* 选项 */})
2
3
4
// vue2.X
Vue.use(/*...*/)
Vue.mixin(/*... */)
Vue.component(/*...*/)
Vue.directive(/*...*/)
// vue3
app.use(/*...*/)
app.mixin(/*...*/)
app.component(/*...*/)
app.directive(/*... */)
2
3
4
5
6
7
8
9
10
# 9.2 emits属性
- 用于声明由组件触发的自定义事件,可用该API实现子组件向父组件传值
- 在setup函数的this 是不指向当前实例的(this为undefined),如果想使用使用
$emit
的功能,需要借助setup(props, context)
中的context
,解构取出``emit`使用即可
//父组件
<Template>
<Hello @onSayHello="sayHello"></Hello>
</Template>
<script>
export default {
methods:{
sayHello(){
console.log('Hello Vue3')
}
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
//子组件
export default {
props: {
msg: String,
},
// 需要声明
emits: ["onSayHello"],
// 传参
setup(props, { emit }) {
emit("onSayHello", "vue3");
},
}
2
3
4
5
6
7
8
9
10
11
12
# 9.3 多事件处理
Vue3可以在事件处理程序中使用逗号分隔多个事件处理程序
<!-在 methods 里定义 one two 两个函数-->
<button @click="one($event),two($event)">
Submit
</button>
2
3
4
# 9.4 Fragment
Fragments 作为Vue3的新特性之一,允许一个vue组件可以有多个根节点。
<!--Vue2.x 组件模板-->
<template>
<div class="blog-post">
<h3>title</h3>
<div v-html="content"></div>
</div>
</template>
2
3
4
5
6
7
<!--Vue3 组件模板-->
<template>
<h3>title</h3>
<div v-html="content"></div>
</template>
2
3
4
5
# 9.5 移除.sync语法糖
<!--Vue2.x-->
<MyComponent v-bind:title.sync="title"></MyComponent>
<!--Vue3.x-->
<MyComponent v-model:title="title"></MyComponent>
2
3
4
5
# 9.6 异步组件
/* Vue2写法,借助Webpack实现*/
new Vue({
//...
components:{
'my-component':()=>import("./my-async-component.vue'
}
}
/*Vue3写法,需要引入用一下defineAsyncComponent方法*/
import { createApp, defineAsyncComponent } from 'vue'
createApp({
//...
components:{
AsyncComponent: defineAsyncComponent(()=>
import('./components/AsyncComponent.vue')
)
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 9.7 移除filter
<!--以下filter在vue3中不可用了!!!-->
<!--在双花括号中-->
{{ message | capitalize }}
<!--在`v-bind`中-->
<div v—bind:rawld | formatld"></div>
2
3
4
5
6
# 9.8 Teleport
<Teleport>
是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去
<!--data中设置modalopen:false -->
<button @click="modalOpen=true">
Open full screen modal!(With teleport!)
</button>
<teleport to="body">
<div v-if="modalopen"class="modal">
<div>
telePort弹窗(父元素是body)
<button @click="modalOpen=false">Close</button>
</div>
</div>
</teleport>
2
3
4
5
6
7
8
9
10
11
12
# 9.9 Suspense
<Suspense>
是一个内置组件,用来在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。
<Suspense>
<template>
<Test1/><!--是一个异步组件-->
</template>
<!--#fallback就是一个具名插槽。即Suspense组件内部,有两个slot,其中一个具名为fallback-->
<template #fallback>
Loading··
</template>
</Suspense>
2
3
4
5
6
7
8
9
# 9.10 主要Composition API
- ref相关
- reactive
- watch和watchEffect
- setup
- readonly
- 生命周期钩子函数
# 10.Composition API实现逻辑复用
- 抽离逻辑代码到一个函数
- 函数命名约定为useXxxx格式(React Hooks也是)
- 在setup中引用useXxx函数
# 11.Proxy 基本使用
- 回顾 Object.defineProperty
- Proxy 实现响应式
- 两者对比
Proxy 实现响应式
- 基本使用
- Reflect
- 实现响应式
Proxy 基本使用示例
const data = {
name: 'pengyouyi',
age: 20
};
const proxyData = new Proxy(data, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver)
console.log('get', key)
return result // 返回结果
},
set(target, key, val, receiver) {
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val)
console.log('result', result) // true
return result // 是否设置成功
},
deleteProxy(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('delete property', key)
console.log('result', result) // true
return result // 是否删除成功
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Proxy 基本使用 - 数组
const data = ['a', 'b', 'c']
const proxyData = new Proxy(data, {
get(target, key, receiver) {
// 只处理本身(非原型的)属性
const ownKeys = Reflect.ownKeys(target)
if(ownKeys.includes(key)) {
console.log('get', key) // 监听
}
const result = Reflect.get(target, key, receiver)
return result // 返回结果
},
set(target, key, val, receiver) {
// 重复的数据,不处理
const oldVal = target[key]
if (val === oldVal) {
return true
}
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val)
return result // 是否设置成功
},
deleteProxy(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('delete property', key)
return result // 是否删除成功
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 12.Reflect 作用
- 和 Proxy 能力一一对应
- 规范化、标准化、函数式
- 替代掉 Object 上的工具函数
Reflect 规范化、标准化、函数式
const obj = {a: 100, b: 200}
// 原 JS 语法
'a' in obj
// Reflect 语法
Reflect.has(obj, 'a')
----------------------
// 原 JS 语法
delete obj.a
// Reflect 语法
Reflect.deleteProperty(obj, 'b')
2
3
4
5
6
7
8
9
10
11
12
Reflect 替代掉 Object 上的工具函数
obj = {a: 100, b:200}
// 原 JS 语法
Object.getOwnpropertyNames(obj) // ['a','b']
// Reflect 语法
Reflect.ownKeys(obj)
2
3
4
5
6
# 13.vue3 用 Proxy 实现响应式
// 创建响应式
function reactive(target = {}) {
if (typeof target !== 'object' || target == null) {
// 不是对象或者数组,则返回
return target
}
// 代理配置
const proxyConf = {
get(target, key, receiver) {
// 只处理本身(非原型的)属性
const ownKeys = Reflect.ownKeys(target)
if (ownKeys.includes(key)) {
console.log('get', key) // 监听
}
const result = Reflect.get(target, key, receiver)
// 深度监听
// 性能如何提升的?
return reactive(result)
},
set(target, key, val, receiver) {
// 重复的数据,不处理
if (val === target[key]) {
return true
}
const ownKeys = Reflect.ownKeys(target)
if (ownKeys.includes(key)) {
console.log('已有的 key', key)
} else {
console.log('新增的 key', key)
}
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val)
// console.log('result', result) // true
return result // 是否设置成功
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('delete property', key)
// console.log('result', result) // true
return result // 是否删除成功
}
}
// 生成代理对象
const observed = new Proxy(target, proxyConf)
return observed
}
// 测试数据
const data = {
name: 'youyi',
age: 25
}
// const data = {
// name: 'zhangsan',
// age: 20,
// info: {
// city: 'beijing',
// a: {
// b: {
// c: {
// d: {
// e: 100
// }
// }
// }
// }
// }
// }
const proxyData = reactive(data)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
Proxy 对比 Object.defineProperty 性能如何提升的?
- Object.defineProperty ,defineReactive 函数中 默认一上来就 observer(value) 一次性递归
- Proxy 只在 get 的时候 return reactive(result) 深度监听,什么时候 get 什么时候深度递归
Object.defineProperty
// 触发更新视图
function updateView() {
console.log('视图更新')
}
// 重新定义数组原型
const oldArrayProperty = Array.prototype
// 创建新对象,原型指向 oldArrayProperty ,再扩展新的方法不会影响原型
const arrProto = Object.create(oldArrayProperty);
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
arrProto[methodName] = function () {
updateView() // 触发视图更新
oldArrayProperty[methodName].call(this, ...arguments)
// Array.prototype.push.call(this, ...arguments) ,同上
}
})
// 重新定义属性,监听起来
function defineReactive(target, key, value) {
// 深度监听
observer(value)
// 核心 API
Object.defineProperty(target, key, {
get() {
return value
},
set(newValue) {
if (newValue !== value) {
// 深度监听
observer(newValue)
// 设置新值
// 注意,value 一直在闭包中,此处设置完之后,再 get 时也是会获取最新的值
value = newValue
// 触发更新视图
updateView()
}
}
})
}
// 监听对象属性
function observer(target) {
if (typeof target !== 'object' || target === null) {
// 不是对象或数组
return target
}
// 污染全局的 Array 原型
// Array.prototype.push = function () {
// updateView()
// ...
// }
if (Array.isArray(target)) {
target.__proto__ = arrProto
}
// 重新定义各个属性(for in 也可以遍历数组)
for (let key in target) {
defineReactive(target, key, target[key])
}
}
// 准备数据
const data = {
name: 'zhangsan',
age: 20,
info: {
address: '北京' // 需要深度监听
},
nums: [10, 20, 30]
}
// 监听数据
observer(data)
// 测试
// data.name = 'lisi'
// data.age = 21
// console.log('age', data.age)
// data.x = '100' // 新增属性,监听不到 —— 所以有 Vue.set
// delete data.name // 删除属性,监听不到 —— 所有已 Vue.delete
// data.info.address = '上海' // 深度监听
data.nums.push(4) // 监听数组
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# 14.Proxy 实现响应式优缺点
Proxy 实现响应式优点
- 深度监听,性能更好
- 可监听 新增/删除 属性
- 可监听数组变化
Proxy 实现响应式缺点
- Proxy 无法兼容所有浏览器,无法polyfill
# 15.v-model参数的用法(移除.sync修饰符)
# 15.1 Vue 2.x 的相关语法
在 2.x 中,在组件上使用 v-model
相当于绑定 value
属性并触发 input
事件:
<ChildComponent v-model="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent :value="pageTitle" @input="pageTitle = $event" />
2
3
这里
v-model
实际上就是为表单元素定制的,input
事件和value
属性都是强耦合的
如果想要更改属性或事件名称,则需要在子组件中添加 model
选项:
<!-- ParentComponent.vue -->
<ChildComponent v-model="pageTitle" />
2
// ChildComponent.vue
export default {
model: {
prop: 'title',
event: 'change'
},
props: {
// 这将允许 `value` 属性用于其他用途
value: String,
// 使用 `title` 代替 `value` 作为 model 的 属性
title: {
type: String,
default: 'Default title'
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
某些情况下,我们可能需要对某个 prop 进行“双向绑定”,例如一个弹框组件的显示隐藏,既可以从组件外面进行控制,也可以从组件内部去控制。为此,我们可以使用 update:myPropName
的格式抛出事件。例如,对于在上一个示例中带有 title
prop 的 ChildComponent
,我们可以通过下面的方式将分配新 value 的意图传达给父级:
this.$emit('update:title', newValue)
然后父组件可以在需要时监听该事件,并更新本地的 data property。例如:
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
为了方便起见,我们可以使用 .sync (opens new window) 修饰符来缩写,如下所示:
<ChildComponent :title.sync="pageTitle" />
.sync
实际上就是上面的语法糖,可以看到其实和v-model
用法非常相似
# 15.2 Vue 3.x 的相关语法
在 Vue 3.x 中,自定义组件上的 v-model
相当于传递了 modelValue
prop 并接收抛出的 update:modelValue
事件:
<ChildComponent v-model="pageTitle" />
<!-- 上面的写法是以下的简写: -->
<ChildComponent
:modelValue="pageTitle"
@update:modelValue="pageTitle = $event"
/>
2
3
4
5
6
7
若需要更改 model
的名称,现在我们可以为 v-model
传递一个参数,以作为组件内 model
选项的替代:
<ChildComponent v-model:title="pageTitle" />
<!-- 是以下的简写: -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
2
3
4
这也可以作为 .sync
修饰符的替代,而且允许我们在自定义组件上使用多个 v-model
。
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />
<!-- 是以下的简写: -->
<ChildComponent
:title="pageTitle"
@update:title="pageTitle = $event"
:content="pageContent"
@update:content="pageContent = $event"
/>
2
3
4
5
6
7
8
9
# 16.watch和watchEffect的区别
- 两者都可监听data属性变化
- watch需要明确监听哪个属性
- watchEffect会根据其中的属性,自动监听其变化
# 16.1 watch
watch默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数
// 监听响应值
const count = ref(0)
watch(count, (newValue, oldValue) => {
console.log(newValue, oldValue)
})
// 监听响应对象
const state = reactive({
name: "Bob",
age: 2,
});
watch(
// 确定要监听对象哪个 key
() => state.age,
(new,old) => {
console.log(new, old)
},
{
immediate: true,
deep: true
}
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 16.2 watchEffect
- watchEffect立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行
- 初始化时,一定会执行一次(收集要监听的数据)
const state = reactive({
name: "Bob",
age: 2,
});
watchEffect(() => {
console.log("state.name", state.name);
});
2
3
4
5
6
7
停止侦听器
const stop = watchEffect(() => {});
// 当不再需要此侦听器时:
stop();
2
3
# 17.setup中如何获取组件实例
- 在setup和其他Composition API中没有this
- 可通过getCurrentInstance获取当前实例
- 若使用Options API可照常使用this
示例demo
<script>
import { onMounted,getcurrentInstance } from 'vue'
export default {
name:'GetInstance',
data(){
return X:1, y:2
},
setup(){
console.log('this1',this)
onMounted(()=>
console.log('this in onMounted',this)
console.log('x',instance.data.x)
})
const instance getCurrentInstance()
console.log('instance',instance)
},
mounted(){
console.log('this2',this)
console.log('y',this.y)
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 18.Vue3为何比Vue2快
主要有以下6点:
- Proxy响应式
- PatchFlag
- hoistStatic
- cacheHandler
- SSR优化
- tree-shaking
# 18.1 Patch Flag
- 编译模板时,动态节点用Patch Flag做标记
- 标记,分为不同的类型,如TEXT、PROPS等
- diff算法时,可以区分静态节点,以及不同类型的动态节点
- vue2中虚拟dom通过模板创建虚拟节点(js对象),然后使用虚拟节点跟上一次缓存的虚拟节点进行全量的对比
- vue3在与上次虚拟节点进行对比时候,只对比带有patch flag的节点,并且可以通过flag的信息得知当前节点要对比的具体内容

# 18.2 hoistStatic
- 将静态节点的定义,提升到父作用域,缓存起来
- 多个相邻的静态节点,会被合并起来
- 典型的拿空间换时间的优化策略
# 18.3 cacheHandler
- 在vue2中,针对节点绑定的事件,每次触发都要重新生成全新的function去更新。
- 而在vue3中,当cacheHandler开启的时候,编译会自动生成一个内联函数,将其变成一个静态节点,相当于React中的useCallback。这样每次就不用重复渲染了,在事件更新频繁或者绑定事件过多的情况下,性能优化非常显著。
# 18.4 SSR 优化
- 静态节点直接输出,绕过了vdom
- 动态节点,还是需要动态渲染
当有大量的静态内容的时候,这些内容会被当做字符串推进一个buffer(存储缓冲器)里面,会通过模板插值嵌入进去,这样会比虚拟dom来渲染快上很多。
当静态内容打到一定量级,会用_createStaticVNode方法在客户端去生成一个静态节点(static node),会被直接innerHTML,就不需要创建对象,然后根据对象渲染。
# 18.5 tree shaking
编译时,依据编译内容的不同,引入不同 API
在Vue3中,所有的API都通过ES6模块化的方式引入,这样就能让webpack或rollup等打包工具在打包时对没有用到的API进行剔除,最小化bundle体积
# 19.Vite为什么启动非常快
- 开发环境用 ES6 module ,无需打包——非常快
- 生产环境 使用rollup,速度没有明显提升
//ES6 module,直接 script 里写 import
<script type="module">
import { add, multi } from "./src/math.js";
</script>
2
3
4
# 20.CompositionAPI和ReactHooks对比
- 前者setup只会被调用一次,而后者函数会被多次调用
- 前者无需useMemo useCallback,因为setup只调用一次
- 前者无需顾虑调用顺序,而后者需要保证hooks的顺序一致