前言
vue
的构建其实是分为了两种版本,即完整版本: runtime+compiler
和 运行时版本: runtime
。这两种版本各有什么不同呢?
让我们打开 Vue官网 可以看到Vue给我们提供了一个 compile
函数,通过传入html的字符串就可以获得两个渲染函数。
注意加粗部分的 完整版时可用 表达的意思就是只能在runtime+compiler的构建模式下才能使用。
由于完整版需要编译器的参与,所以传统的html中的js如果都这么写的话那么每次项目运行时编译就会耗费很长的时间。
我们现在大多数项目( webpack\vite
等在内的打包工具下)基本都是只有runtime阶段,而compile阶段则是由像 vue-loader
这种插件帮我们在项目运行的预编译阶段就已经做了
但是为了更好的学(面)习(试)源(吹)码(牛) 🤩, 所以我们还是需要了解其中的原理以及学习其中良好的代码风格。
正文
首先准备好vue2.x的源码,由于版本不同代码可能不太相同,这里使用的版本是 2.6.14
我们知道vue的编译分为了三个阶段:
-
模版解析阶段 :即将一堆模板字符串用正则等方式解析成抽象语法树AST(解析器)
-
优化阶段:遍历AST,找出其中的静态节点,并打上标记(优化器)
-
代码生成阶段:将AST转换成渲染函数(代码生成器)
下面我们将根据源码中文件的调用顺序一步一步的了解 compile
阶段,vue到底做了哪些工作。
多图警告⚠️ 恐图者甚!!!
入口文件
首先进入到入口文件src/platforms/web/entry-runtime-with-compiler.js
-
这里首先通过判断有没有 render函数,如果有了那就直接通过render函数返回dom。
-
反之则获取template模版。重点看图片注释
-
然后就走到了
compileToFunctions
函数部分,返回了render函数和staticRender函数用于生成VDOM
生成Render函数
进入到 compileToFunctions
函数中,发现调用了 createCompiler
函数
随后进入到 createCompiler
函数中,发现通过又调用了 createCompilerCreator
函数。
这个函数返回了一个对象 CompiledResult
用于返回render和staticRender和ast等属性,这个对象的类型如下:
declare type CompiledResult = {
ast: ?ASTElement;
render: string;
staticRenderFns: Array<string>;
stringRenderFns?: Array<string>;
errors?: Array<string | WarningMessage>;
tips?: Array<string | WarningMessage>;
};
进入到 createCompilerCreator
函数发现通过函数闭包,返回了一个对象其中有两个函数, compile
函数 和 compileToFunctions
函数 ,compile函数中,执行createCompilerCreator
传入的函数参数 baseCompile
进行编译的三个流程。其中具体的代码如下。
所以最重要的就是这个 compile
函数,我们也可以看到返回的第二个函数也调用了一下这个compile,其实作用就是使这个 compileToFunctions
具有编译生成 render
函数的作用。
由此这个函数就开始了编译的三个阶段
-
Vue通过HTMLParse这个库将template模版解析成AST树。
-
通过optimize标记静态节点作为常量,并在使得在VDOM diff时不在更新。
-
通过
generate
函数生成render
函数和staticRenderFns
函数用于后面生成虚拟DOM
具体的这三个流程的代码的话可以自行查看,这里不在赘述。主要说一下 generate
函数
函数中通过挂载到 vm
上到 _c
实例方法(其实就是调用了 createElement
方法)进行 ast
生成的,然后render字符串通过 with()包裹返回
, staticRenderFns
在之前已经做过静态标记,所以就直接取state里的staticRenderFns了。 ☠️
除了
_c
方法还有_m
等方法通过core/instance/render.js
下renderMixin
函数调用installRenderHelpers
函数,挂载到实例上
以上就是全部的compile阶段的整个流程,其中有部分代码(HTMLParse解析、optimize如何做静态标记、生成render字符串)没有深入探究,后续有机会再探讨! 🤣
Vue模版AST标记详情可参考
后记
既然我们通过 compile阶段
得到了render函数, runtime阶段
那么Vue又是如何将render函数转换生成虚拟dom的呢? 😎
其实在 new Vue()
时Vue就调用了 initRender
方法,调用了一个 _c
方法也就是 createElement
方法(也就是在开发时写render函数的参数 h
),这个方法返回了一个 _createElement
方法就会去递归创建 Vnode
树。具体代码在 core/vdom/index.js
中。
实例化流程
initMixin方法中
:
生成vdom后,vue就将对data等进行数据劫持等操作,以及其他的一些操作。至此,Vue就走到了created阶段,data和vdom也准备好了。
✍️中西结合疗效好
最后结合Vue的生命周期图来看,就能更好的理解 Vue的整个生命周期函数过程了
✋全文只代表作者个人观点,有分析错误的,记得指出哦~ 😀