vue3的全面提升
前言: vue框架是采用MVVM的架构进行开发的,M就是model,就是数据层,进行数据的逻辑判断和执行,V是指View,指的是视图层,VM指的是数据的响应和渲染,是连接数据层和视图层的桥梁,可以理解为vue的响应式系统,vue通过观察者模式和采用代理的方式进行数据劫持和数据响应,实现了响应式系统,通过VNode,diff算法,还有对于一些事件的特殊定制,比如特制的指令操作和绑定操作,组件化处理,进行模版渲染操作,再和响应式系统、编译器联合,就形成了以数据驱动视图的声明式框架
问题来源
对于vue2来说,可以让开发者更好的上手进行vue的页面开发,但是这也带来了一些弊端,也是在对于熟练使用vue后,最让人难受的地方,它的捆绑模式太严重,导致为了调用某个值或者某个功能,让开发者进行一些冗余代码的编写,比如,函数的嵌套,this的滥用而导致一些逻辑错乱等等,这些问题在vue3的设计中,进行了全面的升级,它对于vue2是一次全新变革
架构的升级
vue3从架构层面来说,基于跨平台的优化,在渲染器、响应式、编译器、组件化,运行时等方面进行了拆分,使vue拥有跟好的跨平台性,首先,vue3对渲染器的优化,将渲染逻辑进行单独的封装,只要开发者将相关的js平台的渲染API按照格式传入渲染函数中,浏览器就会根据渲染逻辑进行时图的渲染,不在局限于dom的操作,像小程序,app等平台有了更好的兼容,其次,开发者可以单独进行响应式系统的使用,不像vue2中,响应式系统只是对vue2进行服务的,它的响应式系统,甚至可以进行React的代理响应
从setup函数说起
首先,vue3中采用在setup函数中进行代码的编写,这个函数可以更好的契合函数式的写法,相较于vue2的选项式options API的写法,函数式的写法更利于逻辑的梳理,不用像vue2一样,看一个逻辑反复跳动,而且,这种写法更像是一种编程式的写法,符合代码阅读和范式的一种规范性
然后,vue3的中将this进行undefinde的定义,通过单独调用API的方式,进行当前代码功能的定义,防止总是this形式的单一调用,避免像vue2中嵌套捆绑所导致的拓展性差和高耦合。
还有,对于功能复用,在vue2中,想要复用代码,需要通过minxs的方式进行混入,而函数式的写法,可以将一些公用的功能进行单独封装,不用minxs的方式,更加灵活
对于生命周期上,去掉了create和beforeCreate,实际是将其封装到setup函数中执行,在setup中进行了vue的实例化,并且对于mounted函数之类的钩子函数,可以进行多次调用,更好的理顺代码逻辑
关于响应式系统
首先,vue2中采用的defindepropertype,其实是个伪代理,他本质上是通过对 对象 的属性修改的形式进行代理,这个有个缺点就是,对于代理的obj来说,如果没有代理的属性,definepropertype就不会进行拦截,可以这种做法,是一种懒代理,而vue3采用了proxy对象进行代理,在es6中进行语法上的定义,对obj对象,会进行全面的代理,不管是否有这个属性,都会同意拦截处理,像vue2中对obj对象的动态添加,或者是数组的动态添加或者是长度变化,在proxy中都得到了很好的解决,还增加了对于es6新增的数据类型(set,map等)的响应式代理功能
其次,响应式系统对于代理对象,才用了weakMap这种弱类型的数据结构,对于垃圾对象的回收和计算机内存的利用率都有明显的提升
还有对于computed和watch的功能,进行重新的定义,在computed中,通过缓存和调度器的方式,进行实现懒值的一个处理,在watch中,首先进行了首次渲染默认不执行以及首次执行的watchEffect的定义,通过参数进行watch的设定,包括回调函数的调度时机和watch的数量,并且返回的值可以控制watch的结束,他们内部都默认进行了响应式的处理
对于组件的见的通讯,进行了emit和props的定义,都单独封装了API进行使用,包括对外暴露的功能,通过expose导出,增加了编码美感,移除了原来冗余的filters的功能
对于ts有了跟友好的支持,vue3框架就是用ts进行编写的,对于ts的支持是毋庸置疑的,另外,从写法上对于ts的支持,vue2有需要通过修饰器等辅助,vue3可以直接进行使用,比较方便,还有,对于vue2将实例的所有内容都绑定到this上的操作,其实,对于ts的类型定义和类型推导,是很麻烦的事情,vue3的拆分,反而更有利于类型推导和定义,从编码上,提高了代码的健壮性
渲染器优化
首先,patch函数的diff算法,采用快速diff算法,这个算法是通过预处理和寻找最长递增子序列的算法,将在原先的双端diff算法的基础上,提升了dom操作性能
什么是预处理,不做过度讨论,后面会进行详细的讲解 什么是最长递增子序列,不过度讨论,后面进行详细讲解,不过,源码中对于getSequence的实现如下:
在一个数组中获取最长递增子序列:
//core/packages/runtime-core/src/renderer.ts
function getSequence(arr: number[]): number[] {
const p = arr.slice()
const result = [0]
let i, j, u, v, c
const len = arr.length
for (i = 0; i < len; i++) {
const arrI = arr[i]
if (arrI !== 0) {
j = result[result.length - 1]
if (arr[j] < arrI) {
p[i] = j
result.push(i)
continue
}
u = 0
v = result.length - 1
while (u < v) {
c = (u + v) >> 1
if (arr[result[c]] < arrI) {
u = c + 1
} else {
v = c
}
}
if (arrI < arr[result[u]]) {
if (u > 0) {
p[i] = result[u - 1]
}
result[u] = i
}
}
}
u = result.length
v = result[u - 1]
while (u-- > 0) {
result[u] = v
v = p[v]
}
return result
}
采用fragment(片段)的方式,通过新增的fragment的元素类型处理方式,解决了在vue2中只能进行一个根节点的操作,提升了组件化的效率
新增了teleport内置组件和异步组件的方式,提升了在渲染方面的效率和多样化处理方式
对于v-if和v-for的不能并存问题,由原先的v-for的优先级大于v-if,改为了v-if大于v-for
编译器
对于编译优化,通过编译在分析模版的过程中提取了关键字信息patchFlag,将其存放在编译好的vnode中,再将这些节点分成动态节点和静态节点,并根据动态节点生成一个block树,对于静态节点进行静态提升,还采用对静态节点预字符串化和v-once的方法,减少dom的创建和创建操作vnode的开销,以及缓存内联事件的方式,减少不必要组件的跟新
tree-sharing,由于vue3中的拆分,以及esm的方式导入和导出,可以在打包过程中未使用的代码进行清路,减小打包体积
总结:
基于vue3的升级,是编码风格进行规范化,写法上更范式,通过拆分的方式,我们可以像落积木的方式,进行代码功能的实现,代理也基于语法层面进行解决,还采用vite进行结合,更快的提高效率,但是,他也带来一些不好的地方,在与只能使用带有ESM的浏览器或者其他的平台,对于ESM的依赖性很大。
转载自:https://juejin.cn/post/7251895470548041783