加载中......
输入验证码,即可复制
微信扫码下载好向圈APP, 登陆后即可进入消息页面查看验证码
只需要3秒时间
Vue3.0性能提升


  • 响应式系统升级
  • 收集依赖
  • reactive
  • ref
  • toRefs
  • computed
  • 编译优化
  • 源码体积优化
响应式系统升级

Vue3.0相比于2.x版本做了响应式系统的升级,在Vue2.x版本中是通过defineProperty去设置响应式,但在3.0中采取了Proxy的方式,这样做有三个优点

  • 可以监听动态添加对象的属性
  • 可以监听删除的属性
  • 可以监听数组的索引以及数组的length属性
收集依赖

收集依赖的过程主要是通过三个函数,这三个函数分别是

  • effect 处理副作用的函数 它会接受一个函数作为参数 并且执行这个函数 并把这个函数存储起来 在收集依赖完毕后 重置
  • track 建立关系 首先声明一个weakMap 用来建立对象和值得关系 对象 target 值 map 这个值是一个map对象 用来建立对象中的属性和属性值的关系 对象 key 值 set map中的值是一个set的数据结构 用来描述属性值 通过key 获取值
  • trigger 通过target key 去获取weakMap中储存的响应式对象 找到后获取到存取的值并返回
编译时对VDom的性能优化

PatchFlag

首先看下面这个案例,模版中有三个P标签,其中只有最后一个P标签的TEXT部分是动态的



图文整理 Vue3.0性能提升 Vue3主要是通过哪几个点体现-1.jpg



在之前的VDOM中,如果msg值发生改变,整个模版中的所有元素都需要重新渲染。但在Vue3.0中,在这个模版编译时,编译器会在动态标签末尾加上 /* Text*/ PatchFlag。只能带patchFlag 的 Node 才被认为是动态的元素,会被追踪属性的修改。并且 PatchFlag 会标识动态的属性类型有哪些,比如这里 的TEXT 表示只有节点中的文字是动态的。
每一个Block中的节点,就算很深,也是直接跟Block一层绑定的,可以直接跳转到动态节点而不需要逐个逐层遍历。
既有VDOM的灵活性,又有性能保证。
hoistStatic 静态节点提升

当使用hoistStatic时,所有 静态的节点都被提升到render方法之外。这意味着,他们只会在应用启动的时候被创建一次,而后随着每次的渲染被不停地复用。



图文整理 Vue3.0性能提升 Vue3主要是通过哪几个点体现-2.jpg



在大型应用中对于内存有很大的优化。
cacheHandler 事件监听缓存

正常情况下,当绑定一个事件:
<div>  <p @click="handleClick">静态代码</p></div>模版会被编译为
export function render(_ctx, _cache) {  return (_openBlock(), _createBlock("div", null, [    _createVNode("p", { onClick: _ctx.handleClick }, "静态代码", 8 /* PROPS */, ["onClick"])  ]))}其中事件会每次从全局上下文中获取。而当开启了cacheHandler之后
export function render(_ctx, _cache) {  return (_openBlock(), _createBlock("div", null, [    _createVNode("p", {      onClick: _cache[1] || (_cache[1] = ($event, ...args) => (_ctx.handleClick($event, ...args)))    }, "静态代码")  ]))}编辑器会为你动态创建一个内联函数,内联函数里面再去引用当前组件上最新的handler。之后编辑器会将内联函数缓存。每次重新渲染时如果事件处理器没有变,就会使用缓存中的事件处理而不会重新获取事件处理器。这个节点就可以被看作是一个静态的节点。这种优化更大的作用在于当其作用于组件时,之前每次重新渲染都会导致组件的重新渲染,在通过handler缓存之后,不会导致组件的重新渲染了。
SSR 服务端渲染

当开启SSR了之后,如果我们模版中有一些静态标签,这些静态标签会被直接转化成文本。



图文整理 Vue3.0性能提升 Vue3主要是通过哪几个点体现-3.jpg



其中的动态绑定依然是一个单独的字符串内嵌进去。这个性能肯定比React 转成VDOM在专为HTML快很多。
StaticNode 静态节点

刚才提到在SSR中静态的节点会被转化为纯字符串。如果在客户端,当静态节点嵌套足够多的时候,VUE编译器也会将VDOM转化为纯字符串的HTML。即 StaticNode。



图文整理 Vue3.0性能提升 Vue3主要是通过哪几个点体现-4.jpg



通过这些操作,我们可以看下,跟vue2比可以快一倍以上,内存占用可以小一倍以上。



图文整理 Vue3.0性能提升 Vue3主要是通过哪几个点体现-5.jpg



Tree Shaking

因为ES6模块是静态引用的,所以我们可以在编译时正确的判断到底加载了哪些代码。对代码全局做一个分析,找到那些没用被用到的模块、函数、变量,并把这些去掉。当使用一个 bundle (webpack etc.)的时候,默认会加上 TreeShaking。Vue 3.0 中没有被用到的模块可以不被打包到编译后的文件中,被 TreeShake 掉。当只有一个HelloWorld的时候 Vue3打包后 13.5kb。所有的组件全部加载进来时是 22.5kb
Composition API

随着Vue组件的增大,组件内代码变得越来越难以理解和维护。其中的一些可以复用的代码很难被抽离出来。同时 Vue2.0还缺少 TS支持。在Vue2中,逻辑概念(功能)被管理在组件中,但是功能和组件并不是一对一关系。一个功能可以被多个组件使用同时一个组件可以有多个功能。在Vue中,一个功能可能需要依赖多个Options(components、props、data、computed、methods及生命周期方法)。
在 Composition API中提供可 setup 方法。以一个有搜索功能和 排序功能组件为例:
<script>export default {    setup() {        }}function useSearch() {    return {     ...useSearch(),     ...useSorting()    }}function useSorting() {    }</script>Vue2 中的代码复用

Mixin

在Vue2中有几种方式可以复用代码,其中之一就是 Mixins。

  • Mixins可以实现组织功能
  • 容易发生冲突
  • 很难说明依赖关系
  • 代码不容易复用
Mixin 工厂


  • 可以方便复用
  • 明确的依赖关系
  • 弱命名空间
  • 隐性的属性添加
Scoped Slots


  • 解决了 Mixin 的问题
  • 增加了层级关系导致更难以理解
  • 很多配置信息
  • 灵活性更少
  • 性能较差
核心 API


  • reactive
  • ref
  • computed
  • readonly
  • watchEffect
  • watch
  • Lifecycle Hooks
Fragments

Vue3中不在要求模版的跟节点必须是只能有一个节点。跟节点和和render函数返回的可以是纯文字、数组、单个节点,如果是数组,会自动转化为 Fragments。
Teleport

对标 React Portal。可以做一些关于响应式的设计,如果屏幕宽度比较宽的时候,加入某些元素,屏幕变窄后移除。
Suspense

等待嵌套的异步依赖。再把一个嵌套的组件树渲染到页面上之前,先在内存中进行渲染,并记录所有的存在异步依赖的组件。只有所有的异步依赖全部被resolve之后,才会把整个书渲染到dom中。当你的组件中有一个 async的 setup函数,这个组件可以被看作是一个Async Component,只有当这个组件被Resolve之后,再把整个树渲染出来

  • async setup()
  • Async Component
Typescript

Vue3源码使用 TS重写,但不意味着vue3的项目也要使用TS。但Vue3会对 TS有更好的支持

  • 支持 TSX
  • 支持 Class component
  • 代码会变大一些


一、编译阶段

回顾Vue2,我们知道每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把用到的数据property记录为依赖,当依赖发生改变,触发setter,则会通知watcher,从而使关联的组件重新渲染



图文整理 Vue3.0性能提升 Vue3主要是通过哪几个点体现-6.jpg



试想一下,一个组件结构如下图
<template>    <div id="content">        <p class="text">静态文本</p>        <p class="text">静态文本</p>        <p class="text">{{ message }}</p>        <p class="text">静态文本</p>        ...        <p class="text">静态文本</p>    </div></template>

可以看到,组件内部只有一个动态节点,剩余一堆都是静态节点,所以这里很多 diff 和遍历其实都是不需要的,造成性能浪费
因此,Vue3在编译阶段,做了进一步优化。主要有如下:

  • diff算法优化
  • 静态提升
  • 事件监听缓存
  • SSR优化
diff算法优化

vue3在diff算法中相比vue2增加了静态标记
关于这个静态标记,其作用是为了会发生变化的地方添加一个flag标记,下次发生变化的时候直接找该地方进行比较
下图这里,已经标记静态节点的p标签在diff过程中则不会比较,把性能进一步提高



图文整理 Vue3.0性能提升 Vue3主要是通过哪几个点体现-7.jpg



关于静态类型枚举如下
export const enum PatchFlags {  TEXT = 1,// 动态的文本节点  CLASS = 1 << 1,  // 2 动态的 class  STYLE = 1 << 2,  // 4 动态的 style  PROPS = 1 << 3,  // 8 动态属性,不包括类名和样式  FULL_PROPS = 1 << 4,  // 16 动态 key,当 key 变化时需要完整的 diff 算法做比较  HYDRATE_EVENTS = 1 << 5,  // 32 表示带有事件监听器的节点  STABLE_FRAGMENT = 1 << 6,   // 64 一个不会改变子节点顺序的 Fragment  KEYED_FRAGMENT = 1 << 7, // 128 带有 key 属性的 Fragment  UNKEYED_FRAGMENT = 1 << 8, // 256 子节点没有 key 的 Fragment  NEED_PATCH = 1 << 9,   // 512  DYNAMIC_SLOTS = 1 << 10,  // 动态 solt  HOISTED = -1,  // 特殊标志是负整数表示永远不会用作 diff  BAIL = -2 // 一个特殊的标志,指代差异算法}

静态提升

Vue3中对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用
这样就免去了重复的创建节点,大型应用会受益于这个改动,免去了重复的创建操作,优化了运行时候的内存占用
<span>你好</span><div>{{ message }}</div>

没有做静态提升之前
export function render(_ctx, _cache, $props, $setup, $data, $options) {  return (_openBlock(), _createBlock(_Fragment, null, [    _createVNode("span", null, "你好"),    _createVNode("div", null, _toDisplayString(_ctx.message), 1 /* TEXT */)  ], 64 /* STABLE_FRAGMENT */))}

做了静态提升之后
const _hoisted_1 = /*#__PURE__*/_createVNode("span", null, "你好", -1 /* HOISTED */)export function render(_ctx, _cache, $props, $setup, $data, $options) {  return (_openBlock(), _createBlock(_Fragment, null, [    _hoisted_1,    _createVNode("div", null, _toDisplayString(_ctx.message), 1 /* TEXT */)  ], 64 /* STABLE_FRAGMENT */))}// Check the console for the AST

静态内容_hoisted_1被放置在render 函数外,每次渲染的时候只要取 _hoisted_1 即可
同时 _hoisted_1 被打上了 PatchFlag ,静态标记值为 -1 ,特殊标志是负整数表示永远不会用于 Diff
事件监听缓存

默认情况下绑定事件行为会被视为动态绑定,所以每次都会去追踪它的变化
<div>  <button @click = 'onClick'>点我</button></div>

没开启事件监听器缓存
export const render = /*#__PURE__*/_withId(function render(_ctx, _cache, $props, $setup, $data, $options) {  return (_openBlock(), _createBlock("div", null, [    _createVNode("button", { onClick: _ctx.onClick }, "点我", 8 /* PROPS */, ["onClick"])                                             // PROPS=1<<3,// 8 //动态属性,但不包含类名和样式  ]))})

开启事件侦听器缓存后
export function render(_ctx, _cache, $props, $setup, $data, $options) {  return (_openBlock(), _createBlock("div", null, [    _createVNode("button", {      onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.onClick(...args)))    }, "点我")  ]))}

上述发现开启了缓存后,没有了静态标记。也就是说下次diff算法的时候直接使用
SSR优化

当静态内容大到一定量级时候,会用createStaticVNode方法在客户端去生成一个static node,这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染
div>        <div>                <span>你好</span>        </div>        ...  // 很多个静态属性        <div>                <span>{{ message }}</span>        </div></div>

编译后
import { mergeProps as _mergeProps } from "vue"import { ssrRenderAttrs as _ssrRenderAttrs, ssrInterpolate as _ssrInterpolate } from "@vue/server-renderer"export function ssrRender(_ctx, _push, _parent, _attrs, $props, $setup, $data, $options) {  const _cssVars = { style: { color: _ctx.color }}  _push(`<div${    _ssrRenderAttrs(_mergeProps(_attrs, _cssVars))  }><div><span>你好</span>...<div><span>你好</span><div><span>${    _ssrInterpolate(_ctx.message)  }</span></div></div>`)}

二、源码体积

相比Vue2,Vue3整体体积变小了,除了移出一些不常用的API,再重要的是Tree shanking
任何一个函数,如ref、reavtived、computed等,仅仅在用到的时候才打包,没用到的模块都被摇掉,打包的整体体积变小
import { computed, defineComponent, ref } from 'vue';export default defineComponent({    setup(props, context) {        const age = ref(18)        let state = reactive({            name: 'test'        })        const readOnlyAge = computed(() => age.value++) // 19        return {            age,            state,            readOnlyAge        }    }});

三、响应式系统

vue2中采用 defineProperty来劫持整个对象,然后进行深度遍历所有属性,给每个属性添加getter和setter,实现响应式
vue3采用proxy重写了响应式系统,因为proxy可以对整个对象进行监听,所以不需要深度遍历

  • 可以监听动态属性的添加
  • 可以监听到数组的索引和数组length属性
  • 可以监听删除属性
关于这两个 API 具体的不同,我们下篇文章会进行一个更加详细的介绍
给大家分享我收集整理的各种学习资料,前端小白交流、学习交流,也可以直接问我,我会组织大家一起做项目练习,帮助大家匹配一位学习伙伴互相监督学习-下面是学习资料参考。
前端学习交流、自学、学习资料等推荐 - 知乎
广告圈
11287 查看 5 0 反对

说说我的看法高级模式

您需要登录后才可以回帖 登录|立即注册

  • 大海卐

    2022-6-16 11:10:09 使用道具

    来自: 中国来自: 中国来自: 中国来自: 中国
    总有一天前端会回归到原生方式
  • 刺心!

    2022-6-16 11:10:48 使用道具

    来自: 中国来自: 中国来自: 中国来自: 中国
    一堆坑,退回2了
  • 返奕

    2022-6-16 11:11:40 使用道具

    来自: 中国来自: 中国来自: 中国来自: 中国
    3挺好用
  • 看雨又看风

    2022-6-16 11:11:45 使用道具

    来自: 北京来自: 北京来自: 北京来自: 北京
    转发了
  • Royal乄遥ゝ

    2022-6-16 11:12:23 使用道具

    来自: 中国来自: 中国来自: 中国来自: 中国
    转发了

相关阅读