contact us
联系我们POSTTIME:2024-03-11 作者:佚名 点击量:
使用 Vue2 的时候,碰到了一些性能瓶颈,然后在看 Vue3 源码的时候,发现 Vue3 的优化有点意思,大多数人写过的 Vue3 优化我就不说了,说一说比较细的几个方面。
在 Vue2 中,对非数组数据的劫持是通过 Object.defineProperty,因为要确保所有数据的变化都能被监听到,不论数据数据多深,用到没,只能无脑递归。
页面加载之后,我们点击按钮,来看下性能。我们能发现响应式数据耗费了大概 8.7s,哪怕我们页面中并没有用到这个数据。
可以看到耗费时间大约 0.1s,因为没有使用脚手架创建工程,所以这个 0.1s 中有很多时间用在了模板编译上,实际上的时间就更少了。
Vue3 和 Vue2 响应式数据最大的区别就是,Vue3 是用到哪个数据,才会去对这个数据做劫持,我下面贴一段 Vue3 响应式处理的代码。
确实有点意思,我觉得可以叫做惰性劫持。
Vue3 利用编译做了一些优化,而且对性能的提升是非常高的,现在贴一段代码,看看在 Vue2,Vue3 编译过后有什么不同。
在 Vue2 编译中主要的优化是:找到静态节点=> 找到值得标记的静态根节点,静态节点就是不会变化的节点,而静态根节点就是静态节点最大的祖先,也不会变化。这样每次组件更新的时候就可以跳过这些节点,从而提升性能。
Vue3 将所有可以提升的节点全部提升,从而每次 render 的时候不会重复创建静态节点。
接着上面 Vue3 的编译继续讲,你可以看到 Vue3 编译的时候调用了 _openBlock 这个方法,下面来讲讲这个方法做了什么。
现在我们回顾刚才 render 函数里面创建 VNode 的方法,创建 VNode 的方法都会调用 createBaseVNode 这个方法,我们在下面可以看到他把当前这个 VNode 放到了下面的 currentBlock 数组里面,Vue3 是把动态节点和所有能提升的节点隔离开来,然后把当前组件所创建的 VNode 放到了一个数组里面。
现在我们继续接着 openBlock 挨着的这个方法,我们看他做了什么。
接下来我们去看看这个 dynamicChildren 到底是要做什么。
代码太多,下面以图片示例,组件 patch 的时候会调用一些 patch 的子方法,其中会对 dynamicChildren 做判断,如果有,直接用新旧两个 dynamicChildren 去对比,Vue2 中你不论怎么办,你都得从头比到脚,而在 Vue3 中,可以直接对会变化的节点进行对比,这个是之前不敢想像的。
我们平常项目开发中经常会这样使用组件。
如上所示,在 Vue2 和 Vue3 中的效果是截然不同的。
如图所示,子组件插槽的编译和 VNode 的生成都是在父组件,在 Vue2 中这种情况,只要父组件更新,子组件就会伴随着强制更新,如果我们这个子组件很大,那么就会带来不必要的性能开销。
如图所示,静态插槽被归类成了作用域插槽,也就意味着子组件插槽的编译是在父组件,但是这个生成 VNode 的函数会在子组件内调用生成,也就意味着子组件会被所使用的响应式数据收集到依赖中,也就只有这些数据发生变化时,才会通知子组件更新,子组件也不会伴随父组件更新而强制更新。
插槽的影响可以看我之前的这篇文章:理解Vue内部渲染机制(避免页面性能瓶颈)
Vue3 确实有点意思!!!