Rumah  >  Artikel  >  hujung hadapan web  >  Apakah pengoptimuman yang telah dilakukan dalam kompilasi vue3?

Apakah pengoptimuman yang telah dilakukan dalam kompilasi vue3?

青灯夜游
青灯夜游asal
2022-12-19 18:05:582971semak imbas

Pengoptimuman kompilasi Vue3 termasuk: 1. PatchFlag diperkenalkan untuk menandakan kandungan dinamik semasa proses penyusunan, label yang berbeza akan ditanda mengikut jenis atribut yang berbeza, sekali gus merealisasikan algoritma perbezaan pantas. 2. Pokok Blok. 3. Promosi statik adalah untuk mempromosikan nod atau atribut statik. 4. Pre-parsing stringification, apabila terdapat lebih daripada 10 nod statik berturut-turut, nod statik akan bersiri menjadi rentetan. 5. Cache fungsi; selepas menghidupkan pilihan CacheHandlers, fungsi akan dicache dan boleh digunakan terus kemudian.

Apakah pengoptimuman yang telah dilakukan dalam kompilasi vue3?

Persekitaran pengendalian tutorial ini: sistem windows7, versi vue3, komputer DELL G3.

Artikel ini menganalisis terutamanya pengoptimuman yang dilakukan dalam Vue3.0 peringkat kompilasi dan cara menggunakan strategi pengoptimuman ini untuk mengurangkan bilangan perbandingan dalam peringkat patch. Memandangkan keseluruhan vnode pokok komponen masih perlu dilalui apabila komponen dikemas kini, seperti templat berikut:

<template>
  <div id="container">
    <p class="text">static text</p>
    <p class="text">static text</p>
    <p class="text">{{ message }}</p>
    <p class="text">static text</p>
    <p class="text">static text</p>
  </div>
</template>

Keseluruhan proses perbezaan adalah seperti yang ditunjukkan dalam rajah:

Apakah pengoptimuman yang telah dilakukan dalam kompilasi vue3?

Seperti yang anda lihat, kerana hanya terdapat satu nod dinamik dalam kod ini, terdapat banyak perbezaan dan traversal di sini yang sebenarnya tidak diperlukan Ini akan menyebabkan prestasi vnod untuk berkait secara langsung dengan saiz templat, dan dengan nod dinamik Nombor itu tiada kaitan dengan Apabila terdapat hanya beberapa nod dinamik dalam keseluruhan templat beberapa komponen, traversal ini membazirkan prestasi. Untuk contoh di atas, sebaiknya anda hanya perlu membezakan tag p bagi nod dinamik mesej terikat.

Vue.js 3.0 disusun untuk menjana Block tree dengan menganalisis templat statik semasa fasa penyusunan.

Block tree ialah blok bersarang yang memotong templat berdasarkan arahan nod dinamik Struktur nod di dalam setiap blok ditetapkan dan setiap blok hanya perlu dijejaki dengan satu Array Nod dinamik yang terkandung dalam. sendiri. Dengan bantuan Block tree, Vue.js meningkatkan prestasi kemas kini vnode daripada berkaitan dengan saiz keseluruhan templat kepada berkaitan dengan jumlah kandungan dinamik Ini adalah pencapaian prestasi yang sangat besar.

PatchFlag

Memandangkan algoritma diff tidak dapat mengelakkan operasi perbandingan yang tidak berguna antara DOM maya lama dan baharu, Vue.js 3.0 memperkenalkan patchFlag untuk menandakan kandungan dinamik . Semasa proses penyusunan, pengecam yang berbeza akan ditanda mengikut jenis atribut yang berbeza, sekali gus merealisasikan algoritma diff yang pantas. Semua jenis penghitungan untuk PatchFlags adalah seperti berikut:

export const enum PatchFlags {
  TEXT = 1, // 动态文本节点
  CLASS = 1 << 1, // 动态class
  STYLE = 1 << 2, // 动态style
  PROPS = 1 << 3, // 除了class、style动态属性
  FULL_PROPS = 1 << 4, // 有key,需要完整diff
  HYDRATE_EVENTS = 1 << 5, // 挂载过事件的
  STABLE_FRAGMENT = 1 << 6, // 稳定序列,子节点顺序不会发生变化
  KEYED_FRAGMENT = 1 << 7, // 子节点有key的fragment
  UNKEYED_FRAGMENT = 1 << 8, // 子节点没有key的fragment
  NEED_PATCH = 1 << 9, // 进行非props比较, ref比较
  DYNAMIC_SLOTS = 1 << 10, // 动态插槽
  DEV_ROOT_FRAGMENT = 1 << 11, 
  HOISTED = -1, // 表示静态节点,内容变化,不比较儿子
  BAIL = -2 // 表示diff算法应该结束
}

Pokok Sekat

Apakah pengoptimuman yang telah dilakukan dalam kompilasi vue3?

di sebelah kiri template Selepas penyusunan, fungsi render di sebelah kanan akan dihasilkan, yang mengandungi fungsi tambahan seperti _openBlock, _createElementBlock, _toDisplayString, _createElementVNode(createVnode), dsb.

let currentBlock = null
function _openBlock() {
  currentBlock = [] // 用一个数组来收集多个动态节点
}
function _createElementBlock(type, props, children, patchFlag) {
  return setupBlock(createVnode(type, props, children, patchFlag));
}

export function createVnode(type, props, children = null, patchFlag = 0) {
  const vnode = {
    type,
    props,
    children,
    el: null, // 虚拟节点上对应的真实节点,后续diff算法
    key: props?.["key"],
    __v_isVnode: true,
    shapeFlag,
    patchFlag 
  };
  ...

  if (currentBlock && vnode.patchFlag > 0) {
    currentBlock.push(vnode);
  }
  return vnode;
}

function setupBlock(vnode) {
  vnode.dynamicChildren = currentBlock;
  currentBlock = null;
  return vnode;
}

function _toDisplayString(val) {
  return isString(val)
    ? val
    : val == null
    ? ""
    : isObject(val)
    ? JSON.stringify(val)
    : String(val);
}

Vnod yang dijana pada masa ini adalah seperti berikut:

Apakah pengoptimuman yang telah dilakukan dalam kompilasi vue3?

Nod maya yang dijana pada masa ini mempunyai satu lagi atribut dynamicChildren, yang mengumpul nod dinamik span.

Strategi pengoptimuman perbezaan nod:

Kami telah menganalisis sebelum itu apabila mengemas kini elemen nod dalam peringkat patch, fungsi patchElement akan dilaksanakan ia sekali lagi Pelaksanaannya:

const patchElement = (n1, n2) => { // 先复用节点、在比较属性、在比较儿子
  let el = n2.el = n1.el;
  let oldProps = n1.props || {}; // 对象
  let newProps = n2.props || {}; // 对象
  patchProps(oldProps, newProps, el);

  if (n2.dynamicChildren) { // 只比较动态元素
    patchBlockChildren(n1, n2);
  } else {
    patchChildren(n1, n2, el); // 全量 diff
  }
}

Kami menganalisis proses ini dalam bab kemas kini komponen sebelumnya Semasa menganalisis kemas kini nod anak, kami tidak mengambil kira senario pengoptimuman pada masa itu, jadi kami hanya menganalisis. kemas kini perbandingan penuh.

Sebenarnya, jika ini vnode adalah Block vnode, maka kita tidak perlu melalui perbandingan penuh patchChildren, kita hanya perlu melalui patchBlockChildren untuk membandingkan dan mengemas kini dinamik dalam Block Hanya nod anak. Dapat dilihat bahawa prestasi telah dipertingkatkan dengan banyak, daripada perbandingan peringkat tree kepada perbandingan struktur linear.

Mari kita lihat pelaksanaannya:

const patchBlockChildren = (n1, n2) => {
  for (let i = 0; i < n2.dynamicChildren.length; i++) {
    patchElement(n1.dynamicChildren[i], n2.dynamicChildren[i])
  }
}

Strategi pengoptimuman perbezaan atribut:

Seterusnya mari kita lihat pengoptimuman atribut Strategi perbandingan:

const patchElement = (n1, n2) => { // 先复用节点、在比较属性、在比较儿子
  let el = n2.el = n1.el;
  let oldProps = n1.props || {}; // 对象
  let newProps = n2.props || {}; // 对象
  let { patchFlag, dynamicChildren } = n2
  
  if (patchFlag > 0) {
    if (patchFlag & PatchFlags.FULL_PROPS) { // 对所 props 都进行比较更新
      patchProps(el, n2, oldProps, newProps, ...)
    } else {
      // 存在动态 class 属性时
      if (patchFlag & PatchFlags.CLASS) {
        if (oldProps.class !== newProps.class) {
          hostPatchProp(el, 'class', null, newProps.class, ...)
        }
      }
      // 存在动态 style 属性时
      if (patchFlag & PatchFlags.STYLE) {
        hostPatchProp(el, 'style', oldProps.style, newProps.style, ...)
      }
      
      // 针对除了 style、class 的 props
      if (patchFlag & PatchFlags.PROPS) {
        const propsToUpdate = n2.dynamicProps!
        for (let i = 0; i <p> Ringkasan: <code>vue3</code> akan menggunakan sepenuhnya <code>patchFlag</code> dan <code>dynamicChildren</code> untuk pengoptimuman. Jika ditentukan bahawa hanya terdapat perubahan setempat, seperti <code>style</code> perubahan, maka hanya <code>hostPatchProp</code> akan dipanggil dan parameter yang sepadan <code>style</code> akan dihantar untuk melakukan kemas kini tertentu (<strong> kemas kini disasarkan </strong>); jika terdapat <code>dynamicChildren</code>, <code>patchBlockChildren</code> akan dilaksanakan untuk perbandingan dan kemas kini, <strong> tidak akan melakukan perbandingan penuh dan kemas kini </strong> pada prop dan nod anak setiap kali. Rajah adalah seperti berikut: </p><p><img src="https://img.php.cn/upload/image/773/463/495/1671443691817446.png" title="1671443691817446.png" alt="Apakah pengoptimuman yang telah dilakukan dalam kompilasi vue3?"></p><h2 data-id="heading-4"><strong>静态提升</strong></h2><p>静态提升是将静态的节点或者属性提升出去,假设有以下模板:</p><pre class="brush:js;toolbar:false;"><div>
  <span>hello</span> 
  <span a=1 b=2>{{name}}</span>
  <a><span>{{age}}</span></a>
</div>

编译生成的 render 函数如下:

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", null, [
    _createElementVNode("span", null, "hello"),
    _createElementVNode("span", {
      a: "1",
      b: "2"
    }, _toDisplayString(_ctx.name), 1 /* TEXT */),
    _createElementVNode("a", null, [
      _createElementVNode("span", null, _toDisplayString(_ctx.age), 1 /* TEXT */)
    ])
  ]))
}

我们把模板编译成 render 函数是这个酱紫的,那么问题就是每次调用 render 函数都要重新创建虚拟节点。

开启静态提升 hoistStatic 选项后

const _hoisted_1 = /*#__PURE__*/_createElementVNode("span", null, "hello", -1 /* HOISTED */)
const _hoisted_2 = {
  a: "1",
  b: "2"
}

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", null, [
    _hoisted_1,
    _createElementVNode("span", _hoisted_2, _toDisplayString(_ctx.name), 1 /* TEXT */),
    _createElementVNode("a", null, [
      _createElementVNode("span", null, _toDisplayString(_ctx.age), 1 /* TEXT */)
    ])
  ]))
}

预解析字符串化

静态提升的节点都是静态的,我们可以将提升出来的节点字符串化。 当连续静态节点超过 10 个时,会将静态节点序列化为字符串。

假如有如下模板:

<div>
  <span>static</span>
  <span>static</span>
  <span>static</span>
  <span>static</span>
  <span>static</span>
  <span>static</span>
  <span>static</span>
  <span>static</span>
  <span>static</span>
  <span>static</span>
</div>

开启静态提升 hoistStatic 选项后

const _hoisted_1 = /*#__PURE__*/_createStaticVNode("<span>static</span><span>static</span><span>static</span><span>static</span><span>static</span><span>static</span><span>static</span><span>static</span><span>static</span><span>static</span>", 10)
const _hoisted_11 = [  _hoisted_1]

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", null, _hoisted_11))
}

函数缓存

假如有如下模板:

<div @click="event => v = event.target.value"></div>

编译后:

const _hoisted_1 = ["onClick"]

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", {
    onClick: event => _ctx.v = event.target.value
  }, null, 8 /* PROPS */, _hoisted_1))
}

每次调用 render 的时候要创建新函数,开启函数缓存 cacheHandlers 选项后,函数会被缓存起来,后续可以直接使用

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", {
    onClick: _cache[0] || (_cache[0] = event => _ctx.v = event.target.value)
  }))
}

总结

以上几点即为 Vuejs 在编译阶段做的优化,基于上面几点,Vuejspatch 过程中极大地提高了性能。

【相关推荐:vuejs视频教程web前端开发

Atas ialah kandungan terperinci Apakah pengoptimuman yang telah dilakukan dalam kompilasi vue3?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn