Vue 源码剖析- 模板编译

321 阅读2分钟

「这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战

模板编译

本章呢,还是我最喜欢的炒冷饭环节,通过Vue中的源码,学习模板编译的过程

u=784990673,236832882&fm=15&fmt=auto.webp

模板编译的主要目的

  • 模板编译的主要目的是将模板 (template) 转换为渲染函数 (render)
<div>

    <h1 @click="handler">title</h1>

    <p>some content</p>

</div>
  • 渲染函数 render
render (h) {
    return h('div', [
        h('h1', { on: { click: this.handler} }, 'title'),
        h('p', 'some content')
    ])
}

这就是上面的html变成render函数的效果啦!

模板编译的作用

  • Vue 2.x 使用 VNode 描述视图以及各种交互,用户自己编写 VNode 比较复杂

  • 用户只需要编写类似 HTML 的代码(它的本质就是 Vue模板 ),通过编译器将模板转换为返回 VNoderender 函数

  • .vue 文件会被 webpack 在构建的过程中转换成 render 函数,webpack本身是不支持把模板编译成 render函数的,它本身是通过vue-loader来实现这一步操作的

体验模板编译的结果

  • 带编译器版本的 Vue.js 中,使用 template 或 el 的方式设置模板

    <div id="app"> 
        <h1>Vue<span>模板编译过程</span></h1> 
        <p>{{ msg }}</p> 
        <comp @myclick="handler"></comp> 
    </div> 
    
    <script src="../../dist/vue.js"></script> 
    <script> 
    Vue.component('comp', { 
      template: '<div>I am a comp</div>'         
    })
    const vm = new Vue({ 
        el: '#app', 
        data: { 
            msg: 'Hello compiler'
        },
        methods: { 
            handler () { 
                console.log('test')
            }
        }
    }) 
    console.log(vm.$options.render)
    </script>
    
  • 编译后 render 输出的结果

    (function anonymous() { 
         with (this) { 
            return _c( 
                "div", 
                { attrs: { id: "app" } }, 
                [ _m(0),  // 用来处理静态内容
                _v(" "),  // 创建一个空白的文本节点
                _c("p", [_v(_s(msg))]), // createElement; `_s` 是调用 toString函数
                _v(" "), 
                _c("comp", { on: { myclick: handler } }),  // 创建自定义组件的VNode
            ],
            1 
          ); 
        } 
    });
    
  • _ccreateElement() 方法,定义的位置 instance/render.js

  • 相关的渲染函数(_开头的方法定义),在 instance/render-helps/index.js

    // instance/render-helps/index.js 
    target._v = createTextVNode
    target._m = renderStatic
    
    // core/vdom/vnode.js export
    function createTextVNode(val: string | number) {
      return new VNode(undefined, undefined, undefined, String(val))
    }
    
    // 在 instance/render-helps/render-static.js 
    export function renderStatic(index: number, isInFor: boolean): VNode | Array<VNode> {
      const cached = this._staticTrees || (this._staticTrees = []) 
      let tree = cached[index]
      // 如果已经渲染了静态树并且不在 v-for 中,
      // 我们可以重用同一棵树。
      if (tree && !isInFor) { return tree }
      // 否则,渲染一棵新鲜的树。
      tree = cached[index] = this.$options.staticRenderFns[index].call(
      this._renderProxy, 
      null, 
      this // 为功能组件模板生成的渲染 fns
      )
      markStatic(tree, `__static__${index}`, false) 
      return tree
    }
    
  • template 转换成 render 的入口 src\platforms\web\entry-runtime-with-compiler.js

Vue Template Explorer

我们可以通过下面两个网页工具来学习render函数,也可以用这个工具 对 Vue2.X Vue3.Xrender 函数进行比对,可以发现两个版本的模板编译差别(说不定到时候就是面试加分项哦!)

小结

模板编译的过程用三个词来说

  • 解析:解析器将模板解析为抽象语树 AST,只有将模板解析成 AST 后,才能基于它做优化或者生成代码字符串
  • 优化:优化抽象语法树,检测子节点中是否是纯静态节点
  • 生成 很简单的总结,但是里面的东西却非常的多,下个小结我们会仔细进行详细解析的,希望大家可以多多学习啦!

关注!收藏!点赞! 求求啦!你的每一个点赞都是我更新的动力!

求赞.jpeg