文章目录
前言、Vue3的核心变化
- Typescript,
- Proxy响应式,
- Composition解决代码反复横跳
- 虚拟DOM
一、Vue3相比Vue2的优势
-
1、速度更快 ——-
详细说明见第四大点,Vue3的虚拟DOM
1、重写了虚拟Dom实现 2、编译模板的优化 3、undate性能提高1.3~2倍 4、SSR速度提高了2~3倍
-
2、vue3支持tree-shaking,避免对不必要的文件进行打包,从而使得打包后的文件体积更小
通过webpack的tree-shaking
功能,可以将无用模块“剪辑”,仅打包需要的,这样有两大好处:1、对开发人员,不必担忧项目整体体积过大 2、对使用者,打包出来的包体积变小了
-
3、关键的api都单独暴露出来,更加方便封装。比如:
onMounted 在项目挂载时调用
,ref 使数据变成响应式,相当于注入data中
-
4、更接近原生
可以自定义渲染 API
二、Vue3.0变化的三大点
1、监测机制的改变:用Proxy替换Object.defineProperty
关于Proxy,一定程度上会带来的性能上的提升,因为传统的原型链拦截的方法,无法检测对象及数组的一些更新操作,但使用Proxy又带来了浏览器兼容问题。
Proxy 是什么
-
Proxy为 构造函数,用来生成 Proxy实例
var proxy = new Proxy(target, handler) //target 表示所要拦截的目标对象(任何类型的对象,包括原生数组,函数) // handler通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为
-
关于handler拦截属性,有如下13种
- 1、get(对象属性的读取)、
- 2、set(对象属性的设置)、
- 3、apply(对函数的调用call、apply操作进行拦截)、
- 4、has( 用于拦截hasProperty操作,返回一个布尔值)、
- 5、deleteProperty (拦截delete操作,返回一个布尔值)
- 6、defineProperty ( 拦截Object.defineProperty操作)
- 7、getOwnPropertyDescriptor (用于拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined)
- 8、getPrototypeOf (用于拦截获取对象的原型)
- 9、isExtensible (拦截Object.isExtensible()操作)
- 10、ownKeys (用于拦截对象自身属性的读取操作,比如:Object.keys ()、Object.getOwnPropertySymbols ()、Object.getOwnPropertyNames ())
- 11、preventExtensions (该方法拦截 Object.preventExtensions () ,该方法必须返回个布尔值)
- 12、setPrototypeOf ( Object.setPrototypeOf 方法 )
- 13、construct (拦截 new 操作符命令)
-
若需要在Proxy内部调用对象的默认行为,建议使用Reflect,
var person = { name: "张三" }; var proxy = new Proxy(person, { get: function(target, propKey) { return Reflect.get(target,propKey) } }); proxy.name // "张三"
proxy更多了解,请参考https://mp.weixin.qq.com/s/rIqznB3WXT6M4T0YQb6CQA
Vue3.0 里为什么要用 Proxy API 替代 defineProperty API?
响应式优化。
-
1、defineProperty API 的局限性,就是它只能针对单个属性做监听。
Vue2.x 中的响应式实现正是基于 defineProperty ,对 data 中的属性做了遍 历 + 递归,为每个属性设置了 getter、setter
。 这也就是为什么 Vue 只能对 data 中预定义过的属性做出响应的原因。defineProperty的局限性
-
对于数组来说,
通过下标的方式
直接修改属性的值,或者添加一个预先不存在的属性,是无法监听到数据变化的, -
对于对象来说,
只能劫持对象的属性
,从而需要对每个对象的属性进行遍历,如果,属性值是对象,还需要深度遍历。这就是 defineProperty 的局限性
-
2、Proxy API 的监听是针对一个对象的,那么对这个对象的所有操作都会进入监听操作,这就完全可以代理所有属性,将会带来很大的性能提升和代码的优化。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须 先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
-
3、响应式是惰性的
在 Vue.js 2.x 中,对于一个深层属性嵌套的对象,
要劫持它内部深层次的变化,就需要 递归遍历这个对象,执行 Object.defineProperty 把每一层对象数据都变成响应式的
,这 无疑会有很大的性能消耗。
在 Vue.js 3.0 中,使用Proxy API 并不能监听到对象内部深层次的属性变化,因此它的 处理方式是在 getter 中去递归响应式
,这样的好处是真正访问到的内部属性才会变成响 应式,简单的可以说是按需实现响应式,减少性能消耗。
Proxy 区别于 Object.definedProperty
-
是否可以监听属性的读写、删除、方法的调用
Object.defineProperty 只能监听到属性的读写,而Proxy
除读写外,还可以监听属性的删除,方法的调用等。 -
是否可以监听数组、对象的变化
Object.defineProperty 无法监测数组、对象的变化。而 Proxy 可以直接监视数组、对象的变化。Vue中是通过
对重写数组/对象的方法进行重写来实现监听数组/对象的变化的
-
监听方式
Proxy 是以非入侵的方式监管了对象的读写,而 defineProperty 需要按特定的方式定义对象的属性。let list = [1, 2, 3]; let listproxy = new Proxy(list, { set(target, property, value) { target[property] = value; return true; // 标识设置成功 } }); list.push(4);
-
Proxy对象可以拦截什么?
总共13个,见上方。
Vue3有了解过吗?能说说跟Vue2的区别吗?Vue3性能优化的地方在哪里?静态标记
2、作用域插槽方面
模板方面没有大的变更,只改了作用域插槽,
2.x 的机制导致作用域插槽变了,父组件也会重新渲染
,
3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染
,提升了渲染的性能。
3、对象式的组件声明方式
Vue2.x 中的组件是通过声明的方式传入一系列 option
Vue3.0 修改了组件的声明方式,改成了类式的写法
,这样使得和 TypeScript 的结合变得很容易。
TypeScript遵循最新的 ES6、 ES5 规范,从语言层面上扩展了JavaScript的语法。 (比如来说,javascript为弱类型语言,而typescript则可以通过类型注释来提供编译时的静态类型检查,从而一定程度上防止报错)
typescript对比javascript,参考链接 https://www.jianshu.com/p/0dfbcd4a0757
4、生成 Block tree
Vue.js 2.x
的数据更新并触发重新渲染的粒度是组件级的。在 2.0 里,渲染效率的快慢与组件大小成正相关:组件越大,渲染效率越慢。并且,对于一些静态节点,又无数据更新,这些遍历对比就是对性能浪费。
Vue.js 3.0
做到了通过编译阶段对静态模板的分析,编译生成了 Block tree。
Block tree 是一个基于动态节点指令将模版进行切割所形成的区块
,每个区块内部的节点结构是固定的, 每个区块只需要追踪自身包含的动态节点。所以,在 3.0 里,渲染效率不再与模板大小 成正相关,而是与模板中动态节点的数量成正相关。
5、其它方面的更改
1). 支持自定义渲染器,从而使得 weex 可以通过自定义渲染器的方式来扩展,而不是直接 fork 源码来改的方式。
2). 支持多个根节点
3). 基于 tree shaking进行项目优化,使得项目打包后的体积更小,运行更快。
Tree shaking
是一种通过清除多余代码的方式,来优化项目打包体积的技术,
其原理是基于ES6模板语法(import与exports),在编译时就能确定模块的依赖关系,以及输入和输出的变量。
tree shaking的参考链接:https://www.jianshu.com/p/975073fef572
参考链接: https://www.jianshu.com/p/4db4b191de06
三、非兼容变更
1、模板指令
- 组件上 v-model 用法已更改
- <template v-for>和 非 v-for节点上key用法已更改
- 在同一元素上使用的 v-if 和 v-for 优先级已更改
- v-bind=“object” 现在排序敏感
- v-for 中的 ref 不再注册 ref 数组
2、组件
- 只能使用普通函数创建功能组件
- 异步组件现在需要 defineAsyncComponent 方法来创建
四、Vue3的虚拟dom
传统Vdom的缺点
虽然∨ue能够保证进行最小化的更新,节约浏览器的渲染性能,但却新增了新旧Vdom对比的性能消耗。因此, vdom的大小就直接影响了对比的快慢,而vdom中有一部分是静态标签,不会改变,但在对比时,仍会进行遍历,因此就会增加性能的消耗。 所以在vue3.x中新增了静态标记,对于标记了静态标记的标签,diff时不再对比。这样就优化了diff的速率。
Vue3性能的优化,具体表现为:
- upadte性能提升1.3~2倍,
- ssr(服务器端渲染)提升2~3倍
Vue3编译速度上,性能优化的地方:
- 编译模版的静态标记
- 内联事件缓存
1、编译模版的静态标记
我们来看一段很常见的代码
<div id="app">
<h1>技术摸鱼</h1>
<p>今天天气真不错</p>
<div>{
{
name}}</div>
</div>
vue2中会解析
function render() {
with(this) {
return _c('div', {
attrs: {
"id": "app"
}
}, [_c('h1', [_v("技术摸鱼")]), _c('p', [_v("今天天气真不错")]), _c('div', [_v(
_s(name))])])
}
}
其中前面两个标签是完全静态的,后续的渲染中不会产生任何变化
,
Vue2中依然使用_c新建成vdom,在diff的时候需要对比,有一些额外的性能损耗
vue3中的解析结果
import {
createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"
export function render(_ctx, _cache) {
return (_openBlock(), _createBlock("div", {
id: "app" }, [
_createVNode("h1", null, "技术摸鱼"),
_createVNode("p", null, "今天天气真不错"),
_createVNode("div", null, _toDisplayString(_ctx.name), 1 /* TEXT */)
]))
}
// Check the console for the AST
_createVNode新增第四个参数:
- 如果是静态节点,则不添加第四个参数,则在对比时不遍历,
- 如果非静态节点,则根据动态变化的地方,添加不同的标记,比如:text,props等动态变化,则添加不同的标记,这样再diff的时候,只需要对比text或者props,不用再做无谓的props遍历,这样update性能就会提高。
- 如果一个节点是有多个动态变化的地方,比如说既有id的动态绑定,又有text的绑定,根据位运算符进行组合即可,比如:
<div id="app">
<h1>技术摸鱼</h1>
<p>今天天气真不错</p>
<div :id="userid"">{
{
name}}</div>
</div>
编译后
import {
createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"
export function render(_ctx, _cache) {
return (_openBlock(), _createBlock("div", {
id: "app" }, [
_createVNode("h1", null, "技术摸鱼"),
_createVNode("p", null, "今天天气真不错"),
_createVNode("div", {
id: _ctx.userid,
"\"": ""
}, _toDisplayString(_ctx.name), 9 /* TEXT, PROPS */, ["id"])
]))
}
text是1,props是8,组合在一起就是9,我们可以简单的通过位运算来判定需要做text和props的判断, 按位与即可,只要不是0就是需要比较
Vue3静态标记不对比,提高更新渲染速度的示意图
2、事件缓存
绑定的@click会存在缓存里
链接
<div id="app">
<button @click="handleClick">戳我</button>
</div>
编译后
export function render(_ctx, _cache) {
return (_openBlock(), _createBlock("div", {
id: "app" }, [
_createVNode("button", {
onClick: _cache[1] || (_cache[1] = $event => (_ctx.handleClick($event)))
}, "戳我")
]))
}
五、vue3和react fiber的vdom
1、Vue2.x
引入vdom,控制了颗粒度,组件层面走watcher通知, 组件内部走vdom做diff
,既不会有太多watcher,也不会让vdom的规模过大,这样就不会使diff超过16ms。
2、React 16 Fiber
React走了另外一条路,既然主要问题是diff导致卡顿,于是React走了类似requestIdleCallback的cpu调度的逻辑,**把vdom这棵树,变成了链表**,利用浏览器的空闲时间来做diff,如果超过了16ms,有动画或者用户交互的任务,就把主进程控制权还给浏览器,等空闲了继续
,
Vue3通过Proxy响应式+组件内部vdom+静态标记,把任务颗粒度控制的足够细致,所以也不太需要React的Fiber了
六、Vue3.0 是如何变得更快的?
- 1、diff 方法优化
Vue2.x 中的虚拟 dom 是进行全量的对比。
Vue3.0 中新增了静态标记(PatchFlag):新旧虚拟结点进行对比的时候,只对比 带有静态标记的节点。这样就优化了对比量,提高了对比速度。 - 2、静态提升
Vue2.x : 无论元素是否参与更新,每次都会重新创建。
Vue3.0 : 对不参与更新的元素,只会被创建一次,之后会在每次渲染时候被不停的复用。 - 3、事件侦听器缓存
默认情况下 onClick 会被视为动态绑定,所以每次都会去追踪它的变化,
但是在Vue中,绑定的方法会被缓存起来,下次更新直接复用即可。
参考链接:https://mp.weixin.qq.com/s/dgC6zOkpo1ghyUYS680oWw
参考链接:https://juejin.cn/post/6844904134647234568
今天的文章vue2还有必要学吗_vue2还有必要学吗[通俗易懂]分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/87432.html