探索Vue.js底层源码——编译过程

引言

Vue 的编译过程总共分为三个步骤:

  1. 根据 template 生成 AST(抽象语法树)
  2. 优化 AST
  3. 根据 AST 可执行的函数(render 函数之类的)

这三个步骤其实在源码中,也写的清清楚楚。

export const createCompiler = createCompilerCreator(function baseCompile (
  template: string,
  options: CompilerOptions
): CompiledResult {
  // 生成 AST
  const ast = parse(template.trim(), options)
  if (options.optimize !== false) {
  	// 优化 AST
    optimize(ast, options)
  }
  // 生成可执行的函数
  const code = generate(ast, options)
  return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
})

parse

在 Vue 编译过程中,第一件做的事就是将 template 解析成 AST(抽象语法树,Abstract Syntax tree)。
Vue 中定义的 AST:

ASTElement: {
	type: Number, // 1普通元素 2表达式 3纯文本
	tag: String, // 例如 ‘p’
	arrtsList: Array, // 
	rawAttrsMap: {}, // 会将 attrsList 抽成对象
	parent: ASTElement, // 它的父元素 
	children: [] // 每个元素都是 ASTElement
	start: Number, // 它在整个模板的起始位置
	end: Number // 它在整个模板的结束位置
}

PS:其实在 optimize 之后 AST 上还会多出两个属性 static、staticRoot,这个在之后说到 optimize 的时候会提及这两个属性的由来。

而在 Vue 中将 template 生成 AST,则是通过 parse 这个方法。

export function parse (
  template: string,
  options: CompilerOptions
): ASTElement | void {
  ....
  parseHTML(template, options, {
  	... 
	// 遍历 tempalte 根据各种情况生成对应的 AST 树,并形成父子关系
  })
  return root  // 这个就是最终的 AST 树
  ...
}

在生成 template 对应的 AST 后,会对 AST 进行优化,即遍历 AST 树,对每个 AST 元素进行判断,是否为静态 AST 或 静态 AST Root,为之后的 patch 过程提供依据。

optimize

在 Vue 编译过程给 AST 添加 static 或 staitcRoot,则是通过 optimize 这个方法。

export function optimize (root: ?ASTElement, options: CompilerOptions) {
  if (!root) return
  ...
  // 首先对所有的节点进行判断标记,这个添加的是 static 属性
  markStatic(root)
  // 最后标记父节点,这个添加的是 staticRoot 属性
  markStaticRoots(root, false)
}

markStatic

function markStatic (node: ASTNode) {
  node.static = isStatic(node)
  if (node.type === 1) {
    if (
      !isPlatformReservedTag(node.tag) &&
      node.tag !== 'slot' &&
      node.attrsMap['inline-template'] == null
    ) {
      return
    }
    for (let i = 0, l = node.children.length; i < l; i++) {
      const child = node.children[i]
      markStatic(child)
      if (!child.static) {
        node.static = false
      }
    }
    if (node.ifConditions) {
      for (let i = 1, l = node.ifConditions.length; i < l; i++) {
        const block = node.ifConditions[i].block
        markStatic(block)
        if (!block.static) {
          node.static = false
        }
      }
    }
  }
}

其中 isStatic 会对节点进行一些基础的判断,返回 true or false。

如果 AST 的 type 为 2 即表达式,则返回false
如果 AST 的 type 为 3 即纯文本,则返回 true
如果:有 v-pre 指令
	  没有 v-if 或 v-for 指令
	  没有 slot 或 component
	  是平台保留的 HTML 标签
	  如果不是 tempalte 的直接后代
	  AST 中没有静态的 key 标识
则返回 true

markStaticRoot

function markStaticRoots (node: ASTNode, isInFor: boolean) {
  if (node.type === 1) {
    if (node.static || node.once) {
      node.staticInFor = isInFor
    }
    // 这里需要注意 node.static 满足 children.length 存在 长度为1 并且 children 的类别 不能为纯文本类型
    if (node.static && node.children.length && !(
      node.children.length === 1 &&
      node.children[0].type === 3
    )) {
      node.staticRoot = true
      return
    } else {
      node.staticRoot = false
    }
    if (node.children) {
      for (let i = 0, l = node.children.length; i < l; i++) {
        markStaticRoots(node.children[i], isInFor || !!node.for)
      }
    }
    if (node.ifConditions) {
      for (let i = 1, l = node.ifConditions.length; i < l; i++) {
        markStaticRoots(node.ifConditions[i].block, isInFor)
      }
    }
  }
}

在 makeStaticRoots 方法中,其实比较特殊的就是判断子节点只有一个的情况,这种情况下,它的子节点不能为纯文本节点(这一点可能是作者做过什么性能测试什么的,所以注释中也写了这样要求获得的会比消耗的性能多)

generate

编译的最后一步,遍历 AST,将优化后的 AST 生成可执行的代码,例如生成 _c、_l、_v 之类的函数。
例如:

<div class="static-class" :class="bindClass">
	<span v-for="(item, index) in data" @click="clickMe"></span>
</div>

会 generate 成这样的可执行的函数:

with(this){
  return _c('div', {
	        staticClass: "static-class",
	        class: bindClass
	      },
	      _l((data), function(item, index) {
	        return _c('span', {
	          on: {
	            "click": clickMe
	          }
	        }
	      })
	    )
}
```
发布了140 篇原创文章 · 获赞 16 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_42049445/article/details/103758493