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

静态节点标记与优化

Vue通过 markStatic 递归标记AST中不包含动态绑定的节点,实现渲染优化和虚拟DOM diff跳过。

markStatic 标记过程

JavaScript
function markStatic(node: ASTNode) {
  node.static = isStatic(node)
  if (node.type === 1) {
    // 排除slot和component
    if (
      node.tag !== 'slot' &&
      node.tag !== 'component' &&
      !isPlatformReservedTag(node.tag) === false
    ) {
      for (let i = 0, l = node.children.length; i < l; i++) {
        const child = node.children[i]
        markStatic(child)
        if (!child.static) {
          node.static = false
        }
      }
    }
    if (node.ifConditions) {
      walkBlockConditions(node.ifConditions, node)
    }
  }
}

function isStatic(node): boolean {
  if (node.type === 2) return false  // 含表达式的文本
  if (node.type === 3) return true   // 纯文本
  return !!(
    node.pre ||
    (
      !node.hasBindings &&
      !node.if && !node.for &&
      !isBuiltInTag(node.tag) &&
      isPlatformReservedTag(node.tag) &&
      !isDirectChildOfTemplateFor(node) &&
      Object.keys(node).every(isStaticKey)
    )
  )
}

递归遍历AST,子节点有非静态则父节点也标记为非静态。

静态节点判定条件

JavaScript
// 非静态特征
const isBuiltInTag = makeMap('slot,component')
const isStaticKey = makeMap(
  'type,tag,attrsList,attrsMap,plain,parent,' +
  'children,attrs,staticRoot,staticProcessed'
)

function isStatic(node): boolean {
  // type=2 包含 {{ }} 表达式 → 非静态
  // type=3 纯文本 → 静态
  // 包含 v-bind/v-on 等动态绑定 → 非静态
  // 包含 v-if/v-for → 非静态
  // slot/component 标签 → 非静态
  // 原生标签且无动态属性 → 静态
}

静态节点必须是无动态绑定、无v-if/v-for的原生标签或纯文本。

markStaticRoots 静态根节点

JavaScript
function markStaticRoots(node, index) {
  if (node.type === 1) {
    if (node.static && node.children.length && !(
      node.children.length === 1 &&
      node.children[0].type === 3
    )) {
      node.staticRoot = true
      return
    } else {
      node.staticRoot = false
    }
    node.children.forEach((child, i) => {
      markStaticRoots(child, i)
    })
  }
}

静态根节点是包含子节点的静态节点,用于优化diff过程。

静态根节点优化条件

JavaScript
// 不为静态根的情况
// 1. 只有一个纯文本子节点
//    <div>hello</div>  // staticRoot = false
// 2. 没有子节点
//    <br/>             // staticRoot = false

// 为静态根的情况
// <div>
//   <span>hello</span>  // 多个子节点或子节点非纯文本
//   <p>world</p>
// </div>                // staticRoot = true

避免将单一纯文本子节点标记为静态根,优化收益不足。

生成静态渲染函数

JavaScript
function genStatic(el, state) {
  el.staticProcessed = true
  state.staticRenderFns.push(
    `with(this){return ${genElement(el, state)}}`
  )
  return `_m(${state.staticRenderFns.length - 1})`
}

静态节点生成独立的渲染函数,通过 _m(index) 调用。

渲染优化效果

JavaScript
// 原始模板
<div>
  <h1>标题</h1>       // 静态节点
  <p>{{ msg }}</p>    // 动态节点
  <footer>版权信息</footer>  // 静态节点
</div>

// 渲染函数
function render() {
  with(this) {
    return _c('div', [
      _m(0),           // 静态节点h1,调用staticRenderFns[0]
      _c('p', [_v(_s(msg))]),  // 动态节点,每次渲染
      _m(1)            // 静态节点footer,调用staticRenderFns[1]
    ])
  }
}

静态节点通过 _m() 调用,只在首次渲染时执行,diff时直接复用。

要点总结

  • markStatic 递归标记AST中无动态绑定的节点为静态
  • 静态节点判定:无v-bind/v-on、无v-if/v-for、非slot/component
  • 子节点有非静态则父节点也标记为非静态
  • markStaticRoots 标记包含多子节点的静态节点为静态根
  • 静态根节点生成独立渲染函数,通过 _m(index) 调用
  • diff时静态节点直接跳过比较,提升渲染性能

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

← 上一篇 虚拟DOM渲染函数生成
下一篇 → Vue大型项目API层封装与请求管理
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

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

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