Vue3编译器系统详解
🎯 编译器概述
Vue3的编译器系统是框架的核心优化引擎,它将模板编译成高效的渲染函数,实现了编译时优化和运行时性能提升。
编译器架构图
┌─────────────────────────────────────┐
│ 模板层 (Template Layer) │
│ <template> | 字符串模板 | JSX │
├─────────────────────────────────────┤
│ 解析层 (Parse Layer) │
│ 词法分析 | 语法分析 | AST生成 │
├─────────────────────────────────────┤
│ 转换层 (Transform Layer) │
│ 静态提升 | 补丁标志 | 块级优化 │
├─────────────────────────────────────┤
│ 生成层 (Codegen Layer) │
│ 渲染函数 | 优化代码 | 源码映射 │
└─────────────────────────────────────┘
🔍 解析阶段 (Parse)
1. 词法分析 (Lexical Analysis)
// 词法分析器状态
export enum State {
Text = 1, // 文本状态
InterpolationOpen, // 插值开始
Interpolation, // 插值内容
InterpolationClose, // 插值结束
BeforeTagName, // 标签名前
InTagName, // 标签名中
InSelfClosingTag, // 自闭合标签
BeforeClosingTagName, // 结束标签名前
InClosingTagName, // 结束标签名中
AfterClosingTagName, // 结束标签名后
BeforeAttrName, // 属性名前
InAttrName, // 属性名中
InDirName, // 指令名中
InDirArg, // 指令参数中
InDirDynamicArg, // 动态指令参数中
InDirModifier, // 指令修饰符中
AfterAttrName, // 属性名后
BeforeAttrValue, // 属性值前
InAttrValueDq, // 双引号属性值
InAttrValueSq, // 单引号属性值
InAttrValueNq, // 无引号属性值
BeforeDeclaration, // 声明前
InDeclaration, // 声明中
BeforeComment, // 注释前
InComment, // 注释中
BeforeSpecialS, // 特殊标签前
BeforeSpecialT, // 特殊标签前
InRCDATA, // RCDATA中
InEntity, // 实体中
}
// 词法分析器
export function baseParse(
content: string,
options: ParserOptions = {},
): RootNode {
const context = createParserContext(content, options)
const start = getCursor(context)
return createRoot(
parseChildren(context, TextModes.DATA, []),
getSelection(context, start),
)
}
2. 语法分析 (Syntax Analysis)
// 解析子节点
function parseChildren(
context: ParserContext,
mode: TextModes,
ancestors: ElementNode[],
): TemplateChildNode[] {
const parent = last(ancestors)
const ns = parent ? parent.ns : Namespaces.HTML
const nodes: TemplateChildNode[] = []
while (!isEnd(context, mode, ancestors)) {
const s = context.source
let node: TemplateChildNode | TemplateChildNode[] | undefined = undefined
if (mode === TextModes.DATA || mode === TextModes.RCDATA) {
if (!context.inVPre && startsWith(s, context.options.delimiters[0])) {
// 解析插值
node = parseInterpolation(context, mode)
} else if (mode === TextModes.DATA && s[0] === '<') {
// 解析标签
if (s.length === 1) {
emitError(context, ErrorCodes.EOF_IN_TAG)
} else if (s[1] === '!') {
// 解析注释或DOCTYPE
if (startsWith(s, '<!--')) {
node = parseComment(context)
} else if (startsWith(s, '<!DOCTYPE')) {
node = parseBogusComment(context)
} else if (startsWith(s, '<![CDATA[')) {
if (ns !== Namespaces.HTML) {
node = parseCDATA(context, ancestors)
} else {
emitError(context, ErrorCodes.CDATA_IN_HTML_CONTENT)
node = parseBogusComment(context)
}
} else {
emitError(context, ErrorCodes.INCORRECTLY_OPENED_COMMENT)
node = parseBogusComment(context)
}
} else if (s[1] === '/') {
// 解析结束标签
if (/[a-z]/i.test(s[2])) {
parseTag(context, TagType.End, parent)
continue
} else {
emitError(context, ErrorCodes.INVALID_FIRST_CHARACTER_OF_TAG_NAME)
node = parseBogusComment(context)
}
} else if (/[a-z]/i.test(s[1])) {
// 解析开始标签
node = parseElement(context, ancestors)
} else if (s[1] === '?') {
emitError(
context,
ErrorCodes.UNEXPECTED_QUESTION_MARK_INSTEAD_OF_TAG_NAME,
)
node = parseBogusComment(context)
} else {
emitError(context, ErrorCodes.INVALID_FIRST_CHARACTER_OF_TAG_NAME)
}
}
}
if (!node) {
// 解析文本
node = parseText(context, mode)
}
if (isArray(node)) {
for (let i = 0; i < node.length; i++) {
pushNode(nodes, node[i])
}
} else {
pushNode(nodes, node)
}
}
return nodes
}
3. AST节点类型
export enum NodeTypes {
ROOT, // 根节点
ELEMENT, // 元素节点
TEXT, // 文本节点
COMMENT, // 注释节点
SIMPLE_EXPRESSION, // 简单表达式
INTERPOLATION, // 插值表达式
ATTRIBUTE, // 属性节点
DIRECTIVE, // 指令节点
COMPOUND_EXPRESSION, // 复合表达式
IF, // 条件节点
IF_BRANCH, // 条件分支
FOR, // 循环节点
TEXT_CALL, // 文本调用
VNODE_CALL, // VNode调用
JS_CALL_EXPRESSION, // JS调用表达式
JS_OBJECT_EXPRESSION, // JS对象表达式
JS_PROPERTY, // JS属性
JS_ARRAY_EXPRESSION, // JS数组表达式
JS_FUNCTION_EXPRESSION, // JS函数表达式
JS_CONDITIONAL_EXPRESSION, // JS条件表达式
JS_CACHE_EXPRESSION, // JS缓存表达式
JS_BLOCK_STATEMENT, // JS块语句
JS_TEMPLATE_LITERAL, // JS模板字面量
JS_IF_STATEMENT, // JS if语句
JS_ASSIGNMENT_EXPRESSION, // JS赋值表达式
JS_SEQUENCE_EXPRESSION, // JS序列表达式
JS_RETURN_STATEMENT, // JS返回语句
}
// AST节点接口
export interface Node {
type: NodeTypes
loc: SourceLocation
}
export interface ElementNode extends Node {
type: NodeTypes.ELEMENT
ns: Namespaces
tag: string
tagType: ElementTypes
isSelfClosing: boolean
props: Array<AttributeNode | DirectiveNode>
children: TemplateChildNode[]
}
export interface InterpolationNode extends Node {
type: NodeTypes.INTERPOLATION
content: ExpressionNode
}
export interface SimpleExpressionNode extends Node {
type: NodeTypes.SIMPLE_EXPRESSION
content: string
isStatic: boolean
constType: ConstantTypes
hoisted?: JSChildNode
isIdentifier?: boolean
}
🔄 转换阶段 (Transform)
1. 转换器架构
// 转换上下文
export interface TransformContext {
root: RootNode
helpers: Map<symbol, number>
helper<T extends keyof typeof import('./runtimeHelpers')>(name: T): string
helperString(name: string): string
replaceNode(node: TemplateChildNode): void
removeNode(node?: TemplateChildNode): void
onNodeRemoved(): void
addIdentifiers(exp: ExpressionNode | string): void
removeIdentifiers(exp: ExpressionNode | string): void
hoist(exp: string | JSChildNode | Array<string | JSChildNode>): string
cache<T extends Node>(exp: T, isVNode?: boolean): string | T
constantCache: Map<TemplateChildNode, string>
filters?: Set<string>
prefixIdentifiers?: boolean
preTransformNode(node: ElementNode): ElementNode | void
postTransformNode?: (node: ElementNode) => void | (() => void)
onError: (error: CompilerError) => void
}
// 节点转换器
export type NodeTransform = (
node: RootNode | TemplateChildNode,
context: TransformContext,
) => void | (() => void) | (() => void)[]
// 指令转换器
export type DirectiveTransform = (
dir: DirectiveNode,
node: ElementNode,
context: TransformContext,
augmentor?: (ret: DirectiveTransformResult) => DirectiveTransformResult,
) => DirectiveTransformResult
2. 静态提升 (Static Hoisting)
// 静态提升转换器
export const transformHoist: NodeTransform = (node, context) => {
if (
node.type === NodeTypes.ELEMENT &&
node.tagType === ElementTypes.ELEMENT
) {
const { constantCache } = context
// 检查是否为静态节点
const hoisted = getHoistedNode(node, context)
if (hoisted) {
// 提升静态节点
context.hoist(hoisted)
// 替换为静态引用
context.replaceNode(
createSimpleExpression(
context.helperString(HoistType.HOISTED),
false,
node.loc,
),
)
}
}
}
// 获取可提升的节点
function getHoistedNode(
node: ElementNode,
context: TransformContext,
): string | undefined {
const { constantCache } = context
// 检查是否已经缓存
if (constantCache.has(node)) {
return constantCache.get(node)
}
// 检查是否为静态节点
if (isStaticNode(node)) {
const hoisted = generateStaticNode(node, context)
constantCache.set(node, hoisted)
return hoisted
}
return undefined
}
// 检查静态节点
function isStaticNode(node: ElementNode): boolean {
// 检查属性是否静态
for (const prop of node.props) {
if (prop.type === NodeTypes.DIRECTIVE) {
return false
}
if (prop.type === NodeTypes.ATTRIBUTE) {
if (prop.value && !isStaticExp(prop.value)) {
return false
}
}
}
// 检查子节点是否静态
for (const child of node.children) {
if (!isStaticChild(child)) {
return false
}
}
return true
}
3. 补丁标志 (Patch Flags)
// 补丁标志转换器
export const transformElement: NodeTransform = (node, context) => {
if (node.type !== NodeTypes.ELEMENT) {
return
}
const { tag, props } = node
const isComponent = isComponentTag(tag)
// 处理组件
if (isComponent) {
return transformComponent(node, context)
}
// 处理元素
const vnodeTag = `"${tag}"`
const vnodeProps = buildProps(node, context)
const vnodeChildren = node.children.length
? buildChildren(node, context)
: undefined
// 生成补丁标志
const patchFlag = getPatchFlag(node, context)
// 生成动态属性
const dynamicProps = getDynamicProps(node, context)
// 创建VNode调用
context.replaceNode(
createVNodeCall(
context,
vnodeTag,
vnodeProps,
vnodeChildren,
patchFlag,
dynamicProps,
),
)
}
// 获取补丁标志
function getPatchFlag(node: ElementNode, context: TransformContext): number {
let patchFlag = 0
// 检查动态属性
for (const prop of node.props) {
if (prop.type === NodeTypes.DIRECTIVE) {
const dir = prop
if (dir.name === 'bind') {
patchFlag |= PatchFlags.PROPS
} else if (dir.name === 'on') {
patchFlag |= PatchFlags.HYDRATE_EVENTS
}
} else if (prop.type === NodeTypes.ATTRIBUTE) {
if (prop.name === 'class') {
patchFlag |= PatchFlags.CLASS
} else if (prop.name === 'style') {
patchFlag |= PatchFlags.STYLE
}
}
}
// 检查动态子节点
for (const child of node.children) {
if (child.type === NodeTypes.INTERPOLATION) {
patchFlag |= PatchFlags.TEXT
}
}
return patchFlag
}
4. 块级优化 (Block Optimization)
// 块级优化转换器
export const transformBlock: NodeTransform = (node, context) => {
if (node.type !== NodeTypes.ELEMENT) {
return
}
// 检查是否为块级节点
if (hasDynamicChildren(node)) {
// 标记为块级节点
context.replaceNode(createBlock(node, context))
}
}
// 检查是否有动态子节点
function hasDynamicChildren(node: ElementNode): boolean {
for (const child of node.children) {
if (child.type === NodeTypes.INTERPOLATION) {
return true
}
if (child.type === NodeTypes.ELEMENT) {
if (hasDynamicProps(child)) {
return true
}
if (hasDynamicChildren(child)) {
return true
}
}
}
return false
}
// 创建块级节点
function createBlock(node: ElementNode, context: TransformContext): VNodeCall {
const { tag, props, children } = node
return createVNodeCall(
context,
`"${tag}"`,
buildProps(node, context),
buildChildren(node, context),
getPatchFlag(node, context),
getDynamicProps(node, context),
true, // isBlock
)
}
🎨 代码生成 (Codegen)
1. 代码生成器
// 代码生成上下文
export interface CodegenContext {
source: string
code: string
line: number
column: number
offset: number
indentLevel: number
push(code: string, node?: Node): void
indent(): void
deindent(withoutNewLine?: boolean): void
newline(): void
}
// 代码生成结果
export interface CodegenResult {
code: string
preamble: string
ast: RootNode
map?: RawSourceMap
}
// 生成代码
export function generate(
ast: RootNode,
options: CodegenOptions = {},
): CodegenResult {
const context = createCodegenContext(ast, options)
const { mode, push, prefixIdentifiers } = context
const helpers = Array.from(ast.helpers)
const hasHelpers = helpers.length > 0
// 生成前导代码
if (mode === 'module') {
genModulePreamble(ast, context, genScopeId(ast, context))
} else {
genFunctionPreamble(ast, context)
}
// 生成渲染函数
if (!prefixIdentifiers && mode === 'module') {
push('function render(')
genFunctionArguments(ast, context)
push(') {\n')
} else {
push('function render(')
genFunctionArguments(ast, context)
push(') {\n')
}
// 生成函数体
genIndent(context)
push('return ')
if (ast.codegenNode) {
genNode(ast.codegenNode, context)
} else {
push('null')
}
// 结束函数
context.deindent()
push('\n}')
return {
code: context.code,
preamble: context.preamble,
ast,
map: context.map ? context.map.toJSON() : undefined,
}
}
2. 节点代码生成
// 生成节点代码
function genNode(node: CodegenNode, context: CodegenContext) {
if (isString(node)) {
context.push(node)
return
}
switch (node.type) {
case NodeTypes.ELEMENT:
case NodeTypes.IF:
case NodeTypes.FOR:
genNode(node.codegenNode!, context)
break
case NodeTypes.TEXT:
genText(node, context)
break
case NodeTypes.SIMPLE_EXPRESSION:
genExpression(node, context)
break
case NodeTypes.INTERPOLATION:
genInterpolation(node, context)
break
case NodeTypes.TEXT_CALL:
genTextCall(node, context)
break
case NodeTypes.COMPOUND_EXPRESSION:
genCompoundExpression(node, context)
break
case NodeTypes.COMMENT:
genComment(node, context)
break
case NodeTypes.VNODE_CALL:
genVNodeCall(node, context)
break
case NodeTypes.JS_CALL_EXPRESSION:
genCallExpression(node, context)
break
case NodeTypes.JS_OBJECT_EXPRESSION:
genObjectExpression(node, context)
break
case NodeTypes.JS_PROPERTY:
genProperty(node, context)
break
case NodeTypes.JS_ARRAY_EXPRESSION:
genArrayExpression(node, context)
break
case NodeTypes.JS_FUNCTION_EXPRESSION:
genFunctionExpression(node, context)
break
case NodeTypes.JS_CONDITIONAL_EXPRESSION:
genConditionalExpression(node, context)
break
case NodeTypes.JS_CACHE_EXPRESSION:
genCacheExpression(node, context)
break
case NodeTypes.JS_BLOCK_STATEMENT:
genBlockStatement(node, context)
break
case NodeTypes.JS_TEMPLATE_LITERAL:
genTemplateLiteral(node, context)
break
case NodeTypes.JS_IF_STATEMENT:
genIfStatement(node, context)
break
case NodeTypes.JS_ASSIGNMENT_EXPRESSION:
genAssignmentExpression(node, context)
break
case NodeTypes.JS_SEQUENCE_EXPRESSION:
genSequenceExpression(node, context)
break
case NodeTypes.JS_RETURN_STATEMENT:
genReturnStatement(node, context)
break
}
}
// 生成VNode调用
function genVNodeCall(node: VNodeCall, context: CodegenContext) {
const { push, helper, pure } = context
const {
tag,
props,
children,
patchFlag,
dynamicProps,
directives,
isBlock,
disableTracking,
isComponent,
} = node
if (directives) {
push(helper(WITH_DIRECTIVES) + '(')
}
if (isBlock) {
push(`(${helper(OPEN_BLOCK)}(${disableTracking ? 'true' : ''}), `)
}
if (pure) {
push(PURE_ANNOTATION)
}
if (isComponent) {
push(helper(RESOLVE_COMPONENT) + `(${tag})`)
} else {
push(helper(CREATE_ELEMENT_VNODE) + `(${tag}`)
}
if (props) {
push(', ')
genNode(props, context)
} else {
push(', null')
}
if (children) {
push(', ')
if (isArray(children)) {
genNodeList(children, context)
} else {
genNode(children, context)
}
} else if (patchFlag !== undefined) {
push(', null')
}
if (patchFlag !== undefined) {
push(', ' + String(patchFlag))
if (dynamicProps) {
push(', ' + JSON.stringify(dynamicProps))
}
}
push(')')
if (isBlock) {
push(')')
}
if (directives) {
push(', ')
genNode(directives, context)
push(')')
}
}
3. 优化代码生成
// 生成优化代码
function genOptimizedCode(ast: RootNode, context: CodegenContext) {
// 生成静态提升
if (ast.hoists.length) {
context.push('const ')
for (let i = 0; i < ast.hoists.length; i++) {
if (i > 0) context.push(', ')
context.push(`_hoisted_${i + 1}`)
}
context.push(' = ')
for (let i = 0; i < ast.hoists.length; i++) {
if (i > 0) context.push(', ')
genNode(ast.hoists[i], context)
}
context.push('\n')
}
// 生成缓存
if (ast.cached.length) {
context.push('const ')
for (let i = 0; i < ast.cached.length; i++) {
if (i > 0) context.push(', ')
context.push(`_cached_${i + 1}`)
}
context.push(' = ')
for (let i = 0; i < ast.cached.length; i++) {
if (i > 0) context.push(', ')
context.push(helper(CACHE) + `(${i + 1}, `)
genNode(ast.cached[i], context)
context.push(')')
}
context.push('\n')
}
}
🔧 编译器优化
1. 编译时优化
// 编译器选项
export interface CompilerOptions {
mode?: 'module' | 'function' // 编译模式
prefixIdentifiers?: boolean // 前缀标识符
hoistStatic?: boolean // 静态提升
cacheHandlers?: boolean // 缓存处理器
scopeId?: string // 作用域ID
slotted?: boolean // 插槽
ssr?: boolean // 服务端渲染
isNativeTag?: (tag: string) => boolean // 原生标签检查
isBuiltInComponent?: (tag: string) => symbol | void // 内置组件检查
delimiters?: [string, string] // 分隔符
comments?: boolean // 保留注释
whitespace?: 'preserve' | 'condense' // 空白处理
bindingMetadata?: BindingMetadata // 绑定元数据
}
// 编译优化
export function compile(
source: string | RootNode,
options: CompilerOptions = {},
): CodegenResult {
const ast = isString(source) ? baseParse(source, options) : source
// 应用转换
const [nodeTransforms, directiveTransforms] = getBaseTransformPreset(
options.prefixIdentifiers,
)
transform(ast, {
...options,
nodeTransforms: [...nodeTransforms, ...(options.nodeTransforms || [])],
directiveTransforms: {
...directiveTransforms,
...(options.directiveTransforms || {}),
},
})
// 生成代码
return generate(ast, {
...options,
mode: options.mode || 'function',
})
}
2. 运行时优化
// 运行时优化标志
export const enum RuntimeFlags {
OPEN_BLOCK = 1 << 0,
CREATE_ELEMENT_VNODE = 1 << 1,
CREATE_VNODE = 1 << 2,
CREATE_COMMENT = 1 << 3,
CREATE_TEXT = 1 << 4,
CREATE_STATIC = 1 << 5,
RESOLVE_COMPONENT = 1 << 6,
RESOLVE_DIRECTIVE = 1 << 7,
WITH_DIRECTIVES = 1 << 8,
RENDER_LIST = 1 << 9,
RENDER_SLOT = 1 << 10,
TO_STRING = 1 << 11,
TO_DISPLAY_STRING = 1 << 12,
INTERPOLATE = 1 << 13,
SET_BLOCK_TRACKING = 1 << 14,
PUSH_SCOPE_ID = 1 << 15,
POP_SCOPE_ID = 1 << 16,
WITH_CTX = 1 << 17,
UNREF = 1 << 18,
IS_REF = 1 << 19,
WITH_MEMO = 1 << 20,
IS_MEMO_SAME = 1 << 21,
}
// 运行时助手
export const helperNameMap: Record<symbol, string> = {
[OPEN_BLOCK]: 'openBlock',
[CREATE_ELEMENT_VNODE]: 'createElementVNode',
[CREATE_VNODE]: 'createVNode',
[CREATE_COMMENT]: 'createCommentVNode',
[CREATE_TEXT]: 'createTextVNode',
[CREATE_STATIC]: 'createStaticVNode',
[RESOLVE_COMPONENT]: 'resolveComponent',
[RESOLVE_DIRECTIVE]: 'resolveDirective',
[WITH_DIRECTIVES]: 'withDirectives',
[RENDER_LIST]: 'renderList',
[RENDER_SLOT]: 'renderSlot',
[TO_STRING]: 'toString',
[TO_DISPLAY_STRING]: 'toDisplayString',
[INTERPOLATE]: 'interpolate',
[SET_BLOCK_TRACKING]: 'setBlockTracking',
[PUSH_SCOPE_ID]: 'pushScopeId',
[POP_SCOPE_ID]: 'popScopeId',
[WITH_CTX]: 'withCtx',
[UNREF]: 'unref',
[IS_REF]: 'isRef',
[WITH_MEMO]: 'withMemo',
[IS_MEMO_SAME]: 'isMemoSame',
}
📊 性能优化
1. 编译时优化策略
// 静态提升优化
const hoisted = /*#__PURE__*/ createVNode('div', { class: 'static' })
// 补丁标志优化
const patchFlag = PatchFlags.CLASS | PatchFlags.STYLE
// 块级优化
const dynamicChildren = [dynamicVNode1, dynamicVNode2]
// 缓存优化
const cached = _cache(1, () => expensiveComputation())
2. 运行时优化策略
// 快速路径检查
if (patchFlag > 0) {
// 有补丁标志,进行优化更新
if (patchFlag & PatchFlags.CLASS) {
// 只更新类名
}
if (patchFlag & PatchFlags.STYLE) {
// 只更新样式
}
} else {
// 无补丁标志,全量更新
}
// 块级更新
if (dynamicChildren) {
// 只更新动态子节点
for (let i = 0; i < dynamicChildren.length; i++) {
patch(dynamicChildren[i], newDynamicChildren[i])
}
}
🎯 总结
Vue3的编译器系统展现了现代前端框架的设计精髓:
- 高效解析 - 词法分析 + 语法分析 + AST生成
- 智能转换 - 静态提升 + 补丁标志 + 块级优化
- 优化生成 - 渲染函数 + 优化代码 + 源码映射
- 编译时优化 - 多种优化策略组合
- 运行时优化 - 快速路径 + 块级更新
- 跨平台支持 - 模块化 + 可扩展
这套编译器系统不仅为Vue3提供了高效的编译能力,也为其他前端项目提供了优秀的设计参考。