1、Vue 3.0 性能提升主要是通过哪几方面体现的?
- 响应式系统升级
- 编译升级
- 源码体积的优化
在性能方面Vue.js 3.x大幅度提升,使用代理对象Proxy重写了响应式的代码并且对编译器做了优化重写了虚拟DOM,从而让渲染的Update的性能都有了大幅度的提升
另外,官方介绍服务端渲染的性能也提升两到三倍
响应式系统升级
- Vue.js 2.x中响应式系统的核心defineProperty
- 初始化时遍历data中的所有成员,通过defineProperty把对象的属性转换成getter和setter,如果data中的属性又是对象的话,需要递归处理每一个子对象的属性。这些都是初始化时进行的,如果你未使用这些属性也会进行响应式的处理
- Vue.js 3.x中使用Proxy对象重写响应式系统
- Proxy的性能本身就比defineProperty好,切代理对象可以拦截属性的访问、赋值、删除等操作,不需要初始化时遍历所有的属性,如果有多层属性嵌套只有访问某个属性时才会递归处理下一级属性
- 使用Proxy对象默认可以监听动态新增的属性,而Vue.js 2.x想要动态添加响应式属性需要调用Vue.set方法来处理
- Vue.js 2.x监听不到属性的删除
- Vue.js 2.x对数组的索引和length属性也监听不到
除了响应式系统的升级,Vue.js 3.x通过优化编译的过程和重写虚拟DOM让首次渲染和更新的性能有了大幅度提升
编译升级
Vue.js 2.x模板首先需要编译成render函数,这个过程一般在构建时完成,在编译时会编译静态根节点和静态节点,静态根节点要求节点中必须有一个静态子节点
当组件的状态发生变化后会通知watcher触发update去执行,虚拟DOM的patch操作,遍历所有的虚拟节点找到差异更新到真实DOM上,diff的过程中会去比较整个虚拟DOM,先对比新旧div以及它的属性再对比内部子节点
Vue.js 2.x中渲染最小的单位是组件,diff的过程会跳过静态根节点,因为惊天大根节点的内容不会发生变化,即
- Vue.js 2.x中通过标记静态根节点,优化diff的过程,但是静态节点还需要进行diff,没有被优化
Vue.js 3.x中标记和提升所有静态根节点,diff的时候只需要对比动态节点内容
Vue.js 3.x中移除了一些不常用的API
- 例如:inline-template、filter等
- tree-shaking
2、Vue 3.0 所采用的 Composition Api 与 Vue 2.x使用的Options Api 有什么区别?
- OptionsAPI
- 包含一个描述组件选项(data、methods、props等)的对象
- Options API开发复杂组件,同一个功能逻辑的代码被拆分到不同选项
- Composition API
- Vue.js 3.x新增的一组API
- 一组基于函数的API
- 可以更灵活的组织组件的逻辑
相对于OptionsAPI这样做的好处:查看某个逻辑时只需关注具体的函数即可,当前的逻辑代码都封装在函数内部,不像OptionsAPI时获取功能代码的位置分散在不同位置,查看这部分代码还需要上下拖动查看
3、Proxy 相对于 Object.defineProperty 有哪些优点?
- proxy更为强大
Object.defineProperty()只能见识属性的读写,Proxy能监视到更多的对象操作。例如delete操作、对对象方法的调用等。
const person = {
name: 'peiyp',
age: 27
}
const personProxy = new Proxy(person, {
deleteProperty(target, property) {
// 代理目标对象,要删除的属性名称
console.log('delete', property)
delete target[property]
}
})
delte personProxy.age
console.log(person)
delete age
{ name: 'peiyp' }
这也就表明Proxy确实能做到defineProperty做不到的事情,除了delete以外还有许多其他的对象操作都可以监视到。
- Proxy更好的支持数组对象的监视
以往想通过Object.defineProperty()去监视数组的操作,最常见的一种方式就是通过重写数组的操作方法(vue.js使用的方式),大体的思路就是通过自定义的方法去覆盖掉数组原先对象的push、shift等方法一次去劫持对应这个方法调用的过程。如何使用Proxy对象监视数组:
const list = []
const listProxy = new Proxy(list, {
// 监视数据写入
set(target, property, value) {
// 得到的属性名 属性值
console.log('set', property, value)
// 设置目标对象当中所对应的属性
target[property] = value
// 表示设置成功
return true
}
})
listProxy.push(100)
listProxy.push(100)
set 0 100
set length 1
set 1 100
set length 2
这里的0实际上就是数组当中的下标,100就是0这个下标对应的值。这也就表示Proxy内部会自动根据push操作去推算出来他应该所处的下标。数组的其他操作方式都是类似的。
- Proxy是以非入侵的方式监管了对象的读写
也就是说一个已经定义好的对象,不需要对对象本身做任何操作就可以监视到内部成员的读写。而Object.defineProperty()就要求我们必须通过特定的方式单独定义对象中需要被监视的属性。
// defineProperty方式
const person = {}
Object.defineProperty(person, 'name', {
get() {
console.log('name被访问')
return person_name
},
set(value) {
console.log('name被设置')
peroson._name = value
}
})
Object.defineProperty(person, 'age', {
get() {
console.log('age被访问')
return person_age
},
set(value) {
console.log('age被设置')
peroson._age = value
}
})
person.name = 'jack'
console.log(person.name);
-------------------------------
name被设置
name被访问
jack
// Proxy方式更为合理
const person2 = {
name: 'jack',
}
const personProxy = new Proxy(person2, {
get (target, property) {
console.log('get', property)
return target[property]
},
set (target, property, value) {
console.log('set', property, value)
target[property] = value
}
})
personProxy.name = 'jack'
console.log(personProxy.name)
-------------------------------------
set name jack
get name
jack
4、Vue 3.0 在编译方面有哪些优化?
Vue.js 2.x模板首先需要编译成render函数,这个过程一般在构建时完成的,在编译时会编译静态根节点和静态节点,静态根节点要求节点中必须有一个静态子节点。当组件的状态发生变化后会通知watcher触发update去执行虚拟DOM的patch操作,遍历所有的虚拟节点找到差异更新到真是DOM上,diff过程中会去比较整个虚拟DOM,先对比新旧div以及他的属性再对比内部子节点。Vue.js 2.x中渲染最小的单位是组件,diff的过程会调过静态根节点,因为静态根节点的内容不会发生变化,即
- Vue.js 2.x中通过标记静态根节点,优化diff的过程,但是静态节点还需要进行diff,没有被优化
- Vue.js 3.x中标记和提升所有静态根节点,diff的时候只需要对比动态节点内容
- Fragments(VS Code需要升级vetur插件):模板中不需要再创建唯一的根节点,可以直接放文本内容或者多个同级标签
- 静态提升
- Patch flag
- 缓存事件处理函数
5、Vue.js 3.0 响应式系统的实现原理?
- Proxy对象实现属性监听
- Vue3重写了响应式系统,和Vue2相比Vue3的响应式系统底层采用Proxy对象实现。在初始化的时候不需要遍历所有的属性,再把属性通过defineProperty转换成getter和setter
- 多层嵌套,在访问属性过程中处理下一级属性
- 如果有多层属性嵌套的话,只有访问某个属性的时候才会递归处理下一级属性,所有Vue3中响应式系统的性能要比Vue2好
- 默认监听动态添加的属性
- 默认监听属性的删除操作
- 默认监听数组索引和length属性
- 可以作为单独的模块使用
核心方法:
- reactive、ref、toRefs、computed
- effect
- track
- trigger
watch/watchEffect是Vue3的runtime.core中实现的,watch函数的内部其实实现了一个底层函数effect,我们会模拟实现effect函数以及Vue3中收集依赖和触发更新的函数track和trigger