Rumah > hujung hadapan web > View.js > teks badan

Apakah kandungan kompilasi dan pengoptimuman vue3?

王林
Lepaskan: 2023-05-17 17:50:16
ke hadapan
1315 orang telah melayarinya

pengoptimuman kompilasi vue3 termasuk: 1. Memperkenalkan patchFlag untuk menandakan kandungan dinamik semasa proses penyusunan, logo yang berbeza akan ditandakan mengikut jenis atribut yang berbeza, sekali gus merealisasikan algoritma perbezaan pantas . 2. Pokok Blok. 3. Promosi statik adalah untuk mempromosikan nod atau atribut statik. Apabila terdapat lebih daripada 10 nod statik berturut-turut, prapemprosesan akan dirangkai dan digabungkan menjadi urutan berterusan nod statik. Apabila pilihan cacheHandlers didayakan, fungsi tersebut akan dicache supaya ia boleh dipanggil terus kemudian.

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>
Salin selepas log masuk

Keseluruhan proses perbezaan adalah seperti yang ditunjukkan dalam rajah:

Apakah kandungan kompilasi dan pengoptimuman vue3?

Seperti yang anda boleh lihat, kerana hanya terdapat satu nod dinamik dalam kod ini, terdapat banyak perbezaan dan traversal yang sebenarnya tidak diperlukan Ini akan menyebabkan prestasi vnod menjadi secara langsung berkaitan dengan saiz templat dan bilangan nod dinamik Tanpa mengira , 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 oleh 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, label yang berbeza akan ditanda mengikut jenis atribut yang berbeza, sekali gus merealisasikan algoritma diff yang pantas. Semua jenis penghitungan untuk PatchFlags kelihatan seperti ini:

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算法应该结束
}
Salin selepas log masuk

Pokok Sekat

Apakah kandungan kompilasi dan pengoptimuman vue3?

template di sebelah kiri disusun Kemudian 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);
}
Salin selepas log masuk

Vnod yang dijana pada masa ini adalah seperti berikut:

Apakah kandungan kompilasi dan pengoptimuman vue3?

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

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
  }
}
Salin selepas log masuk

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 senario bagi. perbandingan penuh dan kemas kini.

Sebenarnya, jika ini vnode adalah Block vnode, maka kita tidak perlu melalui patchChildren untuk perbandingan penuh, 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])
  }
}
Salin selepas log masuk

Strategi pengoptimuman perbezaan atribut:

Seterusnya mari kita lihat strategi pengoptimuman untuk perbandingan atribut :

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, &#39;class&#39;, null, newProps.class, ...)
        }
      }
      // 存在动态 style 属性时
      if (patchFlag & PatchFlags.STYLE) {
        hostPatchProp(el, &#39;style&#39;, oldProps.style, newProps.style, ...)
      }
      
      // 针对除了 style、class 的 props
      if (patchFlag & PatchFlags.PROPS) {
        const propsToUpdate = n2.dynamicProps!
        for (let i = 0; i < propsToUpdate.length; i++) {
          const key = propsToUpdate[i]
          const prev = oldProps[key]
          const next = newProps[key]
          if (next !== prev) {
            hostPatchProp(el, key, prev, next, ...)
          }
        }
      }
      if (patchFlag & PatchFlags.TEXT) { // 存在动态文本
        if (n1.children !== n2.children) {
          hostSetElementText(el, n2.children as string)
        }
      } 
    } else if (dynamicChildren == null) {
      patchProps(el, n2, oldProps, newProps, ...)
    }
  }
}

function hostPatchProp(el, key, prevValue, nextValue) {
  if (key === &#39;class&#39;) { // 更新 class 
    patchClass(el, nextValue)
  } else if (key === &#39;style&#39;) { // 更新 style
    patchStyle(el, prevValue, nextValue)
  } else if (/^on[^a-z]/.test(key)) {  // events  addEventListener
    patchEvent(el, key, nextValue);
  } else { // 普通属性 el.setAttribute
    patchAttr(el, key, nextValue);
  }
}

function patchClass(el, nextValue) {
  if (nextValue == null) {
    el.removeAttribute(&#39;class&#39;); // 如果不需要class直接移除
  } else {
    el.className = nextValue
  }
}

function patchStyle(el, prevValue, nextValue = {}){
  ...
}

function patchAttr(el, key, nextValue){
  ...
}
Salin selepas log masuk

Ringkasan: vue3 akan menggunakan sepenuhnya patchFlag dan dynamicChildren untuk pengoptimuman. Jika ditentukan bahawa hanya terdapat perubahan setempat, seperti style perubahan, maka hanya hostPatchProp akan dipanggil dan parameter yang sepadan style akan dihantar untuk melakukan kemas kini tertentu ( kemas kini disasarkan ); jika terdapat dynamicChildren, patchBlockChildren akan dilaksanakan untuk perbandingan dan kemas kini, tidak akan melakukan perbandingan penuh dan kemas kini pada prop dan nod anak setiap kali. Rajah adalah seperti berikut:

Apakah kandungan kompilasi dan pengoptimuman vue3?

Promosi statik

Promosi statik adalah untuk mempromosikan nod atau atribut statik templat berikut:

<div>
  <span>hello</span> 
  <span a=1 b=2>{{name}}</span>
  <a><span>{{age}}</span></a>
</div>
Salin selepas log masuk

编译生成的 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 */)
    ])
  ]))
}
Salin selepas log masuk

我们把模板编译成 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 */)
    ])
  ]))
}
Salin selepas log masuk

预解析字符串化

静态提升的节点都是静态的,我们可以将提升出来的节点字符串化。 当连续静态节点超过 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>
Salin selepas log masuk

开启静态提升 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))
}
Salin selepas log masuk

函数缓存

假如有如下模板:

<div @click="event => v = event.target.value"></div>
Salin selepas log masuk

编译后:

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))
}
Salin selepas log masuk

每次调用 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)
  }))
}
Salin selepas log masuk

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

Label berkaitan:
sumber:yisu.com
Kenyataan Laman Web ini
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
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!