Vue3 效率提升

# Vue3 效率提升

Vue3 的效率提升主要变现在哪些方面?


# 静态提升

下面的静态节点会被提升

  • 元素节点
  • 没有绑定动态内容

以前 Vue2 的时候我们的template会存在一个预编译过程 就是将 template 里面的内容通过render函数 构建成虚拟 dom

// Vue2
render(){
	createVNode('h1',null,'Hello My friend!')
}

现在 Vue3呢 认为你这个啥也不是 就是个普普通通的 元素节点 默认他不会改变 就会把这个节点提取出来。

const hoisted = createVNode('h1',null,'Hello My friend!')

function render(){
	// ... 直接使用 hoisted
}

这样能保证我们的节点只会创建一个,而不是随着部分内容改变重新创建。

如下:

我们创建一个静态组件(内部全是静态的dom)观察 vite 编译出的资源

<template>
  <div>
    <ul>
      <li>hello</li>
      <li>beta</li>
      <li>gama</li>
      <li>stupid</li>
    </ul>
  </div>
</template>
<script>
export default {
    
}
</script>

![截屏2022-03-23 下午10.00.31](/Users/lucasy/Library/Application Support/typora-user-images/截屏2022-03-23 下午10.00.31.png)

静态属性会被提升

比如说一个节点虽然他的内容是动态的,但是他的属性是静态的,这个属性会被提升。

将代码做部分修改:

<template>
  <div>
    <ul>
      <li class="sofeia">{{ user.name }}</li>
      <li>beta</li>
    </ul>
  </div>
</template>
<script>
export default {
  data() {
    return {
      user: { name: "zhang san" },
    };
  },
};
</script>

观察编译后的资源

![截屏2022-03-23 下午10.06.02](/Users/lucasy/workspace/new_level/note/Vue3/截屏2022-03-23 下午10.06.02.png)


# 预字符串化

什么是预字符串化呢

就是 当编译器遇到大量连续的静态内容,会直接将其便以为一个普通字符串节点

// 模板
<template>
  <div>
    <ul>
      <li class="sofeia">{{ user.name }}</li>
      <li>beta</li>
    </ul>
    <ul>
      <li>hello</li>
      <li>hello</li>
      <li>hello</li>
    </ul>
    <ul>
      <li>kalasica</li>
      <li>kalasica</li>
      <li>kalasica</li>
      <li>kalasica</li>
      <li>kalasica</li>
      <li>kalasica</li>
      <li>kalasica</li>
      <li>kalasica</li>
      <li>kalasica</li>
      <li>kalasica</li>
    </ul>
    <ul>
      <li>music</li>
      <li>music</li>
      <li>music</li>
      <li>music</li>
      <li>music</li>
    </ul>
    <div>{{ user.name }}</div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      user: { name: "zhang san" },
    };
  },
};
</script>

观察编译后的内容:

![截屏2022-03-23 下午10.11.12](/Users/lucasy/workspace/new_level/note/Vue3/截屏2022-03-23 下午10.11.12.png)

我们看到编译后的代码将连续的静态节点变成了一个字符串

const _hoisted_3 = /*#__PURE__*/
_createStaticVNode("<ul><li>hello</li><li>hello</li><li>hello</li></ul><ul><li>kalasica</li><li>kalasica</li><li>kalasica</li><li>kalasica</li><li>kalasica</li><li>kalasica</li><li>kalasica</li><li>kalasica</li><li>kalasica</li><li>kalasica</li></ul><ul><li>music</li><li>music</li><li>music</li><li>music</li><li>music</li></ul>", 3)

限制情况:只有连续的节点且大于二十个

Vue2 的时候 遇到这样的节点树就是会 render 函数生成虚拟dom···直到构建完整棵树。

这样只需要将关注点放在动态节点,而无需关心静态节点问题。


# 缓存事件处理函数

Vue3 同理也认为你绑定的事件是不会改变的

Vue3 中有很多个参数 大多数的参数都是 catch对象 用来缓存我们的数据

<template>
  <button @click="handleClick">click</button>
</template>
<script>
export default {
  methods: {
    handleClick() {},
  },
};
</script>

我们来看下编译后的内容:

_createElementBlock("button", 
{
    onClick: _cache[0] || 
    (_cache[0] = (...args) =>($options.handleClick && $options.handleClick(...args)))
 }, 
 "click")

可以看到在会访问缓存对象,如果缓存中没有 则会保存到缓存中。

# Block Tree

Vue2 在比对新旧树的时候,并不知道哪些节点是静态的,哪些是动态的,因此只能一层一层比较,这就浪费了大部分事件在比对静态节点上。

<template>
  <form>
    <div>
      <label for="">账号:</label>
      <input type="text" v-model="user.name" />
    </div>
    <div>
      <label for="">密码:</label>
      <input type="text" v-model="user.pawd" />
    </div>
  </form>
</template>
<script>
export default {
  data() {
    return {
      user: {
        name: "xxxx",
        pawd: "xxx",
      },
    };
  },
};
</script>

在 Vue2 的时候 比对两棵树 会使用 petch 算法 但是 我们来看上面的模板,像 form、div 、label 这些静态节点又不会改变为什么还要比对呢?

但是 Vue3 中就不是这样子啦

Vue3 会在创建VDom 的时候识别动态节点然后将动态节点的信息保存到 根节点中,当我们页面发生改变后,直接找到根节点中存储的动态节点然后循环遍历直接比对动态节点,越过静态节点。

动静比:静态节点越多动静比越大

这样提升的效率很高。

# Patch Flag

Patch Flag 的作用就是标记我们修改的类型

还是上面的模板

我们看他的编译内容:

function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  return (
    _openBlock(),
    _createElementBlock("form", null, [
      _createElementVNode("div", null, [
        _hoisted_1,
        _withDirectives(
          _createElementVNode(
            "input",
            {
              type: "text",
              "onUpdate:modelValue":
                _cache[0] ||
                (_cache[0] = ($event) => ($data.user.name = $event)),
            },
            null,
            512 /* NEED_PATCH */
          ),
          [[_vModelText, $data.user.name]]
        ),
      ]),
      _createElementVNode("div", null, [
        _hoisted_2,
        _withDirectives(
          _createElementVNode(
            "input",
            {
              type: "text",
              "onUpdate:modelValue":
                _cache[1] ||
                (_cache[1] = ($event) => ($data.user.pawd = $event)),
            },
            null,
            512 /* NEED_PATCH */
          ),
          [[_vModelText, $data.user.pawd]]
        ),
      ]),
    ])
  );
}

我们看这个地方:

_createElementVNode(
            "input",
            {
              type: "text",
              "onUpdate:modelValue":
                _cache[1] ||
                (_cache[1] = ($event) => ($data.user.pawd = $event)),
            },
            null,
            512 /* NEED_PATCH */
          ),

在创建Vdom的时候发现后面跟了一个数字 512 这个数字就是一个标记值,表示我们节点改变的类型

再比如:

我们修改了模板

<template>
  <form>
    <div>
      <label for="">账号:</label>
      <!-- <input type="text" v-model="user.name" /> -->
      <span>{{ user.name }}</span>
    </div>
    <div>
      <label for="">密码:</label>
      <!-- <input type="text" v-model="user.pawd" /> -->
      <span>{{ user.pawd }}</span>
    </div>
  </form>
</template>
<script>
export default {
  data() {
    return {
      user: {
        name: "xxxx",
        pawd: "xxx",
      },
    };
  },
};
</script>

发现后面的数字变成了 1

function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
    return (_openBlock(),
    _createElementBlock("form", null, [_createElementVNode("div", null, [_hoisted_1, _createCommentVNode(" <input type=\"text\" v-model=\"user.name\" /> "), _createElementVNode("span", null, _toDisplayString($data.user.name), 1 /* TEXT */
    )]), _createElementVNode("div", null, [_hoisted_2, _createCommentVNode(" <input type=\"text\" v-model=\"user.pawd\" /> "), _createElementVNode("span", null, _toDisplayString($data.user.pawd), 1 /* TEXT */
    )])]))
}

后面的注释解释 是 TEXT 意思是 文本类型。