这一章我们开始讲模板解析编译:总结来说就是通过compile
函数把tamplate
解析成render Function
形式的字符串compiler/index.js
import {
parse } from './parser/index'
import {
optimize } from './optimizer'
import {
generate } from './codegen/index'
import {
createCompilerCreator } from './create-compiler'
// `createCompilerCreator` allows creating compilers that use alternative
// parser/optimizer/codegen, e.g the SSR optimizing compiler.
// Here we just export a default compiler using the default parts.
export const createCompiler = createCompilerCreator(function baseCompile (
template: string,
options: CompilerOptions
): CompiledResult {
const ast = parse(template.trim(), options)
if (options.optimize !== false) {
optimize(ast, options)
}
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
我们可以看出createCompiler
函数内部运行的是parse
、optimize
、generate
三个函数,而生成的是ast
,render
,staticRenderFns
三个对象
parse
export function parse (
template: string,
options: CompilerOptions
): ASTElement | void {
/** * 有自定义warn用自定义没有用基础: console.error(`[Vue compiler]: ${msg}`) */
warn = options.warn || baseWarn
// 检查标签是否需要保留空格
platformIsPreTag = options.isPreTag || no
// 检查属性是否应被绑定
platformMustUseProp = options.mustUseProp || no
// 检查标记的名称空间
platformGetTagNamespace = options.getTagNamespace || no
/** * 获取modules中的值 */
transforms = pluckModuleFunction(options.modules, 'transformNode')
preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')
postTransforms = pluckModuleFunction(options.modules, 'postTransformNode')
delimiters = options.delimiters
const stack = []
// 是否保留elements直接的空白
const preserveWhitespace = options.preserveWhitespace !== false
let root //return 出去的AST
let currentParent //当前父节点
let inVPre = false
let inPre = false
let warned = false
/** * 单次警告 */
function warnOnce (msg) {
if (!warned) {
warned = true
warn(msg)
}
}
function closeElement (element) {
// check pre state
if (element.pre) {
inVPre = false
}
if (platformIsPreTag(element.tag)) {
inPre = false
}
// apply post-transforms
for (let i = 0; i < postTransforms.length; i++) {
postTransforms[i](element, options)
}
}
parseHTML(template, {
warn,
expectHTML: options.expectHTML,
isUnaryTag: options.isUnaryTag,
canBeLeftOpenTag: options.canBeLeftOpenTag,
shouldDecodeNewlines: options.shouldDecodeNewlines,
shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref,
shouldKeepComment: options.comments,
start (tag, attrs, unary) {
// check namespace.
// inherit parent ns if there is one
/** * 检查命名空间。如果有父nanmespace,则继承父nanmespace */
const ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag)
// handle IE svg bug
/* istanbul ignore if */
// IE的另类bug
if (isIE && ns === 'svg') {
attrs = guardIESVGBug(attrs)
}
// 返回应对的AST
let element: ASTElement = createASTElement(tag, attrs, currentParent)
if (ns) {
element.ns = ns
}
/** * 不是服务段渲染的时候,template 应该只负责渲染UI部分 * 不应该包含syle, script 的标签 */
if (isForbiddenTag(element) && !isServerRendering()) {
element.forbidden = true
process.env.NODE_ENV !== 'production' && warn(
'Templates should only be responsible for mapping the state to the ' +
'UI. Avoid placing tags with side-effects in your templates, such as ' +
`<${
tag}>` + ', as they will not be parsed.'
)
}
// apply pre-transforms
// 预处理
for (let i = 0; i < preTransforms.length; i++) {
element = preTransforms[i](element, options) || element
}
if (!inVPre) {
processPre(element)
if (element.pre) {
inVPre = true
}
}
// 检测该标签是否需要保留空格
if (platformIsPreTag(element.tag)) {
inPre = true
}
if (inVPre) {
// 当不需要转译时
processRawAttrs(element)
} else if (!element.processed) {
// structural directives
// 给AST加上v-for响应属性
processFor(element)
// 给AST加上v-if v-else v-else-if相应属性
processIf(element)
// 判断是否含有v-once
processOnce(element)
// element-scope stuff
processElement(element, options)
}
function checkRootConstraints (el) {
if (process.env.NODE_ENV !== 'production') {
// 根标签不应该是slot和template
if (el.tag === 'slot' || el.tag === 'template') {
warnOnce(
`Cannot use <${
el.tag}> as component root element because it may ` +
'contain multiple nodes.'
)
}
// 根标签不应该含有v-for
if (el.attrsMap.hasOwnProperty('v-for')) {
warnOnce(
'Cannot use v-for on stateful component root element because ' +
'it renders multiple elements.'
)
}
}
}
// tree management
// 赋值给跟标签
if (!root) {
root = element
// 用于检查根标签
checkRootConstraints(root)
// 缓存中是否有值
} else if (!stack.length) {
// allow root elements with v-if, v-else-if and v-else
// 如果根元素有v-if, v-else-if and v-else 则打上响应记号
if (root.if && (element.elseif || element.else)) {
checkRootConstraints(element)
addIfCondition(root, {
exp: element.elseif,
block: element
})
} else if (process.env.NODE_ENV !== 'production') {
warnOnce(
`Component template should contain exactly one root element. ` +
`If you are using v-if on multiple elements, ` +
`use v-else-if to chain them instead.`
)
}
}
if (currentParent && !element.forbidden) {
if (element.elseif || element.else) {
processIfConditions(element, currentParent)
} else if (element.slotScope) {
// scoped slot
// 处理slot, scoped传值
currentParent.plain = false
const name = element.slotTarget || '"default"'
;(currentParent.scopedSlots || (currentParent.scopedSlots = {
}))[name] = element
} else {
currentParent