如果没有 render 选项就把 template 选项通过 compileToFunctions 函数转成 render函数,
var { render } = compileToFunctions(template, {});
options.render = render;
compileToFunctions 函数是 createCompiler 函数返回值对象中的一个方法,
var { compileToFunctions } = createCompiler(baseOptions);
createCompiler 函数的内容是 createCompilerCreator 函数的返回值
var createCompiler = createCompilerCreator()
createCompilerCreator 函数定义如下
function createCompilerCreator(baseCompile) {
return function createCompiler(baseOptions) {
function compile(){ // 核心函数 }
return {
compile: compile,
compileToFunctions: createCompileToFunctionFn(compile)
}
}
}
以compileToFunctions 作为切入点,最终的归属地就在 createCompileToFunctionFn 函数返回值。 createCompileToFunctionFn 函数定义如下
function createCompileToFunctionFn(compile) {
return function compileToFunctions(template, options, vm){
// 真正的编译工作是依托于 compile 函数
var compiled = compile(template, options);
var res = {};
res.render = createFunction(compiled.render, fnGenErrors);
// ...
return res;
}
}
function createFunction(code, errors) {
// ...
return new Function(code);
}
真正的编译工作是依托于 compile 函数
function compile(template, options){
var finalOptions = Object.create(baseOptions);
var compiled = baseCompile(template, finalOptions);
return compiled;
}
baseOptions包含编译器在运作的时候所需的配置选项。(常用的有 modules 和 directives)
var baseOptions = {
expectHTML: true,
modules: modules$1,
directives: directives$1,
isPreTag: isPreTag,
isUnaryTag: isUnaryTag,
mustUseProp: mustUseProp,
canBeLeftOpenTag: canBeLeftOpenTag,
isReservedTag: isReservedTag,
getTagNamespace: getTagNamespace,
staticKeys: genStaticKeys(modules$1)
};
compiled 是 baseCompile 对模板的编译结果,baseCompile 定义
// 正式的进入到Vue编译器词法分析阶段
function baseCompile(template, options) {
// 用正则等方式解析 template 模板中的指令、class、style等数据,形成AST。
var ast = parse(template.trim(), options);
// 标记 static 静态节点
// 后面当 update 更新界面时,会有一个 patch 的过程, diff 算法会直接跳过静态节点,从而减少了比较的过程,优化了 patch 的性能。
if (options.optimize !== false) {
optimize(ast, options);
}
// 将 AST 转化成 render function 字符串的过程,
// 得到结果是 render 的字符串以及 staticRenderFns 字符串。
var code = generate(ast, options);
return {
ast: ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
}
流程整理
createCompilerCreator()(baseOptions).compileToFunctions(template, {}).render
createCompiler(baseOptions).compileToFunctions(template, {}).render
createCompileToFunctionFn(compile)(template, {}).render
compileToFunctions(template, {}).render
createFunction(compile(template, options).render, fnGenErrors)
new Function(compile(template, options).render)
new Function(baseCompile(template, finalOptions).render)
new Function(code.render)
new Function(generate(parse(template.trim(), options), options).render)
所以 template 转成 render 关键就是这几步
- parse - 用正则等方式解析 template 模板中的指令、class、style等数据,形成AST。
- optimize - 标记 static 静态节点。
- generate - 将 AST 转化成 render function 字符串的过程。
- new Function - 得到一个可以执行的渲染函数,该函数在被调用时将返回一个虚拟 DOM。
parse
preTransforms、transforms 和 postTransforms 在哪里处理?
export function parse (template, options) {
parseHTML(template, {
// ...
// 调用 start 函数就会初始化 ast 和处理 preTransforms
start (tag, attrs, unary, start, end) {
// 初始化 ast
let element: ASTElement = createASTElement(tag, attrs, currentParent);
for (let i = 0; i < preTransforms.length; i++) {
element = preTransforms[i](element, options) || element
}
}
// 调用 end 函数会处理 transforms 和 postTransforms
end (tag, start, end) {
const element = stack[stack.length - 1];
// closeElement 内部处理 transforms 和 postTransforms
closeElement(element)
}
})
}
# start 函数哪里调用?
export function parseHTML (html, options) {
while (html) {
// ...
// End tag:
const endTagMatch = html.match(endTag)
if (endTagMatch) {
// ...
// 这个函数内部会调用 end
parseEndTag(endTagMatch[1], curIndex, index)
continue
}
// Start tag:
if (startTagMatch) {
// 这个函数内部会调用 start
handleStartTag(startTagMatch);
// ...
continue;
}
}
}
// 这里会调用 start
function handleStartTag (match) {
// ...
if (options.start) {
options.start(tagName, attrs, unary, match.start, match.end)
}
}
// 这里会调用 end
function parseEndTag (tagName, start, end) {
// ...
if (options.end) {
options.end(stack[i].tag, start, end)
}
// ...
}
function closeElement (element) {
// ...
if (!inVPre && !element.processed) {
// processElement 内部会处理 transforms
element = processElement(element, options)
}
// ...
// 处理 postTransforms
for (let i = 0; i < postTransforms.length; i++) {
postTransforms[i](element, options)
}
}
export function processElement (element, options) {
// ...
processKey(element)
processRef(element)
processSlotContent(element)
processSlotOutlet(element)
processComponent(element)
// 处理 transforms
for (let i = 0; i < transforms.length; i++) {
element = transforms[i](element, options) || element
}
processAttrs(element)
return element
}