zoukankan      html  css  js  c++  java
  • Vue3.0是如何变快的

    Vue3.0是如何变快的?
    - diff算法优化: https://vue-next-template-explorer.netlify.app/
        + Vue2中的虚拟dom是进行全量的对比,同级对比,不管修改和未修改的数据都会对比
        + Vue3新增了静态标记(PatchFlag),
          在与上次虚拟节点进行对比时候,只对比带有patch flag的节点
          并且可以通过flag的信息得知当前节点要对比的具体内容
    - hoistStatic 静态提升
        + Vue2中无论元素是否参与更新, 每次都会重新创建, 然后再渲染
        + Vue3中对于不参与更新的元素, 会做静态提升, 只会被创建一次, 在渲染时直接复用即可
    - cacheHandlers 事件侦听器缓存
        + 默认情况下onClick会被视为动态绑定, 所以每次都会去追踪它的变化
          但是因为是同一个函数,所以没有追踪变化, 直接缓存起来复用即可
    - ssr渲染
        + 当有大量静态的内容时候,这些内容会被当做纯字符串推进一个buffer里面,
          即使存在动态的绑定,会通过模板插值嵌入进去。这样会比通过虚拟dmo来渲染的快上很多很多。
        + 当静态内容大到一定量级时候,会用_createStaticVNode方法在客户端去生成一个static node,
          这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染。
     
    1. 静态标记
    在与上次虚拟节点进行对比时候,只对比带有patch flag的节点(已修改了节点,不会对未改变的父级和兄弟节点对比)
    图解

    代码演示

    <div>
      <p>Xmo</p>
      <p>Xmo</p>
      <p>Xmo</p>
      <p>{{msg}}</p>
    </div>
    
    
    编译
    import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"
    
    export function render(_ctx, _cache, $props, $setup, $data, $options) {
      return (_openBlock(), _createBlock("div", null, [
        _createVNode("p", null, "Xmo"),
        _createVNode("p", null, "Xmo"),
        _createVNode("p", null, "Xmo"),
        _createVNode("p", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
      ]))
    }
    
    // Check the console for the AST

    可以看到,源码中,对 msg 设计了静态标记,这里是1,后面跟注释 TEXT ,代表这个标签的 TEXT 数据是会动态变化的。(动态变化的东西反而叫静态标记,可还行)

     
    2. 静态提升
    <div>
        <p>知播渔</p>
        <p>知播渔</p>
        <p>知播渔</p>
        <p>{{msg}}}</p>
    </div>
    静态提升之前:
    export function render(_ctx, _cache, $props, $setup, $data, $options) {
      return (_openBlock(), _createBlock("div", null, [
        _createVNode("p", null, "知播渔"),
        _createVNode("p", null, "知播渔"),
        _createVNode("p", null, "知播渔"),
        _createVNode("p", null, _toDisplayString(_ctx.msg) + "}", 1 /* TEXT */)
      ]))
    }
    静态提升之后:
    const _hoisted_1 = /*#__PURE__*/_createVNode("p", null, "知播渔", -1 /* HOISTED */)
    const _hoisted_2 = /*#__PURE__*/_createVNode("p", null, "知播渔", -1 /* HOISTED */)
    const _hoisted_3 = /*#__PURE__*/_createVNode("p", null, "知播渔", -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 */)
      ]))
    }

    直接复用未做更改的vnode节点,只创建一次,不需要渲染所有的vnode

     
    3.cacheHandlers 事件侦听器缓存
    事件监听缓存
    <div>
      <button @click="onClick">按钮</button>
    </div>
    
    开启事件监听缓存之前:
    export function render(_ctx, _cache, $props, $setup, $data, $options) {
      return (_openBlock(), _createBlock("div", null, [
        _createVNode("button", { onClick: _ctx.onClick }, "按钮", 8 /* PROPS */, ["onClick"])
      ]))
    }
    开启事件监听缓存之后:
    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)))
        }, "按钮")
      ]))
    }
    注意点: 转换之后的代码, 大家可能还看不懂, 但是不要紧
            我们只需要观察有没有静态标记即可
            因为我们知道在Vue3的diff算法中, 只有有静态标记的才会进行比较, 才会进行追踪

    还没有开启事件监听缓存之时,将事件标记,将事件标记成动态属性,熟悉的静态标记 8 /* PROPS */ 出现了,

    _createVnode 的第二个属性,从

    { onClick: _ctx.onClick }

    变为了

    { onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.onClick(...args))) }

    它的意思很明显,onClick 方法被存入 cache。在使用的时候,如果能在缓存中找到这个方法,那么它将直接被使用。如果找不到,那么将这个方法注入缓存。总之,就是把方法给缓存了。

    开启事件监听进行缓存后,没有出现静态标记了,不用去追踪了,提升了性能

    附录: PatchFlags
    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 或部分子字节有 key
      UNKEYED_FRAGMENT = 1 << 8, // 256 // 子节点没有 key 的 fragment
      NEED_PATCH = 1 << 9, // 512 // 一个节点只会进行非 props 比较
      DYNAMIC_SLOTS = 1 << 10, // 1024 // 动态 slot
      HOISTED = -1, // 静态节点
      // 指示在 diff 过程应该要退出优化模式
      BAIL = -2
    }
     
     
     
     
     
  • 相关阅读:
    《剑指Offer》题目:跳台阶
    《剑指Offer》题目:变态跳台阶
    《剑指Offer》题目:二叉树的镜像
    《剑指Offer》题目:树的子结构
    《剑指Offer》题目:合并两个排序的链表
    《剑指Offer》题目:链表中倒数第k个结点
    《剑指Offer》题目:调整数组顺序使奇数位于偶数前面
    Nginx配置http强制跳转到https
    并查集详解(转)
    AKOJ -- 1529 -- 寻找最大数
  • 原文地址:https://www.cnblogs.com/fsg6/p/14480845.html
Copyright © 2011-2022 走看看