了解Vue3

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-05-31

Vue3.0的亮点

  • Performance: 性能比Vue2.x快1.2~2倍。
  • Tree Shaking Support: 按需编译,体积比Vue2.x更小。
  • Composition API: 组合API。
  • Better Typescript Support: 更好的 TS 支持。
  • Custom Renderer API: 暴露了自定义渲染API。
  • Fragment, Teleport(Portal), Suspense: 更先进的组件。

Vue3.0 是如何变快的

diff 算法的优化

Vue2.0会对每一个节点去做对比,是否有改动有则更新数据,没有则什么都不做。Vue3.0会给动态节点(使用了Vue指令或者包含了Vue插值表达式的节点)绑定 PatchFlag,在更新的时候判断是否有标记,没有标记不去对比直接拿过来用,有标记再去做对比,数据修改过则更新数据。

<div>
  <p>1</p>
  <p>2</p>
  <p>3</p>
  <p>{{msg}}}</p>
</div>

只有第4个P元素参与数据更新:

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("p", null, "1"),
    _createVNode("p", null, "2"),
    _createVNode("p", null, "3"),
    _createVNode("p", null, _toDisplayString(_ctx.msg) + "}", 1 /* TEXT */) // PatchFlag
  ]))
}

PatchFlag 附录:

  • TEXT = 1, 动态文本节点
  • CLASS = 2, 动态class
  • STYLE = 4, 动态 style
  • PROPS = 8, 动态属性,但不包含类名和样式
  • FULL_PROPS = 16, 具有动态的Key属性,当Key改变时,需要进行完整的 diff 比较
  • HYDRATE_EVENTS = 32, 带有监听事件的节点
  • STABLE_FRAGMENT = 64, 一个不会改变子节点顺序的 fragment
  • KEYED_FRAGMENT = 128, 带有key属性的 fragment 或部分子字节有 key
  • UNKEYED_FRAGMENT = 256, 子节点没有 key 的 fragment
  • NEED_PATCH = 512, 一个节点只会进行非 props 比较

这个设计有一点像权限设计,当有多个的时候,可以使用位运算来快速判断,当又是文本节点有具有动态class的时候,就会 1 | 2 = 3。查看是否有某个Tag的时候,就可以用 17 & 1, 转换出来如下:

// <p :class="{b}">{{msg}}}</p>
_createVNode("p", { class: {b: _ctx.b} }, _toDisplayString(_ctx.msg) + "}", 3 /* TEXT, CLASS */)

静态提升 hoistStatic

在 Vue2 中无论元素是否参与更新,每次都会重新创建(如上一样每次都调用_createVNode重新创建),然后再渲染。而 Vue3.0 对于不参与更新的元素,会做静态提升,再渲染直接复用。

// 当启用静态提升之后
const _hoisted_1 = /*#__PURE__*/_createVNode("p", null, "1", -1 /* HOISTED */)
const _hoisted_2 = /*#__PURE__*/_createVNode("p", null, "2", -1 /* HOISTED */)
const _hoisted_3 = /*#__PURE__*/_createVNode("p", null, "3", -1 /* HOISTED */) // 静态提升

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", null, [
    _hoisted_1, // 直接复用
    _hoisted_2, 
    _hoisted_3,
    _createVNode("p", null, _toDisplayString(_ctx.msg) + "}", 1 /* TEXT */)
  ]))
}

事件侦听缓存 cacheHandlers

默认情况下 @click 会为其加上一个 PatchFlag 的,当做动态属性。

但是实际上,@click 接受的动态属性是一个函数,这个函数是不会变化的,所以没必要追踪变化,直接缓存起来复用即可。

<div>
  <button @click="handleClick">按钮</button>
</div>
// 默认会被处理为:
export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("button", { onClick: _ctx.handleClick }, "按钮", 8 /* PROPS */, ["onClick"])
  ]))
}

// 开启事件侦听缓存之后  不再有 PatchFlag
export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("button", {
      onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.handleClick && _ctx.handleClick(...args)))
    }, "按钮")
  ]))
}