全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-20 10 分钟 ✍️ juanwangdev

模板解析与AST生成

Vue模板编译通过词法分析将HTML模板字符串解析为AST节点树,包含标签、属性、文本等节点类型。

编译入口

JavaScript
// src/compiler/index.js
const compiled = compile(template, options)
// 返回 { ast, render, staticRenderFns, errors }

模板编译入口接收HTML字符串和编译选项,返回AST和渲染函数。

parseHTML 词法分析

JavaScript
// 核心解析循环
function parseHTML(html, options) {
  while (html) {
    last = html
    if (!inVPre && !inPre) {
      // 查找 < 位置
      textEnd = html.indexOf('<')
    }
    
    if (textEnd === 0) {
      // 注释节点
      if (matchComment) { ... }
      // 条件注释
      if (matchConditional) { ... }
      // 结束标签
      if (matchEndTag) { ... }
      // 开始标签
      if (matchStartTag) { ... }
    }
    
    // 文本节点
    if (textEnd >= 0) {
      text = html.substring(0, textEnd)
      advance(textEnd)
    }
    
    if (textEnd < 0) {
      text = html
      html = ''
    }
    
    if (options.chars && text) {
      options.chars(text)
    }
  }
}

通过正则匹配识别标签类型,逐个字符推进解析。

AST节点结构

JavaScript
// 标签节点
{
  type: 1,           // 节点类型
  tag: 'div',        // 标签名
  attrsList: [...],  // 原始属性列表
  attrsMap: {...},   // 属性映射
  parent: ASTNode,   // 父节点
  children: [],      // 子节点
  plain: true,       // 是否无属性
  static: false      // 是否静态节点
}

// 文本节点
{
  type: 2,  // 包含表达式的文本
  text: '{{ msg }}',
  expression: '_s(msg)'
}

// 纯文本节点
{
  type: 3,  // 纯文本
  text: 'hello',
  isComment: false
}

三种节点类型:元素节点(1)、带表达式文本(2)、纯文本(3)。

parseStartTag 开始标签解析

JavaScript
function parseStartTag() {
  const start = html.match(startTagOpen)
  if (start) {
    const match = {
      tagName: start[1],
      attrs: [],
      start: index,
      unarySlash: '',
      end: 0
    }
    advance(start[0].length)
    
    let end, attr
    while (!(end = html.match(startTagClose)) && 
           (attr = html.match(attribute))) {
      advance(attr[0].length)
      match.attrs.push(parseAttr(attr))
    }
    
    if (html.match(startTagClose)) {
      match.unarySlash = html.match(startTagClose)[1]
      advance(html.match(startTagClose)[0].length)
      match.end = index
      return match
    }
  }
}

解析标签名和属性列表,识别自闭合标签。

parseAttr 属性解析

JavaScript
function parseAttr(match) {
  const name = match[1]    // 属性名
  const value = match[3]   // 属性值
  return {
    name,
    value: value ? value.replace(/"/g, '') : null
  }
}

// v-bind:class="active"
// 解析为 { name: 'v-bind:class', value: 'active' }

属性通过正则捕获名值对,指令属性也按相同方式解析。

构建AST树

JavaScript
function createASTElement(tag, attrs, parent) {
  return {
    type: 1,
    tag,
    attrsList: attrs,
    attrsMap: makeAttrsMap(attrs),
    parent,
    children: []
  }
}

// 标签开始回调
function start(tag, attrs, unary) {
  let element = createASTElement(tag, attrs, currentParent)
  
  // 处理pre/v-once等指令
  processPre(element)
  processFor(element)
  processIf(element)
  processOnce(element)
  
  currentParent.children.push(element)
  element.parent = currentParent
  
  if (!unary) {
    currentParent = element
    stack.push(element)
  }
}

标签开始时创建AST节点,处理指令,维护父子关系和解析栈。

要点总结

  • 模板编译通过 parseHTML 词法分析将HTML字符串转为AST
  • AST包含三种节点类型:元素(1)、表达式文本(2)、纯文本(3)
  • 通过正则匹配识别开始标签、结束标签、注释、文本
  • 解析时维护 currentParentstack 构建树形结构
  • 属性解析统一处理,指令属性也按名值对存储
  • AST节点携带 tagattrsListattrsMapparentchildren 等信息

📝 发现内容有误?点击此处直接编辑

← 上一篇 插值表达式与事件绑定
下一篇 → 编译时指令处理
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库