回顾vue3组件在运行过程中的编译提升

一、Vue 编译流程概述

Vue 组件的编译全流程可以分为以下几个核心阶段:

  1. 模板解析:将 HTML 模板字符串解析为抽象语法树(AST)

  2. 优化阶段:标记静态节点,提高渲染效率

  3. 代码生成:将 AST 转换为渲染函数代码(render function)

  4. 运行时渲染:通过渲染函数生成虚拟 DOM(VNode)

  5. 挂载与更新:将 VNode 转换为真实 DOM 并处理更新

下面是整个编译流程的简化图示:

复制代码
┌───────────────────────────────────────────────────────────┐
│                      Vue 组件编译流程                       │
├───────────────────────────────────────────────────────────┤
│  编译时阶段(仅在开发环境或预编译时执行)                    │
│                                                           │
│  模板字符串 ──→ 解析器 ──→ AST ──→ 优化器 ──→ 代码生成器 ──→ 渲染函数
│                                                           │
├───────────────────────────────────────────────────────────┤
│  运行时阶段(每次渲染或更新时执行)                          │
│                                                           │
│  渲染函数 ──→ VNode ──→ 真实 DOM ──→ 视图更新(数据变化时)
│                                                           │
└───────────────────────────────────────────────────────────┘

二、编译时阶段详解

1. 模板解析(Template Parsing)

Vue 首先将组件的 template 选项转换为抽象语法树(AST)。这个过程使用了一个基于状态机的解析器,逐字符解析模板字符串,识别标签、属性、文本等内容。

关键步骤

  • 词法分析:将模板字符串分割为 tokens(如开始标签、结束标签、文本等)

  • 语法分析:根据 tokens 构建 AST 树结构

示例模板

预览

xml 复制代码
<div class="container">
  <h1>{{ message }}</h1>
  <button @click="increment">+1</button>
</div>

对应的简化 AST

javascript

yaml 复制代码
{
  type: 1,
  tag: 'div',
  attrsList: [{ name: 'class', value: 'container' }],
  children: [
    {
      type: 1,
      tag: 'h1',
      children: [
        {
          type: 2,
          expression: '_s(message)',
          text: '{{ message }}'
        }
      ]
    },
    {
      type: 1,
      tag: 'button',
      attrsList: [{ name: '@click', value: 'increment' }],
      children: [{ type: 3, text: '+1' }]
    }
  ]
}

2. 优化阶段(Optimization)

优化器遍历 AST,标记出所有静态节点(内容不会变化的节点)。这个步骤在 Vue 2.5+ 中引入,主要目的是在运行时提高更新效率。

优化器的作用

  • 标记静态节点和静态根节点

  • 在后续更新时跳过这些节点的比对,减少计算量

静态节点示例

预览

xml 复制代码
<!-- 静态节点 -->
<div>
  <p>这是一段静态文本</p>
</div>

3. 代码生成(Code Generation)

代码生成器将优化后的 AST 转换为渲染函数代码。主要生成两种函数:

  • render 函数:主渲染函数

  • staticRenderFns 数组:静态节点的渲染函数

示例渲染函数代码

javascript

javascript 复制代码
function render() {
  with(this) {
    return _c('div', { class: 'container' }, [
      _c('h1', [_v(_s(message))]),
      _c('button', { on: { click: increment } }, [_v('+1')])
    ])
  }
}

这里的 _c_v_s 是 Vue 内部的辅助函数:

  • _c:创建 VNode(对应 createElement)
  • _v:创建文本节点
  • _s:JSON.stringify 的简写,用于处理插值表达式

三、运行时阶段详解

1. 渲染函数执行

当组件实例创建或数据变化时,Vue 会执行渲染函数生成虚拟 DOM(VNode)。

关键步骤

  • 执行 render 函数生成 VNode 树
  • 如果有静态节点,复用 staticRenderFns 生成的结果

2. 虚拟 DOM 生成

渲染函数返回的 VNode 是一个轻量级 JavaScript 对象,描述了真实 DOM 的结构和属性。

示例 VNode 结构

css 复制代码
{
  tag: 'div',
  data: { class: 'container' },
  children: [
    {
      tag: 'h1',
      children: [{ text: 'Hello Vue' }]
    },
    {
      tag: 'button',
      on: { click: () => { /* ... */ } },
      children: [{ text: '+1' }]
    }
  ]
}

3. 挂载与更新

  • 首次挂载 :Vue 使用 patch 函数将 VNode 转换为真实 DOM,并插入到页面中。

  • 数据更新 :当响应式数据变化时,Vue 会重新执行渲染函数生成新的 VNode,然后通过 patch 算法比较新旧 VNode,只更新需要变化的部分。

虚拟 DOM 到真实 DOM 的转换

javascript

scss 复制代码
// 简化的 patch 过程
function patch(oldVnode, newVnode) {
  if (!oldVnode) {
    // 首次渲染:创建真实 DOM
    return createElm(newVnode);
  } else {
    // 更新:比较新旧 VNode,只更新变化的部分
    updateChildren(oldVnode.children, newVnode.children);
    // ...
  }
}

四、编译模式对比

Vue 提供了两种编译模式:

  1. 完整版(包含编译器)

    • 同时包含编译器和运行时
    • 可以在浏览器中直接编译模板字符串
    • 体积较大(约 22kb min+gzip)
  2. 运行时版(不含编译器)

    • 只包含运行时,不包含编译器
    • 需要预编译模板(如使用 Vue CLI)
    • 体积更小(约 18kb min+gzip)

五、编译过程的性能优化

Vue 通过多种方式优化编译和渲染性能:

  1. 静态节点标记:优化器标记静态节点,避免重复渲染
  2. 虚拟 DOM 差异比较:只更新变化的部分
  3. 预编译:通过构建工具提前编译模板,避免运行时编译开销
  4. 事件缓存:相同事件处理函数复用,减少内存占用

六、总结

Vue 组件的编译全流程是一个将模板转换为高效渲染函数的过程,分为编译时和运行时两个阶段:

  1. 编译时:模板 → AST → 优化 → 渲染函数

  2. 运行时:渲染函数 → VNode → 真实 DOM → 更新

理解这个过程有助于:

  • 编写更高效的 Vue 组件
  • 调试和优化性能问题
  • 理解 Vue 的一些高级特性(如 render 函数、静态节点优化)
相关推荐
LeeAt5 分钟前
真的!真的就一句话就能明白this指向问题
前端·javascript
阳火锅6 分钟前
都2025年了,来看看前端如何给刘亦菲加个水印吧!
前端·vue.js·面试
hahala233323 分钟前
ESLint 提交前校验技术方案
前端
夕水1 小时前
ew-vue-component:Vue 3 动态组件渲染解决方案的使用介绍
前端·vue.js
我麻烦大了1 小时前
实现一个简单的Vue响应式
前端·vue.js
独立开阀者_FwtCoder1 小时前
你用 Cursor 写公司的代码安全吗?
前端·javascript·github
Cacciatore->1 小时前
React 基本介绍与项目创建
前端·react.js·arcgis
摸鱼仙人~1 小时前
React Ref 指南:原理、实现与实践
前端·javascript·react.js
teeeeeeemo1 小时前
回调函数 vs Promise vs async/await区别
开发语言·前端·javascript·笔记
贵沫末1 小时前
React——基础
前端·react.js·前端框架