🧠 Vue 编译器的表达式处理:transformExpression 通俗讲解

在 Vue 模板编译阶段,有一个特别重要的过程:把模板里的表达式转成可执行的 JS 代码

比如模板里写:

css 复制代码
<div>{{ count + 1 }}</div>

Vue 编译器会生成类似的渲染函数:

kotlin 复制代码
return _ctx.count + 1

你写的只是 count,但编译器偷偷帮你加上了 _ctx.

这一步加前缀 _ctx.$props. 的过程,就叫 表达式重写(expression transform)

而负责干这事的,就是这个文件里的 transformExpression


一、transformExpression 是干嘛的?

它的任务分两部分:

  1. 找到表达式节点(如 {{ ... }}v-bind 等)
  2. 调用 processExpression 来改写里面的代码

简单理解就是:

它扫描模板 AST(语法树),遇到表达式就去"加工"。


二、举个例子

假设你写了这个组件:

xml 复制代码
<template>
  <div>{{ count + 1 }}</div>
</template>

<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>

模板里的 {{ count + 1 }} 看似简单,实际上 Vue 内部会:

  1. 用 Babel 把它解析成一个 JS 语法树;
  2. 找出每一个标识符(count);
  3. 判断这个变量是哪来的;
  4. 把它重写成正确的访问形式,比如 count.value

最后渲染函数就变成:

kotlin 复制代码
return count.value + 1

三、processExpression 是整个逻辑的核心

源码中 processExpression() 是整个文件的"大脑",

它会:

  1. 用 Babel 解析字符串表达式;
  2. 找出所有变量;
  3. 判断是不是 refpropssetup 变量;
  4. 给变量加上前缀或 .value

📍1. 解析表达式

ini 复制代码
ast = parseExpression(source, { plugins: context.expressionPlugins })

Vue 用 Babel 的 @babel/parser 来把字符串 count + 1 转成一棵树,

这样它就能知道哪里是变量、哪里是操作符、哪里是函数。


📍2. 遍历变量(标识符)

javascript 复制代码
walkIdentifiers(ast, (node, parent, _, isReferenced, isLocal) => {
  // ...
})

这一步相当于"扫描"所有变量,比如:

复制代码
count + msg

会找到两个标识符:countmsg


📍3. 判断变量来源(bindingMetadata)

Vue 编译时会记录每个变量的"来源":

来源类别 标识 含义 重写结果
setup 里的 ref SETUP_REF 响应式引用 count.value
setup 常量 SETUP_CONST 普通变量 count
props PROPS 传入属性 __props.xxx
data/methods 其他 实例上下文 _ctx.xxx

📍4. 真正的改写逻辑(rewriteIdentifier

看这一段是关键👇:

typescript 复制代码
if (type === BindingTypes.SETUP_REF) {
  return `${raw}.value`
} else if (type === BindingTypes.PROPS) {
  return genPropsAccessExp(raw) // => __props.xxx
} else if (type && type.startsWith('setup')) {
  return `$setup.${raw}`
} else {
  return `_ctx.${raw}`
}

意思就是:

  • 如果这个变量是个 ref → 加 .value
  • 如果是 props → 改成 __props.xxx
  • 如果是 setup 变量 → 改成 $setup.xxx
  • 其他情况 → 加 _ctx.

📍5. 拼回去生成最终表达式

举个例子:

复制代码
{{ count + msg }}

经过改写后:

复制代码
count.value + _ctx.msg

Vue 把这整个表达式重新拼接成一个"复合表达式(CompoundExpressionNode)",

这样后续生成渲染函数时就能直接插入。


四、为什么不直接用变量名?

假设你模板里写了 {{ count }}

但渲染函数最终运行在一个 JS 环境中,如果没有前缀,它会变成:

kotlin 复制代码
return count // ❌ JS 会报错:count is not defined

因为这个 count 不是全局变量,而是在组件内部。

Vue 就要帮你加上 _ctx..value,告诉 JS 去正确的对象上取值。


五、来看看几种常见的改写结果

模板写法 来源 编译结果
{{ msg }} data _ctx.msg
{{ title }} props __props.title
{{ count }} setup ref count.value
{{ name }} setup const name
{{ count + msg }} 混合 count.value + _ctx.msg

是不是就清楚多了?

Vue 的目标是让模板看起来简单,但编译后保证代码能正常访问正确的变量。


六、总结一句话

transformExpression 的工作就是:

找出模板中的每一个变量,

看它属于谁(setup / props / data / 全局),

然后在编译时改写成能正确访问的 JavaScript 代码。

这样你写模板时不用管上下文,Vue 会帮你在编译阶段自动"补全"访问路径。


✅ 小结(一句话复盘)

  • 模板 → 编译器扫描表达式
  • Babel 解析 → 找出变量
  • 判断变量来源 → 加前缀 / 加 .value
  • 重新拼成新表达式
  • 最终生成正确的渲染函数代码

本文部分内容借助 AI 辅助生成,并由作者整理审核。

相关推荐
我有一个object11 分钟前
uniapp上传文件报错:targetSdkVersion设置>=29后在Android10+系统设备不支持当前路径。请更改为应用运行路径!
前端·javascript·vue.js·uniapp
北极糊的狐16 分钟前
关于jQuery 事件绑定,记录常用事件类型及核心注意事项
前端·javascript·jquery
_Kayo_22 分钟前
vue3 computed 练习笔记
前端·vue.js·笔记
CodeSheep25 分钟前
VS 2026 正式发布,王炸!
前端·后端·程序员
无奈何杨26 分钟前
CoolGuard事件查询增加策略和规则筛选,条件结果展示
前端·后端
梦里不知身是客1129 分钟前
正则表达式常见的介绍
前端·javascript·正则表达式
初学小白...1 小时前
HTML知识点
前端·javascript·html
鹏多多1 小时前
flutter睡眠与冥想数据可视化神器:sleep_stage_chart插件全解析
android·前端·flutter
艾小码1 小时前
Vue3 脚本革命:<script setup> 让你的代码简洁到飞起!
前端·javascript·vue.js
IT_陈寒1 小时前
Python 3.12新特性解析:10个让你代码效率提升30%的实用技巧
前端·人工智能·后端