美文网首页
vue3.0做了哪些优化

vue3.0做了哪些优化

作者: 青城墨阕 | 来源:发表于2022-06-14 14:51 被阅读0次

https://v3.cn.vuejs.org/guide/migration/introduction.html#%E6%A6%82%E8%A7%88

首先,为什么要优化?

     Vue3 组合式 API(Composition API) 主要用于在大型组件中提高代码逻辑的可复用性。
     vue2.x option Api的传统组件随着业务复杂度越来越高,代码量会不断的加大,整个代码逻辑都不易阅读和理解。
     Vue3 使用组合式 API 的地方为 setup。
     在 setup 中,我们可以按逻辑关注点对部分代码进行分组,然后提取逻辑片段并与其他组件共享代码。因此,组合式 API(Composition API) 允许我们编写更有条理的代码。

没有对比就没有伤害~
另,compositon api提供了以下几个函数:
  • setup
  • ref
  • reactive
  • watchEffect
  • watch
  • computed
  • toRefs
  • 生命周期的hooks



优化

一、 源码的优化

1. 更好的代码管理方式:monorepo

vue2.x vue3.x
Vue.js 2.x 的源码托管在 src 目录,然后分别依据功能拆分出了:
1. compiler(模板编译的相关代码)
2. core(与平台无关的通用运行时代码)
3. platforms(平台专有代码)
4. server(服务端渲染的相关代码)
5. sfc(.vue 单文件解析相关代码)
6. shared(共享工具代码) 等目录:
Vue.js 3.0 ,整个源码是通过 monorepo 的方式维护
根据功能将不同的模块拆分到packages 目录下面不同的子目录中
vue2.x代码结构 vue3.x代码结构
优点:
  • 相对于 Vue.js 2.x 的源码组织方式,monorepo 把这些模块拆分到不同的 package 中,每个 package有各自的API、类型定义和测试。这样使得模块拆分更细化,职责划分更明确,模块之间的依赖关系也更加明确,开发人员也更容易阅读、理解和更改所有模块源码,提高代码的可维护性。
  • 另外一些 package(比如 reactivity 响应式库)是可以独立于 Vue.js 使用的,这样用户如果只想使用 Vue.js 3.0 的响应式能力,可以单独依赖这个响应式库而不用去依赖整个 Vue.js,减小了引用包的体积大小,而 Vue.js 2 .x 是做不到这一点的。

2. 有类型的 JavaScript:TypeScript

vue2.x vue3.x
Flow

Flow 是 Facebook 出品的 JavaScript 静态类型检查工具

优点:它可以以非常小的成本对已有的 JavaScript 代码迁入,非常灵活

缺点:但是 Flow 对于一些复杂场景类型的检查,支持得并不好
TypeScript

TypeScript提供了更好的类型检查,能支持复杂的类型推导
由于源码就使用 TypeScript 编写,也省去了单独维护 d.ts 文件的麻烦



二、 性能优化

1. 源码体积优化

  • 移除一些冷门的 feature(比如 filter、inline-template 等);
  • 引入 tree-shaking 的技术,减少打包体积。

tree-shaking原理
tree-shaking 依赖 ES2015 模块语法的静态结构(即 import 和 export),通过编译阶段的静态分析,找到没有引入的模块并打上标记。

举个栗子,一个 math 模块定义了 2 个方法 square(x) 和 cube(x) :

export function square(x) {
  return x * x
}
export function cube(x) {
  return x * x * x
}

我们在另一个模块外面只引入了 cube 方法:

import { cube } from './math.js'
// do something with cube

最终 math 模块会被 webpack 打包生成如下代码:

/* 1 */
/***/ 
(function(module, __webpack_exports__, __webpack_require__) {
  'use strict';
  /* unused harmony export square */
  /* harmony export (immutable) */ __webpack_exports__['a'] = cube;
  function square(x) {
    return x * x;
  }
  function cube(x) {
    return x * x * x;
  }
});

由上可得,未被引入的 square 模块被标记了, 然后压缩阶段会利用例如 uglify-js、terser 等压缩工具真正地删除这些没有用到的代码。
也就是说,利用 tree-shaking 技术,任何一个函数,仅仅在用到的时候才打包,没用到的模块都被摇掉,打包的整体体积变小。

2. 响应式系统——数据劫持优化

通过数据劫持来更新视图的大体流程
vue2.x中采用 Object.defineProperty 来劫持整个对象,然后进行深度遍历所有属性,给每个属性添加getter和setter,实现响应式:
  • 缺点:它必须预先知道要拦截的 key 是什么,所以它并不能检测对象属性的添加和删除,所以提供了vue.setvue.delete方式进行处理,但是对于开发者来说显然是不那么友好的。

vue3.x采用proxy重写了响应式系统,因为proxy可以对整个对象进行监听,所以不需要深度遍历:

  • 可以监听动态属性的添加
  • 可以监听到数组的索引和数组length属性
  • 可以监听删除属性

3. 编译阶段的优化

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

虽然 Vue 能保证触发更新的组件最小化,但在单个组件内部依然需要遍历该组件的整个 vnode 树,举个例子,比如我们要更新这个组件:

<div>
  <p>老八食堂</p>
  <p>{{ message }}</p>
</div>

则整个 diff 过程如图所示(在 Vue 2.x 的全量对比模式下):

可以看到,这段代码中只有一个动态节点,所以这里有很多 diff 和遍历其实都是不需要的,这就会导致 vnode 的性能跟模版大小正相关,跟动态节点的数量无关,当一些组件的整个模版内只有少量动态节点时,这些遍历都是性能的浪费。
而对于上述例子,理想状态只需要 diff 这个绑定 message 动态节点的 p 标签即可。

在 Vue 3.0 中,对 diff 算法进行了优化,在创建虚拟 DOM 时,根据 DOM 内容是否会发生变化,而给予相对应类型的静态标记(PatchFlag),如下图所示:

观察上图,不难发现试图的更新只对带有 flag 标记的标签进行了对比(diff),所以只进行了 1 次比较,而相同情况下,Vue 2.x 则进行了 3 次比较。这便是 Vue 3.0 比 Vue2.x 性能好的第一个原因。

  • 关于静态类型枚举如下:
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 // 一个特殊的标志,指代差异算法
}
3.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。

3.3 事件监听缓存(cacheHandler)

默认情况下 @click 事件被认为是动态变量,所以每次更新视图的时候都会追踪它的变化。但是正常情况下,我们的 @click 事件在视图渲染前和渲染后,都是同一个事件,基本上不需要去追踪它的变化,所以 Vue 3.0 对此作出了相应的优化叫事件监听缓存,举个栗子:

<div>
  <p @click="handleClick">屋里一giao</p>
</div>

编译后如下图所示(还未开启 cacheHandler):


在未开启事件监听缓存的情况下,我们看到这串代码编译后被静态标记为 8,之前讲解过被静态标记的标签就会被拉去做比较,而静态标记 8 对应的是“动态属性,不包括类名和样式”。@click 被认为是动态属性,所以我们需要开启 Options 下的 cacheHandler 属性,如下图所示:

开启 cacheHandler之后,编译后的代码已经没有静态标记(PatchFlag),也就表明图中 P 标签不再被追踪比较变化,也就是说下次diff算法的时候直接使用,进而提升了 Vue 的性能。

3.4 SSR优化

当静态内容大到一定量级时候,会用createStaticVNode方法在客户端去生成一个static node,这些静态node,会被直接innerHtml,就不需要创建虚拟DOM对象,然后根据对象渲染

<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>`)
}





参考文献:
https://juejin.cn/post/6903171037211557895
https://vue3js.cn/interview/vue3/performance.html
https://juejin.cn/post/6850418112878575629#heading-5

相关文章

  • vue3.0做了哪些优化

    https://v3.cn.vuejs.org/guide/migration/introduction.html...

  • Vue3.0的设计目标是什么?做了哪些优化?

    一、设计目标 不以解决实际业务痛点的更新都是耍流氓,下面我们来列举一下Vue3之前我们或许会面临的问题 随着功能的...

  • Synchornized

    Synchornized做了哪些优化? synchornized底层都是使用monitorenter和monito...

  • Android性能优化专题(持续更新)

    面试官:今日头条启动很快,你觉得可能是做了哪些优化? Android 性能优化之内存检测、卡顿优化、耗电优化、AP...

  • vue3.0学习笔记

    一、vue3.0比vue2.0快1.2~2倍 1、优化了diff算法:vue2.0是进行全量的比对vue3.0只对...

  • 稍有难度

    1、jvm对频繁调用的方法做了哪些优化? 2、常见的攻击手段有哪些?如何防范? 3、restful api有哪些设...

  • 稍微有点难度的10道java面试题,你会几道?

    1、jvm对频繁调用的方法做了哪些优化? 2、常见的攻击手段有哪些?如何防范? 3、restful api有哪些设...

  • Vue3.0双向绑定核心:Proxy 示例

    Vue3.0用Proxy 替代了Object.defineProperty,优化了:无法监听 属性的添加和删除、数...

  • java面试题 --- 并发②

    1. JDK1.6 开始对 synchronized 做了哪些优化?使用了锁升级、锁粗化、锁消除等方式来优化性能。...

  • 2020,12,29复盘

    1)今天做了哪些事情? 看书、看易经有关视频、听书、复习精简笔记 2)哪些事情可以优化方式,做得更好? 减少切屏,...

网友评论

      本文标题:vue3.0做了哪些优化

      本文链接:https://www.haomeiwen.com/subject/ldszmrtx.html