vue编译流程

217 阅读1分钟

之前我们分析过模板到真实 DOM 渲染的过程,中间有一个环节是把模板编译成 render 函数,这个过程我们把它称作编译。

虽然我们可以直接为组件编写 render 函数,但是编写 template 模板更加直观,也更符合我们的开发习惯。

接下来我们就来分析编译的过程,不过由于编译的过程是一个相对复杂的过程,我们只要理解整体的流程、输入和输出即可,对于细节不必抠太细。

编译

当我们执行 new Vue 时,初始化后会执行挂载流程。在执行挂载前,会从配置项中优先去找 render 函数。用户可以手写 render 函数,如果没有手写,Vue 会去匹配 template 选项、el选项,匹配到后会执行模板编译的流程,我们来看一下这块的代码:

if (vm.$options.el) {
  vm.$mount(vm.$options.el)
}

Vue.prototype.$mount = function (el) {
  el = el && query(el) // query: document.querySelector(el)
  ...,
  const options = this.$options
  if (!options.render) {
    let template = options.template
    if (template) {
      if (typeof template === 'string') {
        if (template.charAt(0) === '#') {
          template = idToTemplate(template)
        }
      } else if (template.nodeType) {
        template = template.innerHTML
      }
    } else if (el) {
      template = getOuterHTML(el)
    }
    
    // 编译
    if (template) { 
      const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns
    }
  }
  
  // 挂载
  return mount.call(this, el, hydrating)
}

compileToFunctions 方法就是把模板 template 编译生成 render 以及 staticRenderFns,由于内部代码书写顺序特别繁琐,我这里画了一张图用来描述这个过程:

45.png

可以看到,执行 compileToFunctions 最终会执行到 baseCompile 函数, baseCompile 函数内部做了 3 件事:

1.解析模板字符串生成 AST

  const ast = parse(template.trim(), options)
  1. 优化语法树(标记静态节点,标记静态根,在patch的时候如果是静态节点,会跳过比对)
optimize(ast, options)
  1. 生成代码
const code = generate(ast, options)

我们用下面的 demo 来看一下这个流程:

<div id="app"></div>
<script>
  new Vue({
    el: '#app',
    data() {
      return {
        bindCls: 'parse',
        isShow: true,
        data: ['a', 'b', 'c']
      }
    },
    methods: {
      clickItem(idx) {
        console.log(idx)
      }
    },
    template: `<ul :class="bindCls" class="list" v-if="isShow"><li v-for="(item,index) in data" @click="clickItem(index)">{{item}}:{{index}}</li></ul>`
  })
</script>

最终生成的 render 函数如下所示:

"with(this){return (isShow)?_c('ul',_l((data),function(item,index){return _c('li',{on:{"click":function($event){return clickItem(index)}}},[_v(_s(item)+":"+_s(index))])}),0):_e()}"

44.png

编译流程

下面让我们看看 parse optimize generate 内部的执行流程

未完待续,敬请期待...