🅰️ Angular 编译器

深度解析 - 从模板到运行时代码

基于 angular/angular 源码分析
2026-03-07 | 技术深度解读

📑 目录

第一部分:基础架构

  • Angular 编译器简介
  • 核心架构
  • 编译流程
  • 编译器演进史
  • AOT vs JIT

第二部分:核心模块

  • 模板解析 (parseTemplate)
  • 表达式解析 (Parser)
  • HTML 解析 (HtmlParser)
  • 绑定解析 (BindingParser)

第三部分:代码生成

  • 组件编译
  • 指令编译
  • 模块编译
  • Pipe 编译

第四部分:设计模式

  • 访问者模式
  • 组合模式
  • 工厂模式

第五部分:性能与最佳实践

  • 性能优化
  • Tree Shaking
  • 调试技巧

🅰️ Angular 编译器简介

Angular 编译器是 Angular 框架的核心组件,负责将模板和装饰器元数据转换为高效的运行时代码。

核心职责:

  • 模板编译 - HTML 模板 → 渲染函数
  • 组件编译 - @Component 装饰器 → 组件定义
  • 指令编译 - @Directive 装饰器 → 指令定义
  • 模块编译 - @NgModule 装饰器 → 模块定义
  • 样式处理 - CSS → 视图封装样式

源码位置:packages/compiler/src/

🏗️ 核心架构

┌─────────────────────────────────────────────────────────────┐
│                    Angular Compiler                          │
├─────────────────────────────────────────────────────────────┤
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐       │
│  │   Template   │  │ Expression   │  │    HTML      │       │
│  │    Parser    │→ │   Parser     │← │   Parser     │       │
│  └──────────────┘  └──────────────┘  └──────────────┘       │
│         ↓                                    ↓               │
│  ┌──────────────────────────────────────────────────┐       │
│  │              R3 AST (Template AST)                │       │
│  └──────────────────────────────────────────────────┘       │
│         ↓                                                   │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐       │
│  │  Component   │  │  Directive   │  │    Module    │       │
│  │  Compiler    │  │  Compiler    │  │   Compiler   │       │
│  └──────────────┘  └──────────────┘  └──────────────┘       │
│         ↓                ↓                ↓                  │
│  ┌──────────────────────────────────────────────────┐       │
│  │              Output AST (JavaScript)              │       │
│  └──────────────────────────────────────────────────┘       │
└─────────────────────────────────────────────────────────────┘

⚙️ 编译流程

1. 解析阶段 (Parsing)
   HTML 模板 → HTML AST → R3 Template AST
   
2. 分析阶段 (Analysis)
   装饰器元数据 → 编译元数据
   
3. 编译阶段 (Compilation)
   编译元数据 → 中间表示 (IR)
   
4. 代码生成 (Code Generation)
   IR → JavaScript AST → 源代码

关键文件:

  • parseTemplate() - 模板解析入口
  • compileComponentFromMetadata() - 组件编译
  • emitTemplateFn() - 代码生成

📜 编译器演进史

版本 编译器 特点
Angular 2-5 JIT (默认) 运行时编译,启动慢
Angular 4+ AOT (可选) 编译时编译,启动快
Angular 9+ Ivy (默认) 新的 Render3 编译器
Angular 17+ Deferred Loading 延迟加载编译支持
Angular 18+ Control Flow 新控制流语法 @if @for

Ivy 编译器 是当前主要使用的编译器,代码位于 render3/ 目录

🔄 AOT vs JIT

AOT (Ahead-of-Time)

  • ✅ 编译时生成代码
  • ✅ 更快的启动速度
  • ✅ 更小的包体积
  • ✅ 更早发现错误
  • ❌ 构建时间较长

JIT (Just-in-Time)

  • ✅ 快速开发迭代
  • ✅ 动态编译
  • ❌ 启动较慢
  • ❌ 包体积较大
  • ❌ 运行时错误

生产环境推荐使用 AOT,开发环境可使用 JIT

📦 核心模块概览

模块 路径 职责
compiler.ts src/ 编译器入口点
template.ts render3/view/ 模板解析
compiler.ts render3/view/ 组件/指令编译
r3_module_compiler.ts render3/ 模块编译
r3_ast.ts render3/ R3 AST 节点定义
parser.ts expression_parser/ 表达式解析
html_parser.ts ml_parser/ HTML 解析

🔍 parseTemplate

模板解析入口函数,将 HTML 字符串转换为 R3 AST

export function parseTemplate(
  template: string,      // 模板内容
  templateUrl: string,   // 模板 URL
  options: ParseTemplateOptions = {},
): ParsedTemplate {
  // 1. 创建绑定解析器
  const bindingParser = makeBindingParser(selectorlessEnabled);
  
  // 2. HTML 解析
  const parseResult = htmlParser.parse(template, templateUrl, {
    tokenizeExpansionForms: true,
    tokenizeBlocks: options.enableBlockSyntax ?? true,
    tokenizeLet: options.enableLetSyntax ?? true,
  });
  
  // 3. 处理 i18n 元信息
  const i18nMetaResult = i18nMetaVisitor.visitAllWithErrors(rootNodes);
  
  // 4. 转换为 R3 AST
  const {nodes, errors, styleUrls, styles, ngContentSelectors} = 
    htmlAstToRender3Ast(rootNodes, bindingParser);
    
  return { preserveWhitespaces, errors, nodes, styleUrls, styles, ngContentSelectors };
}

📝 Parser 类

表达式解析器,处理模板中的插值表达式和绑定表达式

export class Parser {
  constructor(
    private readonly _lexer: Lexer,
    private readonly _supportsDirectPipeReferences = false,
  ) {}

  // 解析事件处理器
  parseAction(input: string, parseSourceSpan: ParseSourceSpan, 
              absoluteOffset: number): ASTWithSource
  
  // 解析属性绑定
  parseBinding(input: string, parseSourceSpan: ParseSourceSpan,
               absoluteOffset: number): ASTWithSource
  
  // 解析 Host 绑定
  parseSimpleBinding(input: string, parseSourceSpan: ParseSourceSpan,
                     absoluteOffset: number): ASTWithSource
  
  // 解析微语法模板绑定 (*ngFor 等)
  parseTemplateBindings(templateKey: string, templateValue: string,
    parseSourceSpan: ParseSourceSpan, absoluteKeyOffset: number,
    absoluteValueOffset: number): TemplateBindingParseResult
  
  // 解析插值表达式
  parseInterpolation(input: string, ...): ASTWithSource | null
}

🌐 HtmlParser 类

HTML 解析器,将模板字符串转换为 HTML AST

export class HtmlParser extends Parser {
  constructor() {
    super(getHtmlTagDefinition);
  }

  override parse(source: string, url: string, 
                 options?: TokenizeOptions): ParseTreeResult {
    return super.parse(source, url, options);
  }
}

支持特性:

  • 标准 HTML 标签解析
  • Angular 扩展语法 (*ngIf, {{}})
  • ICU 表达式 (国际化)
  • 控制流块 (@if, @for, @switch)
  • @let 语法

🔗 BindingParser

绑定解析器,处理模板中的各种绑定语法

export class BindingParser {
  constructor(
    private _exprParser: Parser,
    private _schemaRegistry: ElementSchemaRegistry,
    private _errors: ParseError[]
  ) {}

  // 创建 Host 属性绑定
  createBoundHostProperties(
    properties: {[key: string]: string},
    sourceSpan: ParseSourceSpan
  ): BoundElementProperty[]

  // 创建 Host 事件绑定
  createDirectiveHostEventAsts(
    listeners: {[key: string]: string},
    sourceSpan: ParseSourceSpan
  ): ParsedEvent[]

  // 创建元素属性绑定
  createBoundElementProperty(
    elementName: string, attribute: AttrAst,
    boundAttrs: BoundElementPropertyAst[]
  ): BoundElementProperty
}

// 工厂函数
export function makeBindingParser(selectorlessEnabled = false): BindingParser {
  return new BindingParser(
    new Parser(new Lexer(), selectorlessEnabled), 
    elementRegistry, 
    []
  );
}

🌳 R3 AST 节点

Render3 模板 AST 定义,表示解析后的模板结构

// 节点接口
export interface Node {
  sourceSpan: ParseSourceSpan;
  visit<Result>(visitor: Visitor<Result>): Result;
}

// 核心节点类型
export class Element implements Node { ... }      // HTML 元素
export class Template implements Node { ... }     // ng-template
export class Text implements Node { ... }         // 文本节点
export class BoundText implements Node { ... }    // 绑定文本 {{expr}}
export class BoundAttribute implements Node { ... } // [prop]="expr"
export class BoundEvent implements Node { ... }   // (event)="handler"
export class Content implements Node { ... }      // ng-content
export class Reference implements Node { ... }    // #ref
export class Variable implements Node { ... }     // let-var
export class Component implements Node { ... }    // 组件节点
export class Directive implements Node { ... }    // 指令节点

⚙️ compileComponentFromMetadata

组件编译核心函数,将组件元数据编译为运行时代码

export function compileComponentFromMetadata(
  meta: R3ComponentMetadata<R3TemplateDependency>,
  constantPool: ConstantPool,
  bindingParser: BindingParser,
): R3CompiledExpression {
  // 1. 基础指令字段
  const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
  
  // 2. 添加 Features
  addFeatures(definitionMap, meta);
  
  // 3. 编译模板
  const tpl = ingestComponent(meta.name, meta.template.nodes, ...);
  transform(tpl, CompilationJobKind.Tmpl);
  const templateFn = emitTemplateFn(tpl, constantPool);
  
  // 4. 设置组件定义
  definitionMap.set('decls', o.literal(tpl.root.decls));
  definitionMap.set('vars', o.literal(tpl.root.vars));
  definitionMap.set('template', templateFn);
  
  // 5. 生成代码
  const expression = o.importExpr(R3.defineComponent)
    .callFn([definitionMap.toLiteralMap()]);
    
  return {expression, type, statements: []};
}

📌 compileDirectiveFromMetadata

指令编译函数,将指令元数据编译为运行时代码

export function compileDirectiveFromMetadata(
  meta: R3DirectiveMetadata,
  constantPool: ConstantPool,
  bindingParser: BindingParser,
): R3CompiledExpression {
  // 1. 基础字段
  const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
  
  // 2. 添加 Features
  addFeatures(definitionMap, meta);
  
  // 3. 生成指令定义代码
  const expression = o.importExpr(R3.defineDirective)
    .callFn([definitionMap.toLiteralMap()], undefined, true);
    
  // 4. 创建类型声明
  const type = createDirectiveType(meta);

  return {expression, type, statements: []};
}

baseDirectiveFields 生成:

  • type - 指令类型
  • selectors - 选择器
  • inputs - 输入属性
  • outputs - 输出事件
  • hostBindings - Host 绑定函数

📦 compileNgModule

模块编译函数,生成 NgModule 定义

export function compileNgModule(meta: R3NgModuleMetadata): R3CompiledExpression {
  const definitionMap = new DefinitionMap<R3NgModuleDefMap>();
  
  // 1. 设置模块类型
  definitionMap.set('type', meta.type.value);
  
  // 2. 设置 bootstrap 组件
  if (meta.bootstrap.length > 0) {
    definitionMap.set('bootstrap', refsToArray(meta.bootstrap, ...));
  }
  
  // 3. 设置作用域 (declarations, imports, exports)
  if (meta.selectorScopeMode === R3SelectorScopeMode.Inline) {
    definitionMap.set('declarations', refsToArray(meta.declarations, ...));
    definitionMap.set('imports', refsToArray(meta.imports, ...));
    definitionMap.set('exports', refsToArray(meta.exports, ...));
  }
  
  // 4. 生成模块定义
  const expression = o.importExpr(R3.defineNgModule)
    .callFn([definitionMap.toLiteralMap()]);
    
  return {expression, type, statements};
}

🔧 compilePipeFromMetadata

Pipe 编译函数,生成 Pipe 定义

export function compilePipeFromMetadata(
  meta: R3PipeMetadata
): R3CompiledExpression {
  const definitionMap = new DefinitionMap();
  
  definitionMap.set('type', meta.type.value);
  definitionMap.set('name', o.literal(meta.name));  // pipe 名称
  definitionMap.set('pure', o.literal(meta.isPure)); // 是否纯 pipe
  
  const expression = o.importExpr(R3.definePipe)
    .callFn([definitionMap.toLiteralMap()]);
    
  return {expression, type, statements: []};
}

纯 Pipe vs 非纯 Pipe:

  • 纯 Pipe - 输入不变则不重新计算
  • 非纯 Pipe - 每次变更检测都执行

🏠 parseHostBindings

解析 Host 绑定,处理 @HostBinding 和 @HostListener

export interface ParsedHostBindings {
  attributes: {[key: string]: o.Expression};  // 静态属性
  listeners: {[key: string]: string};         // 事件监听器
  properties: {[key: string]: string};        // 属性绑定
  specialAttributes: {styleAttr?: string; classAttr?: string};
}

export function parseHostBindings(host: {
  [key: string]: string | o.Expression;
}): ParsedHostBindings {
  const attributes: {[key: string]: o.Expression} = {};
  const listeners: {[key: string]: string} = {};
  const properties: {[key: string]: string} = {};
  
  for (const key of Object.keys(host)) {
    const matches = key.match(HOST_REG_EXP);  // [prop] 或 (event)
    
    if (matches === null) {
      // 静态属性: class, style, 或普通属性
      attributes[key] = typeof value === 'string' ? o.literal(value) : value;
    } else if (matches[HostBindingGroup.Binding] != null) {
      // 属性绑定: [prop]
      properties[matches[HostBindingGroup.Binding]] = value;
    } else if (matches[HostBindingGroup.Event] != null) {
      // 事件监听: (event)
      listeners[matches[HostBindingGroup.Event]] = value;
    }
  }
  return {attributes, listeners, properties, specialAttributes};
}

🔄 ingestComponent

模板摄取函数,将 R3 AST 转换为中间表示 (IR)

// 从 template/pipeline/src/ingest.ts
export function ingestComponent(
  name: string,
  nodes: t.Node[],
  constantPool: ConstantPool,
  compilationMode: TemplateCompilationMode,
  relativeContextFilePath: string,
  i18nUseExternalIds: boolean,
  defer: DeferBlockEmitMode,
  allDeferrableDepsFn: o.ReadVarExpr | null,
  relativeTemplatePath: string | null,
  enableTemplateSourceLocations: boolean,
): Compilation {
  // 1. 创建编译作业
  const job = new CompilationJob(name, compilationMode, ...);
  
  // 2. 遍历 AST 节点,生成 IR 指令
  for (const node of nodes) {
    node.visit(new TemplateIngestor(job, ...));
  }
  
  // 3. 返回编译结果
  return job;
}

IR 转换阶段:

  1. ingestComponent - 摄取为 IR
  2. transform - 优化转换
  3. emitTemplateFn - 生成代码

📤 emitTemplateFn

代码生成函数,将 IR 转换为 JavaScript 代码

// 从 template/pipeline/src/emit.ts
export function emitTemplateFn(
  job: Compilation,
  constantPool: ConstantPool
): o.Expression {
  // 1. 创建模板函数
  const params = [
    new o.FnParam('rf', o.NONE_TYPE),  // RenderFlag
    new o.FnParam('ctx', o.NONE_TYPE), // 组件上下文
  ];
  
  // 2. 生成指令序列
  const statements: o.Statement[] = [];
  
  // 3. 遍历 IR 节点,生成代码
  for (const ir of job.root.instructions) {
    statements.push(emitInstruction(ir, constantPool));
  }
  
  // 4. 返回箭头函数
  return o.arrowFn(params, statements);
}

生成的代码示例:

function MyComponent_Template(rf, ctx) {
  if (rf & 1) {  // 创建模式
    i0.ɵɵelementStart(0, "div");
    i0.ɵɵtext(1);
    i0.ɵɵelementEnd();
  }
  if (rf & 2) {  // 更新模式
    i0.ɵɵadvance(1);
    i0.ɵɵtextInterpolate(ctx.title);
  }
}

🌳 AST 节点体系

           Node (interface)
              │
    ┌─────────┼─────────┬─────────┐
    │         │         │         │
  Text    BoundText  Element  Template
    │         │         │         │
    │         │    ┌────┴────┐    │
    │         │    │         │    │
    │         │  Content Component Directive
    │         │
    │   ┌─────┴─────┐
    │   │           │
TextAttribute BoundAttribute BoundEvent
    │
    ├─────────┬─────────┐
    │         │         │
Reference Variable  Icu
    │
    ├─────────┴─────────┐
    │                   │
DeferredBlock    ControlFlowBlock
    │                   │
├───┼───┐       ├───┼───┤
Placeholder Loading Error  If For Switch

🔹 Element 节点

表示 HTML 元素的 AST 节点

export class Element implements Node {
  constructor(
    public name: string,           // 标签名
    public attributes: TextAttribute[],   // 静态属性
    public inputs: BoundAttribute[],      // 输入绑定
    public outputs: BoundEvent[],         // 输出绑定
    public directives: Directive[],       // 应用的指令
    public children: Node[],              // 子节点
    public references: Reference[],       // 模板引用
    public isSelfClosing: boolean,        // 自闭合标签
    public sourceSpan: ParseSourceSpan,
    public startSourceSpan: ParseSourceSpan,
    public endSourceSpan: ParseSourceSpan | null,
    readonly isVoid: boolean,             // void 元素
    public i18n?: I18nMeta,
  ) {}
  
  visit<Result>(visitor: Visitor<Result>): Result {
    return visitor.visitElement(this);
  }
}

📄 Template 节点

表示 ng-template 或结构指令的 AST 节点

export class Template implements Node {
  constructor(
    public tagName: string | null,        // 容器标签名
    public attributes: TextAttribute[],
    public inputs: BoundAttribute[],
    public outputs: BoundEvent[],
    public directives: Directive[],
    public templateAttrs: (BoundAttribute | TextAttribute)[], // 模板属性
    public children: Node[],              // 模板内容
    public references: Reference[],
    public variables: Variable[],         // 模板变量 (let-*)
    public isSelfClosing: boolean,
    public sourceSpan: ParseSourceSpan,
    public startSourceSpan: ParseSourceSpan,
    public endSourceSpan: ParseSourceSpan | null,
    public i18n?: I18nMeta,
  ) {}
  
  visit<Result>(visitor: Visitor<Result>): Result {
    return visitor.visitTemplate(this);
  }
}

🔗 BoundAttribute 节点

表示属性绑定的 AST 节点

export class BoundAttribute implements Node {
  constructor(
    public name: string,              // 属性名
    public type: BindingType,         // 绑定类型
    public securityContext: SecurityContext,
    public value: AST,                // 绑定表达式 AST
    public unit: string | null,       // 单位 (如 'px')
    public sourceSpan: ParseSourceSpan,
    readonly keySpan: ParseSourceSpan,
    public valueSpan: ParseSourceSpan | undefined,
    public i18n: I18nMeta | undefined,
  ) {}

  static fromBoundElementProperty(
    prop: BoundElementProperty, 
    i18n?: I18nMeta
  ): BoundAttribute
  
  visit<Result>(visitor: Visitor<Result>): Result {
    return visitor.visitBoundAttribute(this);
  }
}

绑定类型 (BindingType):

  • Property - [property]
  • Attribute - [attr.name]
  • Class - [class.name]
  • Style - [style.name]
  • Animation - [@animation]

⚡ BoundEvent 节点

表示事件绑定的 AST 节点

export class BoundEvent implements Node {
  constructor(
    public name: string,              // 事件名
    public type: ParsedEventType,     // 事件类型
    public handler: AST,              // 处理器表达式
    public target: string | null,     // 目标 (window, document 等)
    public phase: string | null,      // 事件阶段 (capture 等)
    public sourceSpan: ParseSourceSpan,
    public handlerSpan: ParseSourceSpan,
    readonly keySpan: ParseSourceSpan,
  ) {}

  static fromParsedEvent(event: ParsedEvent): BoundEvent
  
  visit<Result>(visitor: Visitor<Result>): Result {
    return visitor.visitBoundEvent(this);
  }
}

事件类型:

  • Regular - (click)="handler()"
  • LegacyAnimation - (@anim.done)

⏳ Deferred 块语法

Angular 17+ 新增的延迟加载语法

export class DeferredBlock extends BlockNode implements Node {
  readonly triggers: Readonly<DeferredBlockTriggers>;
  readonly prefetchTriggers: Readonly<DeferredBlockTriggers>;
  readonly hydrateTriggers: Readonly<DeferredBlockTriggers>;
  
  constructor(
    public children: Node[],
    triggers: DeferredBlockTriggers,
    prefetchTriggers: DeferredBlockTriggers,
    hydrateTriggers: DeferredBlockTriggers,
    public placeholder: DeferredBlockPlaceholder | null,
    public loading: DeferredBlockLoading | null,
    public error: DeferredBlockError | null,
    ...
  ) {}
}

// 触发器类型
export interface DeferredBlockTriggers {
  when?: BoundDeferredTrigger;     // @defer (when condition)
  idle?: IdleDeferredTrigger;      // @defer (on idle)
  immediate?: ImmediateDeferredTrigger;
  hover?: HoverDeferredTrigger;
  timer?: TimerDeferredTrigger;    // @defer (on timer(500ms))
  interaction?: InteractionDeferredTrigger;
  viewport?: ViewportDeferredTrigger;
  never?: NeverDeferredTrigger;
}

🔀 Control Flow 块

Angular 18+ 新的控制流语法

// @if 块
export class IfBlock extends BlockNode implements Node {
  constructor(
    public branches: IfBlockBranch[],
    ...
  ) {}
}

export class IfBlockBranch extends BlockNode {
  constructor(
    public expression: AST | null,     // 条件表达式
    public children: Node[],
    public expressionAlias: Variable | null,  // as alias
    ...
  ) {}
}

// @for 块
export class ForLoopBlock extends BlockNode implements Node {
  constructor(
    public item: Variable,             // let item
    public expression: ASTWithSource,  // of items
    public trackBy: ASTWithSource,     // track by
    public contextVariables: Variable[], // $index, $odd 等
    public children: Node[],
    public empty: ForLoopBlockEmpty | null, // @empty
    ...
  ) {}
}

// @switch 块
export class SwitchBlock extends BlockNode implements Node {
  constructor(
    public expression: AST,
    public groups: SwitchBlockCaseGroup[],
    public exhaustiveCheck: SwitchExhaustiveCheck | null,
    ...
  ) {}
}

🏊 ConstantPool

常量池,用于优化重复表达式的生成

export class ConstantPool {
  // 存储常量定义语句
  statements: o.Statement[] = [];
  
  // 获取或创建常量字面量
  getConstLiteral(
    literal: o.LiteralExpr, 
    forceShared: boolean
  ): o.Expression {
    // 检查是否已存在相同的常量
    // 如果存在则返回引用,否则创建新常量
  }
  
  // 插入常量定义
  insertWhenSharing(
    literal: o.Expression
  ): o.Expression
}

优化效果:

  • 避免重复生成相同的常量
  • 减少输出代码体积
  • 提高运行时性能

📝 output_ast 模块

JavaScript 代码生成的抽象语法树

// 表达式类型
export class LiteralExpr { ... }        // 字面量
export class LiteralArrayExpr { ... }   // 数组
export class LiteralMapExpr { ... }     // 对象
export class ExternalExpr { ... }       // 外部引用
export class InvokeFunctionExpr { ... } // 函数调用
export class ReadPropExpr { ... }       // 属性访问
export class ArrowFunctionExpr { ... }  // 箭头函数
export class DynamicImportExpr { ... }  // 动态 import

// 语句类型
export class DeclareVarStmt { ... }     // 变量声明
export class DeclareFunctionStmt { ... }// 函数声明
export class IfStmt { ... }             // if 语句
export class ReturnStatement { ... }    // return 语句
export class ExpressionStatement { ... }// 表达式语句

// 工厂函数
export function literal(value: any): LiteralExpr
export function literalArr(values: Expression[]): LiteralArrayExpr
export function literalMap(entries: {...}[]): LiteralMapExpr
export function importExpr(id: Identifiers): ExternalExpr

🗺️ DefinitionMap

用于构建组件/指令定义对象

export class DefinitionMap<T = {[key: string]: o.Expression}> {
  values: T;
  
  set<K extends keyof T>(key: K, value: T[K]): void {
    this.values[key] = value;
  }
  
  toLiteralMap(): o.LiteralMapExpr {
    const entries = Object.keys(this.values).map(key => ({
      key,
      value: this.values[key],
      quoted: false,
    }));
    return o.literalMap(entries);
  }
}

// 使用示例
const definitionMap = new DefinitionMap();
definitionMap.set('type', meta.type.value);
definitionMap.set('selectors', asLiteral(selectors));
definitionMap.set('inputs', conditionallyCreateDirectiveBindingLiteral(meta.inputs));

const expression = o.importExpr(R3.defineComponent)
  .callFn([definitionMap.toLiteralMap()]);

🎨 ShadowCss 样式封装

实现视图封装的样式作用域隔离

const COMPONENT_VARIABLE = '%COMP%';
const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;

function compileStyles(
  styles: string[], 
  selector: string, 
  hostSelector: string
): string[] {
  const shadowCss = new ShadowCss();
  return styles.map(style => {
    return shadowCss.shimCssText(style, selector, hostSelector);
  });
}

export function encapsulateStyle(
  style: string, 
  componentIdentifier?: string
): string {
  const shadowCss = new ShadowCss();
  const selector = componentIdentifier
    ? CONTENT_ATTR.replace(COMPONENT_VARIABLE, componentIdentifier)
    : CONTENT_ATTR;
  return shadowCss.shimCssText(style, selector, hostSelector);
}

封装效果: .title.title[_ngcontent-abc]

🎭 设计模式 - 访问者模式

遍历和处理 AST 节点的核心模式

export interface Visitor<Result = any> {
  visit?(node: Node): Result;  // 通用访问
  visitElement(element: Element): Result;
  visitTemplate(template: Template): Result;
  visitContent(content: Content): Result;
  visitText(text: Text): Result;
  visitBoundText(text: BoundText): Result;
  visitBoundAttribute(attribute: BoundAttribute): Result;
  visitBoundEvent(attribute: BoundEvent): Result;
  // ... 更多 visit 方法
}

// 递归访问者基类
export class RecursiveVisitor implements Visitor<void> {
  visitElement(element: Element): void {
    visitAll(this, element.attributes);
    visitAll(this, element.inputs);
    visitAll(this, element.outputs);
    visitAll(this, element.children);
  }
  // ... 其他方法
}

// 遍历所有节点
export function visitAll<Result>(
  visitor: Visitor<Result>, 
  nodes: Node[]
): Result[]

🧩 设计模式 - 组合模式

AST 节点的树形结构设计

// 所有节点实现统一接口
export interface Node {
  sourceSpan: ParseSourceSpan;
  visit<Result>(visitor: Visitor<Result>): Result;
}

// Element 节点包含子节点
export class Element implements Node {
  constructor(
    public name: string,
    public attributes: TextAttribute[],  // 子节点
    public inputs: BoundAttribute[],     // 子节点
    public outputs: BoundEvent[],        // 子节点
    public children: Node[],             // 递归包含更多 Node
    ...
  ) {}
}

// Template 节点也包含子节点
export class Template implements Node {
  constructor(
    public children: Node[],  // 递归结构
    public variables: Variable[],
    ...
  ) {}
}

🏭 设计模式 - 工厂模式

创建复杂对象的工厂函数

// BindingParser 工厂
export function makeBindingParser(
  selectorlessEnabled = false
): BindingParser {
  return new BindingParser(
    new Parser(new Lexer(), selectorlessEnabled),
    new DomElementSchemaRegistry(),
    []
  );
}

// BoundAttribute 工厂
export class BoundAttribute {
  static fromBoundElementProperty(
    prop: BoundElementProperty, 
    i18n?: I18nMeta
  ): BoundAttribute {
    return new BoundAttribute(
      prop.name, prop.type, prop.securityContext,
      prop.value, prop.unit, prop.sourceSpan,
      prop.keySpan, prop.valueSpan, i18n
    );
  }
}

// BoundEvent 工厂
export class BoundEvent {
  static fromParsedEvent(event: ParsedEvent): BoundEvent {
    const target = event.type === ParsedEventType.Regular 
      ? event.targetOrPhase : null;
    return new BoundEvent(event.name, event.type, ...);
  }
}

📊 模板解析流程图

┌─────────────────────────────────────────────────────────────┐
│                    模板字符串                                │
│   "<div *ngIf='show' [class]='cls'>{{title}}</div>"         │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                  HtmlParser.parse()                          │
│   解析 HTML 标签、属性、文本                                  │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                  HTML AST (ml_parser/ast)                    │
│   Element, Attribute, Text 节点                              │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│               htmlAstToRender3Ast()                          │
│   解析绑定、指令、控制流                                       │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                  R3 Template AST                             │
│   Element, BoundAttribute, BoundEvent, Template 等           │
└─────────────────────────────────────────────────────────────┘

📊 组件编译流程图

┌─────────────────────────────────────────────────────────────┐
│              @Component({...}) 元数据                        │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│           compileComponentFromMetadata()                     │
│   1. baseDirectiveFields() - 基础字段                        │
│   2. addFeatures() - 添加 Features                           │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                 ingestComponent()                            │
│   将模板 AST 转换为 IR (中间表示)                             │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                   transform()                                │
│   优化和转换 IR                                              │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                emitTemplateFn()                              │
│   生成模板函数代码                                           │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│              ɵɵdefineComponent({...})                        │
│   运行时组件定义                                             │
└─────────────────────────────────────────────────────────────┘

📊 模块编译流程图

┌─────────────────────────────────────────────────────────────┐
│              @NgModule({...}) 元数据                         │
│   declarations, imports, exports, bootstrap                  │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│               compileNgModule()                              │
│   根据 selectorScopeMode 选择编译策略                         │
└─────────────────────────────────────────────────────────────┘
                            ↓
        ┌───────────────────┼───────────────────┐
        ↓                   ↓                   ↓
┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│    Inline    │  │  SideEffect  │  │    Omit      │
│ 内联到定义    │  │ 侧边函数     │  │  不生成      │
└──────────────┘  └──────────────┘  └──────────────┘
        ↓                   ↓
        └───────────────────┼───────────────────┐
                            ↓
┌─────────────────────────────────────────────────────────────┐
│              ɵɵdefineNgModule({...})                         │
│   运行时模块定义                                             │
└─────────────────────────────────────────────────────────────┘

🔄 IR 转换流程

中间表示的优化和转换管道

// template/pipeline/src/compilation.ts
export const enum CompilationJobKind {
  Tmpl,  // 模板编译
  Host,  // Host 绑定编译
}

// 摄取阶段
const tpl = ingestComponent(
  meta.name,
  meta.template.nodes,
  constantPool,
  compilationMode,
  ...
);

// 转换阶段
transform(tpl, CompilationJobKind.Tmpl);

// 发射阶段
const templateFn = emitTemplateFn(tpl, constantPool);

转换优化:

  • 常量折叠
  • 死代码消除
  • 指令合并
  • 绑定优化

⚡ 性能优化策略

编译时优化

  • 常量池复用
  • IR 优化转换
  • 增量编译
  • 并行编译

运行时优化

  • 单次遍历指令
  • 位标记 (rf & 1)
  • 静态属性缓存
  • 延迟加载
// RenderFlag 位标记
const enum RenderFlags {
  Create = 1,     // 创建模式
  Update = 2,     // 更新模式
}

if (rf & RenderFlags.Create) {
  // 创建 DOM 结构
}
if (rf & RenderFlags.Update) {
  // 更新绑定值
}

🌳 Tree Shaking 支持

编译器生成的代码支持 Tree Shaking

// R3SelectorScopeMode 控制声明方式
export enum R3SelectorScopeMode {
  Inline,     // 内联 - 不利于 Tree Shaking
  SideEffect, // 侧边函数 - 支持 Tree Shaking
  Omit,       // 省略 - 完全不生成
}

// 侧边函数示例 (JIT 模式)
(function() {
  ngJitMode && ɵɵsetNgModuleScope(MyModule, {
    declarations: [MyComponent],
    imports: [CommonModule],
  });
})();

// 生产环境 ngJitMode = false,整个函数被删除

Tree Shaking 原理:未使用的声明代码被侧边函数包裹,在 AOT 编译时可以被移除

🔄 增量编译

只重新编译修改过的文件

// Angular CLI 增量编译策略
1. 文件变更检测
   - 监听文件系统变化
   - 计算文件哈希

2. 依赖图分析
   - 模块依赖
   - 组件依赖
   - 模板依赖

3. 影响范围计算
   - 修改组件 → 重新编译组件
   - 修改服务 → 检查注入者
   - 修改模板 → 重新编译模板

4. 增量编译
   - 只编译受影响的文件
   - 复用之前的编译结果

Angular CLI 使用 webpack 或 esbuild 实现增量编译

✅ 最佳实践 - 模板

推荐做法 ✅

  • 使用 OnPush 变更检测
  • 使用 trackBy 优化 @for
  • 避免复杂表达式
  • 使用纯 Pipe
  • 延迟加载大组件

避免做法 ❌

  • 深度嵌套模板
  • 频繁的 DOM 操作
  • 大型的内联模板
  • 过度使用结构指令
  • 复杂的计算属性
// ✅ 使用 trackBy
@for (item of items; track item.id) {
  <div>{{item.name}}</div>
}

// ✅ 使用 @defer 延迟加载
@defer (on viewport) {
  <heavy-component />
}

✅ 最佳实践 - 组件

// ✅ 使用 standalone 组件
@Component({
  selector: 'app-hero',
  standalone: true,
  imports: [CommonModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `...`
})
export class HeroComponent {}

// ✅ 使用 Signal 输入
export class HeroComponent {
  hero = input.required<Hero>();
  
  // 自动派生
  heroName = computed(() => this.hero().name);
}

// ✅ 使用 Host 绑定
@HostBinding('class.active')
isActive = false;

@HostListener('click')
onClick() {
  this.isActive = !this.isActive;
}

⚠️ 反模式警告

反模式 问题 解决方案
默认变更检测 性能差 使用 OnPush
非纯 Pipe 频繁执行 使用纯 Pipe 或 memoize
大模板文件 编译慢 拆分为小组件
循环中的复杂计算 性能差 预计算或 memoize
深度对象绑定 变更检测慢 扁平化数据

🔧 调试技巧

// 1. 查看编译后的代码
ng build --source-map

// 2. 启用编译器调试模式
import { enableDebugTools } from '@angular/platform-browser';
enableDebugTools();

// 3. 查看 AOT 编译产物
ngc --help
ngc -p tsconfig.aot.json

// 4. 分析编译性能
ng build --stats-json
npx webpack-bundle-analyzer dist/stats.json

// 5. 模板类型检查
// tsconfig.json
{
  "angularCompilerOptions": {
    "strictTemplates": true,
    "fullTemplateTypeCheck": true
  }
}

⚙️ 编译器配置

// tsconfig.json
{
  "angularCompilerOptions": {
    // 严格模式
    "strictTemplates": true,
    "strictInputTypes": true,
    "strictNullInputTypes": true,
    
    // 编译模式
    "fullTemplateTypeCheck": true,
    "enableI18nLegacyMessageIdFormat": false,
    
    // 性能选项
    "compilationMode": "full",  // 'partial' | 'full'
    "preserveWhitespaces": false,
    
    // 高级选项
    "annotationsAs": "static fields",
    "annotateForClosureCompiler": true,
    "skipTemplateCodegen": false,
    "enableResourceInlining": false
  }
}

📚 扩展阅读

社区资源

  • Angular Compiler Internals (深度解析)
  • Ivy Engine Design (设计文档)

📁 源码结构

packages/compiler/src/
├── compiler.ts              # 入口文件
├── core.ts                  # 核心类型
├── config.ts                # 配置选项
├── constant_pool.ts         # 常量池
│
├── render3/                 # Ivy 编译器
│   ├── r3_ast.ts           # R3 AST 节点
│   ├── r3_module_compiler.ts
│   ├── r3_pipe_compiler.ts
│   ├── r3_factory.ts
│   └── view/
│       ├── compiler.ts     # 组件/指令编译
│       ├── template.ts     # 模板解析
│       └── util.ts
│
├── expression_parser/       # 表达式解析
│   ├── lexer.ts
│   ├── parser.ts
│   └── ast.ts
│
├── ml_parser/              # 标记语言解析
│   ├── html_parser.ts
│   ├── lexer.ts
│   └── ast.ts
│
├── template_parser/        # 模板绑定解析
│   └── binding_parser.ts
│
├── output/                 # 代码生成
│   ├── output_ast.ts
│   └── abstract_emitter.ts
│
└── i18n/                   # 国际化
    └── i18n_ast.ts

📅 版本演进

版本 年份 编译器特性
Angular 2 2016 JIT 编译器
Angular 4 2017 AOT 编译器 (可选)
Angular 6 2018 Ivy 预览
Angular 9 2020 Ivy 默认启用
Angular 14 2022 Standalone 组件
Angular 17 2023 Deferred Loading 语法
Angular 18 2024 新控制流语法稳定

🎯 总结

Angular 编译器核心要点:

  • 模板解析 - HTML → R3 AST
  • 表达式解析 - 绑定表达式 → AST
  • 代码生成 - IR → JavaScript
  • 优化策略 - 常量池、Tree Shaking

关键 API:

  • parseTemplate() - 模板解析入口
  • compileComponentFromMetadata() - 组件编译
  • compileNgModule() - 模块编译
  • emitTemplateFn() - 代码生成

源码:github.com/angular/angular/tree/main/packages/compiler

🅰️ 感谢观看

Angular 编译器深度解析

访问地址:
https://atcfu.com/ai-articles/angular-compiler/

2026-03-07 | 基于 angular/angular 源码分析