今天学习《Vue模板编译原理》

事前准备


首先例行编译vue的sourcemap:

rollup -w -c scripts/config.js 
--sourcemap 
--environment TARGET:web-full-dev

我们准备如下内容,方便我们调试


<!DOCTYPE html>
<html>
  <head>
    <title>Vue源码剖析</title>
    <script src="/dist/vue.js"></script>
  </head>

  <body>
    <div id="demo">
      <!-- 静态节点,不需要比对 -->
      <h1>Vue模板<span>编译</span></h1>
      <p v-if="foo">{{foo}}</p>
      <comp></comp>
    </div>
    <script>
      Vue.component("comp", {
        template: "<div>I am comp</div>",
      });
      // 创建实例
      const app = new Vue({
        el: "#demo",
        data: { foo: "foo" },
      });
      // 输出render函数
      console.log(app.$options.render);
    </script>
  </body>
</html>

源码分析


entry-runtime-with-compiler.js


因为我们使用的 vue 打包编译器+运行时。通过之前的介绍,可以进入


src/platforms/web/entry-runtime-with-compiler.js 


因为我们是走的 template 模板,会看到 类似 if(template) 的判断代码。

如图所示已经拿到了模板内容,接下来就是如何解析了。

我们在 compileToFunctions 打断点,进入。

截屏2020-05-16 上午10.57.07.png


to-function.js


我们进入 compileToFunctions 内部,发现返回了一个function,~~这里对 options 做了修改,不重要。~~

最终看到,把 template传给 compile 函数。

截屏2020-05-16 上午11.04.35.png


进入这个函数。


create-compiler.js


返现还是包装了一层 function,跳过,定位到这一行:

const compiled = baseCompile(template.trim(),finalOptions)

发现,之前的操作其实是对options做修正,终于要进入编译了。我们进入这个函数


compiler/index.js


终于对 template这个字符串进行操作了。整个编译分三步: 解析 + 优化 + 生成。


解析


于是就有了这行代码:

const ast = parse(template.rom().options)

截屏2020-05-16 上午11.15.26.png


这里忽略 parse的具体实现,最终我们拿到了 ast,如上图所示:这是一个对象,通过空间换时间,我们对常用的属性做了解析。


优化


我们的template里有一些静态的内容,比如静态节点,后续vue不应当关心这些内容,因为不会发生变化。

我们会在截图的 Line17 做了 optimize 做了优化。

优化的结果是,多了 static 标记。后续看到 static 如果是 true,干脆就不关注了。


image.png

之前看vue作者再b站开直播,讲到vue3会对这一块做更细致的操作。


生成


通过 generate 操作,我们就拿到了 render函数。


image.png


这样 渲染函数拿到了,就可以执行后续的操作了。注意这里是 字符串。


to-function


拿到 字符串渲染函数了,后续如何render呢,我们退回 to-funciton 这个文件,会看到


res.render = createFunction(compiled.render,funGenErrors)

刚才的截图已知, compiled.render 是一个字符串。我们关注 createFunction 是如何操作的。

定位到 function createFunction  我们发现核心代码:

return new Function(code)

直接把 字符串变为Function。


这样整个渲染函数的流程就走通了。


整体流程如下: