AST 的全称是 Abstract Syntax Tree,也就是所谓抽象语法树,用来表示代码的数据结构。
在Vue中可以理解为嵌套的、携带标签名、属性和父子关系的JS对象,以树来表现DOM结构
// vue中的AST有三种类型
declare type ASTElement = {
type: 1;
tag: string;
attrsList:Arraray<{name:string; value:string}>;
attrsMap:{[key: string]: string|null};
parent: ASTElement|void;
children: Array<ASTNode>;
// ....
}
declare type ASTExpression = {
type: 2;
expression: string;
text: string:
static?:boolean;
}
declare type ASTText = {
type: 3;
text: string;
static?: boolean;
}
parse 函数里定义了许多的正则表达式,通过对标签名开头、标签名结尾、属性字段、文本内容等等的递归匹配。把字符串类型的 template 转化成了树状结构的 AST
<div id="test">texttext</div>
经过parse函数解析为
ele1 = {
type: 1,
tag: "div",
attrsList: [{name: "id", value: "test"}],
attrsMap: {id: "test"},
parent: undefined,
children: [{
type: 3,
text: 'texttext'
}
],
plain: true,
attrs: [{name: "id", value: "'test'"}]
}
optimize 优化会对 parse 生成的 AST 进行静态内容(和数据没有关系,不需要每次都刷新的内容)的优化,标记静态节点的作用是为了在后面做 Vnode 的 diff 时起作用。optimize 的过程分为两步:
// 源码
function markStatic (node: ASTNode) {
// 标记 static 属性
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
}
}
}
}
isStatic 函数顾名思义是判断该节点是否 static 的函数,符合如下内容的节点就会被认为是 static 的节点
- 如果是表达式AST节点,直接返回 false
- 如果是文本AST节点,直接返回 true
- 如果元素是元素节点,阶段有 v-pre 指令 || (没有任何指令、数据绑定、事件绑定等 &&没有 v-if 和 v-for &&不是 slot 和 component &&是 HTML 保留标签 &&不是 template 标签的直接子元素并且没有包含在 for 循环中)则返回 true
几种内部方法
_c:对应的是 createElement 方法,顾名思义,它的含义是创建一个元素(Vnode)
_v:创建一个文本结点。
_s:把一个值转换为字符串。(eg: {{data}})
_m:渲染静态内容
// 例:
<template>
<div id="test">
{{val}}
<img src="http://xx.jpg">
</div>
</template>
// 最终会被转换成这样子的函数字符串
{render: "with(this){return _c('div',{attrs:{"id":"test"}},[[_v(_s(val))]),_v(" "),_m(0)])}"}