1.vue构建流程 el,template, render优先级

47 阅读2分钟

参考来源:time.geekbang.org/column/intr…
作者:杨文坚

1. rendertemplateel顺序

render优先级最高 > 其次template > 最后el

2.代码参考

  1. _ctx代表组件的上下文对象,它封装了组件实例的所有可访问属性,包括但不限于props、data、methods、computed properties等。在Vue 3的Composition API中,尽管我们倾向于使用setup函数内部声明的响应式变量(如refreactive创建的),但在某些情况下,直接访问_ctx仍然有用,尤其是在与Vue的生命周期钩子、自定义指令或其他需要访问组件实例属性的场景中。

2._cache主要用于优化渲染性能。Vue在渲染函数模式下,会尝试利用纯函数的特性进行优化。当渲染函数被多次调用时,Vue会检查是否可以复用之前的计算结果,从而避免不必要的计算和DOM操作。_cache数组就扮演了这个角色,Vue会自动管理它,开发者也可以直接利用它来手动缓存数据。

  const Counter = {
    setup() {
      const num = ref(0);
      
      const complexCalculation = (n) => {
        // 假设这是一个复杂的计算过程
        for(let i = 0; i < 100000; i++) {
          // ...计算逻辑
        }
        return `计算结果: ${n * n}`;
      };
  
      return (_ctx, _cache) => {
        if (!_cache[0]) {
          // 首次计算并将结果存储在_cache中
          _cache[0] = complexCalculation(num.value);
        } else if (num.value !== _cache[1]) {
          // 当依赖变化时,重新计算并更新_cache
          _cache[0] = complexCalculation(num.value);
          _cache[1] = num.value; // 记录最新依赖值
        }
  
        return h('div', { class: 'v-counter' }, [
          h('div', { class: 'v-text' }, _cache[0]),
          h('button', { class: 'v-btn', onClick: () => num.value++ }, '点击加1')
        ]);
      };
    },
  };
  • _ctx提供了对组件实例属性和方法的访问途径,尽管在Composition API中直接使用setup内的变量更为常见。
  • _cache则是Vue内部用于优化渲染性能的工具,开发者可以直接利用它来存储中间计算结果,避免不必要的重复计算。
<html>
  <head>
    <style>
      .v-counter {
        width: 400px;
        margin: 20px auto;
        padding: 10px;
        color: #666666;
        box-shadow: 0px 0px 9px #00000066;
        text-align: center;
      }
      .v-counter .v-text {
        font-size: 28px;
        font-weight: bolder;
      }
      .v-counter .v-btn {
        font-size: 20px;
        padding: 0 10px;
        height: 32px;
        cursor: pointer;
      }
    </style>
    <script src="https://unpkg.com/vue@3.2.37/dist/vue.global.js" ></script>
    <!-- <script src="./node_modules/vue/dist/vue.global.js" ></script> -->
  </head>

  <body>
    <div id="app">
      <div>此处是el的数据{{ num }}</div>
      <button>el按钮+1</button>
    </div>
  </body>

  <script>
    (function() {
      const { h,toDisplayString,createApp, ref, createVNode,createElementVNode } = window.Vue;
      const Counter = {
        template: `
        <div class="v-counter">
          <div class="v-text">{{ num }}</div>
          <button class="v-btn" @click="click">点击加1</button>
        </div>
        `,
        setup() {
          const num = ref(0)
          const click = () => {
            num.value += 1;
          }
          return {
            num,
            click
          }
        }
      }
      const CreateElementVNodeCounter = {
        setup() {
          const num = ref(0)
          const click = () => {
            num.value += 1;
          }
          return (_ctx, _cache) => {
            return (
              createElementVNode('div', { class: 'v-counter' }, [
                createElementVNode('div', { class: 'v-text' }, toDisplayString(num.value)),
                createElementVNode("button", { class: 'v-btn', onClick: click }, "CreateElementVNode点击加1")
              ])
            )
          }
        }
      }
      const HCounter = {
        setup() {
          const num = ref(0)
          const click = () => {
            num.value += 1;
          }
          return (_ctx, _cache) => {
            return h('div', { class: 'v-counter' }, [
              h('div', { class: 'v-text' }, toDisplayString(num.value)),
              h('button', {
                class: 'v-btn',
                onClick: click
              }, "h函数点击加1")
            ])
          }
        }
      }
      const RenderCounter = {
        template: `
        <div class="v-counter">
          <div class="v-text">{{ num }}</div>
          <button class="v-btn" @click="click">render点击加1</button>
        </div>
        `,
        setup() {
          const num = ref(0)
          const click = () => {
            num.value += 1;
          }
          return {
            num,
            click
          }
        }
      }

      // 组件挂载渲染
      const app = createApp({
        template: `<div>
                     <v-counter />
                     <CreateElementVNodeCounter />
                     <h-counter />
                     <v-render-counter />
                  </div>`,
        // render() {
        //   return  createVNode(RenderCounter)
        // }
      });
      app.component('v-counter', Counter)
      app.component('v-render-counter', RenderCounter)
      app.component('h-counter', HCounter)
      app.component('CreateElementVNodeCounter', CreateElementVNodeCounter)
      app.mount('#app')
    })()
  </script>

</html>