从生活实例解释什么是AST抽象语法树

AST(Abstract Syntax Tree,抽象语法树) 听起来很高深,但其实它的核心概念非常简单:把"文本"变成"结构化的数据对象" ,方便机器理解和操作。就是把字符串形式的代码 转换成机器能看懂、能操作的结构化数据 ------ 你可以把它理解成:代码的 "说明书"/"骨架"

机器(比如 Babel、Vue 编译器)看不懂直接的字符串代码(比如const a = 1),但能看懂 AST 这种 "键值对 + 层级结构" 的 JSON-like 数据,从而实现「修改代码、转换代码、分析代码」。

为了让你彻底明白,我们分两步走:先看生活中的例子,再看 Vue 中的实际应用。


第一部分:生活中的例子 ------ "点外卖"

假设你是个复杂的客户,你给服务员说了一句很长的话(这就是源代码 Source Code):

"我要一个牛肉汉堡,不要洋葱,加双份芝士,还要一杯可乐,去冰。"

1. 为什么需要 AST?

如果你直接把这句话扔给后厨的厨师,厨师可能听懵了,或者容易漏掉细节。计算机也是一样,它看不懂这一长串字符串,它需要一个清晰的清单

2. 生成 AST(解析过程)

前台服务员(编译器/解析器)听到这句话后,会在点餐系统里输入一张结构化的单子。这张单子就是 AST

它大概长这样:

json 复制代码
{
  "类型": "订单",
  "内容": [
    {
      "商品": "牛肉汉堡",
      "配料修改": [
        { "操作": "移除", "物品": "洋葱" },
        { "操作": "添加", "物品": "芝士", "数量": 2 }
      ]
    },
    {
      "商品": "可乐",
      "属性": [
        { "温度": "去冰" }
      ]
    }
  ]
}

3. 这个例子的核心点:

  • 源代码:那句口语(字符串)。
  • AST:那张结构化的单子(JSON 对象)。
  • 作用:有了这张单子,厨师(浏览器/JS引擎)不需要去分析语法,直接看字段就能精准干活;甚至如果需要把"汉堡"换成"三明治",改单子(修改 AST)比改口语容易得多。

二、回到代码:AST 到底解决了什么问题?

场景:你写了一行代码 const msg = 'hello',想把它改成 var message = 'hello'

  • 如果你直接改字符串:需要 "找 const→替换成 var,找 msg→替换成 message",但代码复杂时(比如嵌套函数、多文件),手动 / 字符串替换极易出错;
  • 用 AST 改:机器先把代码转成 AST(结构化数据),再精准修改节点,最后转回代码 ------ 安全、精准、可批量操作。

第一步:代码 → AST(解析)

const msg = 'hello' 对应的 AST 简化结构:

js 复制代码
{
  "type": "VariableDeclaration", // 节点类型:变量声明
  "kind": "const", // 变量类型:const
  "declarations": [
    {
      "type": "VariableDeclarator",
      "id": { "type": "Identifier", "name": "msg" }, // 变量名:msg
      "init": { "type": "Literal", "value": "hello" } // 变量值:hello
    }
  ]
}

此时代码不再是字符串,而是 "变量声明节点 + 变量名节点 + 值节点" 的结构化数据,每个部分都有明确标识。

第二步:操作 AST(修改)

把 AST 里的 kind: "const" 改成 "var",把 name: "msg" 改成 "message"

js 复制代码
// 伪代码:修改 AST 节点 
ast.kind = "var"; 
ast.declarations[0].id.name = "message";

第三步:AST → 代码(生成)

修改后的 AST 转回字符串代码:var message = 'hello'

Vue 中的 AST

在 Vue 中,AST 主要用于模板编译(Template Compilation)

浏览器其实只认识 HTML、CSS 和 JS。它根本不认识 Vue 的 .vue 文件,也不认识 v-if、v-for这种语法。

Vue 需要把你的 `` 变成浏览器能运行的 render 函数,中间的桥梁就是 AST

  1. 源代码(你写的 Vue 模板)
vue 复制代码
<div id="app">
  <p>你好</p>
</div>

这就好比刚才那句"我要一个汉堡...",对浏览器来说,这只是一串普通的字符串。

2. 解析成的 AST(Vue 内部生成的树)

Vue 的编译器会把上面的 HTML 字符串"拆解",变成下面这样的 JavaScript 对象(简化版):

js 复制代码
const ast = {
  // 标签类型
  tag: &#34;div&#34;,
  // 属性列表
  attrs: [{ name: &#34;id&#34;, value: &#34;app&#34; }],
  // 子节点列表
  children: [
    {
      tag: &#34;p&#34;,
      // 指令被解析成了专门的属性
      if: &#34;show&#34;, 
      children: [
        {
          type: &#34;text&#34;,
          text: &#34;你好&#34;
        }
      ]
    }
  ]
};

3. 为什么要转成 AST?(Vue 拿它干什么?)

一旦变成了上面这种树形对象,Vue 就可以对代码进行**"手术""优化"**:

  1. 识别指令:Vue 扫描这棵树,发现 p 节点有个 if: "show"。于是它知道:生成代码时,要给这行代码加个 if (show) { ... } 的判断逻辑。
  2. 静态提升(优化性能) :Vue 3 扫描 AST,发现 "你好" 是纯文本,永远不会变。Vue 就会给它打个标记:"这块不需要每次渲染都比较,直接复用"。(如果只是看字符串,很难做这种复杂的分析)。

AST 的下一步,是生成 render 函数代码(渲染函数)。

要搞懂 AST 如何转回字符串代码 ,核心是理解「AST 生成器(Generator)」的工作逻辑 ------ 它本质是深度遍历 AST 树,根据每个节点的类型和属性,拼接出对应的代码字符串

第一阶段:AST ➡️ Render 函数代码

这就是浏览器能"认识"的第一步:因为它变成了标准的 JavaScript 代码。

浏览器虽然不懂 <p>,但它懂 JavaScript 的 if 或者三元运算符 ? :。

举个栗子

你的 Vue 模板(源代码):

html 复制代码
<div id="app">
  <p>你好</p>
</div>

生成的 AST(中间产物,略):

(就是一个描述结构的 JSON 对象)

AST 转换后生成的 Render 函数代码(最终产物):

Vue 的编译器会根据 AST,拼接出一段 纯 JavaScript 字符串,长得像这样(为了方便阅读,我简化了 Vue 内部的函数名):

js 复制代码
function render() {
  // _c = createElement (创建元素)
  // _v = createTextVNode (创建文本)
  // _e = createEmptyVNode (创建空节点,用于 v-if 为 false 时)

  return _c('div', { attrs: { &#34;id&#34;: &#34;app&#34; } }, [
    // 重点看这里!v-if 被变成了 JS 的三元运算符
    (show) 
      ? _c('p', [_v(&#34;你好&#34;)]) 
      : _e()
  ])
}

这里的核心变化:

  1. HTML 标签 变成了函数调用 _c('div')。
  2. v-if="show" 消失了,变成了原生的 JS 逻辑 (show) ? ... : ...。
  3. 浏览器完全认识这段代码! 这就是一段标准的 JS 函数,里面全是函数调用和逻辑判断。

第二阶段:浏览器怎么把这段代码变成画面?

你可能会问:"浏览器运行了这个函数,然后呢?屏幕上怎么就有字了?"

这里有两个步骤:生成虚拟 DOM ➡️ 转为真实 DOM

1. 运行 Render 函数,得到 虚拟 DOM (Virtual DOM)

当 Vue 运行时(Runtime)执行上面的 render 函数时,浏览器并不会立即去画界面,而是返回一个 JS 对象树 ,这叫做 VNode(虚拟节点)

执行 render() 后得到的返回值:

js 复制代码
// 这是一个纯 JS 对象,不是真实的 DOM 元素
{
  tag: 'div',
  data: { attrs: { id: 'app' } },
  children: [
    {
      tag: 'p',
      children: [{ text: '你好' }]
    }
  ]
}

为什么要多这一步?

因为操作真实 DOM(网页上的元素)非常慢,而操作 JS 对象非常快。Vue 可以在这个 JS 对象上做各种计算(比如 Diff 算法),确认没问题了,再动手改网页。

2. Patch(修补/渲染)➡️ 真实 DOM

这是最后一步。Vue 的运行时系统(Runtime)会拿着上面的 VNode,调用浏览器底层的 DOM API

这时候,浏览器才真正干活:

  • Vue 看到 tag: 'div' ➡️ 调用 document.createElement('div')
  • Vue 看到 attrs: { id: 'app' } ➡️ 调用 el.setAttribute('id', 'app')
  • Vue 看到 text: '你好' ➡️ 调用 document.createTextNode('你好')
  • 最后把它们拼在一起,挂载到页面上。

vite创建的vue项目是通过babel还是vue自己编译器编译的

在默认的 Vite + Vue 项目中,绝大多数情况下,是不需要 Babel 的,也没有用 Babel。 它的分工是这样的:

  1. .vue 文件的编译(Template -> Render函数) :完全依靠 Vue 自己的编译器(@vue/compiler-sfc)。
  2. JS/TS 语法的转译(ES6+ -> 浏览器能跑的代码) :主要依靠 Esbuild(一个用 Go 语言写的、速度极快的构建工具)。

详细拆解:谁在干活?

为了搞清楚这个问题,我们需要把你写代码时的两个"转换"动作分开看:

1. 动作一:把 Vue 模板变成 JS 代码

也就是刚才我们聊的:v-if -> render 函数。

  • 负责工头Vue Compiler (@vue/compiler-sfc)
  • 工具链:Vite 里的插件 @vitejs/plugin-vue 会调用这个 Vue 编译器。
  • AST 产生地 :这里产生的 AST 是 Vue 专有的 Template AST
  • 结论 :这块跟 Babel 毫无关系。哪怕你安装了 Babel,Vue 模板编译也不归 Babel 管。

总结

  • AST 是什么?

    它是代码的骨架图。它把代码从"一行行文本"变成了"层级分明的对象"。

  • Vue 里的流程:

    template (字符串) ➡️ AST (树形对象) ➡️ render 函数 (可执行 JS) ➡️ 虚拟 DOM ➡️ 真实 DOM。

    2. 动作二:把高级 JS/TS 变成浏览器能懂的 JS

比如你用了箭头函数 () => {},或者 TypeScript 的类型标注 name: string,或者最新的 ?. 语法。

  • 传统做法(Webpack 时代)

    这是 Babel 的地盘。Webpack 会用 babel-loader 把这些新语法转成老旧的 ES5 代码,为了兼容 IE 等老浏览器。

  • 现代做法(Vite 时代)

    Vite 默认认为你不需要兼容 IE(除非你专门配置)。现代浏览器(Chrome, Edge, Firefox, Safari)都已经支持 ES6 模块了。

    • 开发环境 (npm run dev)

      Vite 使用 Esbuild 来处理 JS 和 TS。
      Esbuild 比 Babel 快 10-100 倍。因为它不需要把代码转成复杂的 ES5,只需要把 TypeScript 的类型去掉,把极少数浏览器不支持的语法微调一下即可。

    • 生产打包 (npm run build)

      Vite 使用 Rollup 进行打包,同时默认使用 Esbuild 进行代码压缩和转换。

      什么时候 Vite 才会用到 Babel?

虽然 Vite 默认不用 Babel,但在一种情况下它会把 Babel 请回来:

你需要兼容"老古董"浏览器时(比如 IE11 或旧版 Chrome)。 如果你安装了 @vitejs/plugin-legacy 插件:

js 复制代码
// vite.config.js
import legacy from '@vitejs/plugin-legacy'

export default {
  plugins: [
    vue(),
    legacy({
      targets: ['ie >= 11'], // 只要你需要支持这些老家伙
      additionalLegacyPolyfills: ['regenerator-runtime/runtime']
    })
  ]
}

这时候,Vite 就会在打包时自动下载并使用 Babel,把你的现代代码狠狠地转译成 ES5,以确保在老浏览器上不报错。

总结对照表

任务 Webpack (Vue CLI) Vite (现代模式)
解析 .vue 模板 Vue Loader (调用 Vue Compiler) @vitejs/plugin-vue (调用 Vue Compiler)
JS 转译 (ES6->ES5) Babel (必装,很慢) Esbuild (内置,极快,不转 ES5)
TS 转译 Babel 或 ts-loader Esbuild (毫秒级完成)
AST 类型 Vue AST + Babel AST Vue AST + Esbuild AST

Vite 项目里:

  1. Vue 编译器 负责把 里的代码变成 render 函数(利用 Vue AST)。

  2. Esbuild 负责把你的 JS/TS 变成浏览器能运行的 JS(不做过度的向下兼容)。

  3. Babel 默认是不存在的,除非你为了兼容性专门请它出山。

    所以,Vite 快的原因之一,就是把"慢吞吞"的 Babel 给优化掉了!


结论

转换成 AST 之后的代码,就是 render 函数(JavaScript 代码)。

怎么让浏览器认识?

因为那已经是纯粹的 JavaScript 了!浏览器执行这段 JS,生成虚拟节点对象,最后 Vue 内部通过 document.createElement 等原生 API 把这些对象变成了屏幕上的像素。

相关推荐
VX:Fegn08953 小时前
计算机毕业设计|基于springboot + vue校园社团管理系统(源码+数据库+文档)
前端·数据库·vue.js·spring boot·后端·课程设计
低保和光头哪个先来5 小时前
场景6:对浏览器内核的理解
开发语言·前端·javascript·vue.js·前端框架
+VX:Fegn08956 小时前
计算机毕业设计|基于springboot + vueOA工程项目管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
进击的野人7 小时前
Vue Router 深度解析:从基础概念到高级应用实践
前端·vue.js·前端框架
JIngJaneIL8 小时前
基于java+ vue学生成绩管理系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·后端
老华带你飞8 小时前
智能菜谱推荐|基于java + vue智能菜谱推荐系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
汐泽学园9 小时前
基于Vue的幼儿绘本阅读启蒙网站设计与实现
前端·javascript·vue.js
爱上妖精的尾巴10 小时前
7-2 WPS JS宏 Object对象属性的查、改、增、删
前端·javascript·vue.js
源码获取_wx:Fegn089510 小时前
基于springboot + vueOA工程项目管理系统
java·vue.js·spring boot·后端·spring
change_fate10 小时前
vue模板数组不要直接使用reverse方法
前端·javascript·vue.js