|
@@ -419,6 +419,221 @@ if (process.env.NODE_ENV !== 'production') {
|
|
|
|
|
|
#### compile 的作用
|
|
|
|
|
|
+回顾一下 `compileToFunctions` 函数中调用 `compile` 的方式:
|
|
|
+
|
|
|
+```js
|
|
|
+const compiled = compile(template, options)
|
|
|
+```
|
|
|
+
|
|
|
+很简单的一段代码,其中模板字符串 `template` 被透传了过去,选项参数 `options` 经过简单处理后继续作为第二个参数传递给 `compile` 函数,前面我们分析过,这里传递过去的 `options` 如下:
|
|
|
+
|
|
|
+```js
|
|
|
+{
|
|
|
+ shouldDecodeNewlines,
|
|
|
+ shouldDecodeNewlinesForHref,
|
|
|
+ delimiters,
|
|
|
+ comments,
|
|
|
+ warn // 被 delete
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+其中 `warn` 属性被 `delete` 操作符删除。这里只是给大家做一个简短的回顾,并且我们对 `Vue` 的编译器所接收的参数进行归纳,并整理了附录 [编译器选项整理](/note/附录/compiler-options),后面遇到的任何编译器选项都会整理到该附录里,大家可以在这里查阅 `Vue` 编译器所接收的选项。
|
|
|
+
|
|
|
+知道了这些我们就可以去看 `compile` 函数的代码了,我们知道 `compile` 函数是 `createCompileToFunctionFn` 函数的形参,也就是说,`compile` 函数是被从其他地方传递过来了,其实前面我们都分析过,这里的 `compile` 函数就是 `src/compiler/create-compiler.js` 文件中定义在 `createCompiler` 函数内的 `compile` 函数,如下:
|
|
|
+
|
|
|
+```js
|
|
|
+export function createCompilerCreator (baseCompile: Function): Function {
|
|
|
+ return function createCompiler (baseOptions: CompilerOptions) {
|
|
|
+ // 就是这个 compile 函数
|
|
|
+ function compile (
|
|
|
+ template: string,
|
|
|
+ options?: CompilerOptions
|
|
|
+ ): CompiledResult {
|
|
|
+ // 函数体 ...
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ compile,
|
|
|
+ compileToFunctions: createCompileToFunctionFn(compile)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+可以发现,`compile` 函数接收两个参数,分别是模板字符串(`template`)和选项参数(`options`)。我们顺序的查看其函数体代码,首先是这句代码:
|
|
|
+
|
|
|
+```js
|
|
|
+const finalOptions = Object.create(baseOptions)
|
|
|
+```
|
|
|
+
|
|
|
+这句代码通过 `Object.create` 函数以 `baseOptions` 为原型创建 `finalOptions` 常量,`finalOptions` 才是最终的编译选项参数。这里的 `baseOptions` 是 `createCompiler` 函数的形参,也就是在 `src/platforms/web/compiler/index.js` 文件中调用 `createCompiler` 传递过来的参数:
|
|
|
+
|
|
|
+```js
|
|
|
+import { baseOptions } from './options'
|
|
|
+import { createCompiler } from 'compiler/index'
|
|
|
+
|
|
|
+const { compile, compileToFunctions } = createCompiler(baseOptions)
|
|
|
+```
|
|
|
+
|
|
|
+可以看到 `baseOptions` 来自于 `src/platforms/web/compiler/options.js` 文件,下面是该文件的全部代码:
|
|
|
+
|
|
|
+```js
|
|
|
+/* @flow */
|
|
|
+
|
|
|
+import {
|
|
|
+ isPreTag,
|
|
|
+ mustUseProp,
|
|
|
+ isReservedTag,
|
|
|
+ getTagNamespace
|
|
|
+} from '../util/index'
|
|
|
+
|
|
|
+import modules from './modules/index'
|
|
|
+import directives from './directives/index'
|
|
|
+import { genStaticKeys } from 'shared/util'
|
|
|
+import { isUnaryTag, canBeLeftOpenTag } from './util'
|
|
|
+
|
|
|
+export const baseOptions: CompilerOptions = {
|
|
|
+ expectHTML: true,
|
|
|
+ modules,
|
|
|
+ directives,
|
|
|
+ isPreTag,
|
|
|
+ isUnaryTag,
|
|
|
+ mustUseProp,
|
|
|
+ canBeLeftOpenTag,
|
|
|
+ isReservedTag,
|
|
|
+ getTagNamespace,
|
|
|
+ staticKeys: genStaticKeys(modules)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+还是比较简短的,这个文件的主要作用就是到处一个对象,即我们说到的 `baseOptions`,所以下面我们就把 `baseOptions` 这个对象的内容搞清楚。
|
|
|
+
|
|
|
+对象如下:
|
|
|
+
|
|
|
+```js
|
|
|
+{
|
|
|
+ expectHTML: true,
|
|
|
+ modules,
|
|
|
+ directives,
|
|
|
+ isPreTag,
|
|
|
+ isUnaryTag,
|
|
|
+ mustUseProp,
|
|
|
+ canBeLeftOpenTag,
|
|
|
+ isReservedTag,
|
|
|
+ getTagNamespace,
|
|
|
+ staticKeys: genStaticKeys(modules)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+我们一个一个看,第一个属性 `expectHTML` 被设置为 `true`。第二个属性是 `modules`,根据引用关系可知它来自于 `platforms/web/compiler/modules/index.js` 文件,打开这个文件:
|
|
|
+
|
|
|
+```js
|
|
|
+import klass from './class'
|
|
|
+import style from './style'
|
|
|
+import model from './model'
|
|
|
+
|
|
|
+export default [
|
|
|
+ klass,
|
|
|
+ style,
|
|
|
+ model
|
|
|
+]
|
|
|
+```
|
|
|
+
|
|
|
+以上是该文件的全部代码,可以发现 `modules` 实际上就是一个数组,数组有三个元素 `klass`、`style` 以及 `model`,且这三个元素来自于当前目录下的三个同名 `js` 文件。简单查看这三个文件的输出,如下:
|
|
|
+
|
|
|
+```js
|
|
|
+// klass.js 的输出
|
|
|
+export default {
|
|
|
+ staticKeys: ['staticClass'],
|
|
|
+ transformNode,
|
|
|
+ genData
|
|
|
+}
|
|
|
+// style.js 的输出
|
|
|
+export default {
|
|
|
+ staticKeys: ['staticStyle'],
|
|
|
+ transformNode,
|
|
|
+ genData
|
|
|
+}
|
|
|
+// model.js 的输出
|
|
|
+export default {
|
|
|
+ preTransformNode
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+可以看到这三个文件输出的都是对象,且 `klass.js` 文件与 `style.js` 文件的输出基本相同,只有 `staticKeys` 字段有所区别,而 `model.js` 文件输出的对象只包含 `preTransformNode` 属性。最终 `platforms/web/compiler/modules/index.js` 文件将这三个文件的输出综合为一个数组进行输出,所以其输出的内容为:
|
|
|
+
|
|
|
+```js
|
|
|
+[
|
|
|
+ {
|
|
|
+ staticKeys: ['staticClass'],
|
|
|
+ transformNode,
|
|
|
+ genData
|
|
|
+ },
|
|
|
+ {
|
|
|
+ staticKeys: ['staticStyle'],
|
|
|
+ transformNode,
|
|
|
+ genData
|
|
|
+ },
|
|
|
+ {
|
|
|
+ preTransformNode
|
|
|
+ }
|
|
|
+]
|
|
|
+```
|
|
|
+
|
|
|
+以上就是 `baseOptions` 对象第二个属性 `modules` 的内容。`baseOptions` 对象的第三个属性是 `directives`,类似 `modules` 只不过 `directives` 来自于 `platforms/web/compiler/directives/index.js` 文件,该文件源码如下:
|
|
|
+
|
|
|
+```js
|
|
|
+import model from './model'
|
|
|
+import text from './text'
|
|
|
+import html from './html'
|
|
|
+
|
|
|
+export default {
|
|
|
+ model,
|
|
|
+ text,
|
|
|
+ html
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+同样类似于 `modules` 输出,只不过 `directives` 最终输出的不是数组,而是一个对象,这个对象包含三个属性 `model`、`text` 以及 `html`,这三个属性同样来自于当前目前下的三个文件:`model.js`、`text.js` 以及 `html.js` 文件,我们分别查看这三个文件的输出:
|
|
|
+
|
|
|
+```js
|
|
|
+// model.js 的输出
|
|
|
+export default function model (
|
|
|
+ el: ASTElement,
|
|
|
+ dir: ASTDirective,
|
|
|
+ _warn: Function
|
|
|
+): ?boolean {
|
|
|
+ // 函数体...
|
|
|
+}
|
|
|
+// html.js 的输出
|
|
|
+export default function html (el: ASTElement, dir: ASTDirective) {
|
|
|
+ if (dir.value) {
|
|
|
+ addProp(el, 'innerHTML', `_s(${dir.value})`)
|
|
|
+ }
|
|
|
+}
|
|
|
+// text.js 的输出
|
|
|
+export default function text (el: ASTElement, dir: ASTDirective) {
|
|
|
+ if (dir.value) {
|
|
|
+ addProp(el, 'textContent', `_s(${dir.value})`)
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+可以发现,这个三个文件分别输出了三个函数,所以最终 `baseOptions` 对象的 `directives` 属性如下:
|
|
|
+
|
|
|
+```js
|
|
|
+{
|
|
|
+ model: function(){},
|
|
|
+ html: function(){},
|
|
|
+ text: function(){}
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+它是一个包含三个属性的对象,且属性的值都是函数。
|
|
|
+
|
|
|
+`baseOptions` 的第四个属性是 `isPreTag`,它是一个函数,可以在附录 [platforms/web/util 目录下的工具方法全解](/note/附录/web-util) 中查看其实现讲解,其作用是通过给定的标签名字检查标签是否是 `'pre'` 标签。
|
|
|
+
|
|
|
+
|
|
|
|
|
|
|
|
|
|