🧩 深入浅出讲解:analyzeScriptBindings —— Vue 如何分析 <script> 里的变量绑定


一、这段代码是干什么的?

Vue 组件有两种写法:

类型 示例 特点
普通 <script> export default { data(){...}, props:{...} } 传统写法
<script setup> 顶层直接写 const count = ref(0) Vue3 新写法

而 Vue 编译器在处理 .vue 文件时,需要知道:

每个变量来自哪里?它是 props 吗?data 吗?methods 吗?

这段源码的作用就是:

👉 当我们用"普通写法"时,分析出每个变量的"来源类型"。


二、运行结果长什么样?

假设我们有个组件:

javascript 复制代码
export default {
  props: ['title'],
  data() {
    return { count: 0 }
  },
  methods: {
    inc() { this.count++ }
  }
}

经过这段分析函数后,会得到这样的结果对象:

yaml 复制代码
{
  title: 'props',
  count: 'data',
  inc: 'options',
  __isScriptSetup: false
}

这就告诉 Vue:

  • title 来自 props;
  • count 来自 data;
  • inc 是 methods;
  • 不是 <script setup>

三、从头到尾一步步看源码逻辑

Step 1️⃣:找到 export default { ... }

arduino 复制代码
export function analyzeScriptBindings(ast: Statement[]): BindingMetadata {
  for (const node of ast) {
    if (
      node.type === 'ExportDefaultDeclaration' &&
      node.declaration.type === 'ObjectExpression'
    ) {
      return analyzeBindingsFromOptions(node.declaration)
    }
  }
  return {}
}

🧠 意思:

  • AST 是整段脚本的语法树。

  • 遍历每个语句,找到:

    arduino 复制代码
    export default { ... }
  • 然后调用 analyzeBindingsFromOptions() 来分析里面的对象内容。


Step 2️⃣:创建结果对象并标记类型

php 复制代码
const bindings: BindingMetadata = {}
Object.defineProperty(bindings, '__isScriptSetup', {
  enumerable: false,
  value: false,
})

📘 这一步干嘛?

  • 初始化一个结果对象;
  • 加一个隐藏属性 __isScriptSetup=false,告诉系统"这是普通 script"。

Step 3️⃣:逐个分析对象里的属性

例如:

javascript 复制代码
export default {
  props: ['foo'],
  data() { return { msg: 'hi' } },
  methods: { sayHi() {} }
}

程序就会循环每个属性(props、data、methods...),判断它是哪种类型。


Step 4️⃣:不同类型的属性,分别分析

(1) props 分析

vbnet 复制代码
if (property.key.name === 'props') {
  for (const key of getObjectOrArrayExpressionKeys(property.value)) {
    bindings[key] = BindingTypes.PROPS
  }
}

🧠 支持两种写法:

  • props: ['foo', 'bar']
  • props: { foo: String }

结果:

css 复制代码
{ foo: 'props', bar: 'props' }

(2) inject 分析

vbnet 复制代码
else if (property.key.name === 'inject') {
  for (const key of getObjectOrArrayExpressionKeys(property.value)) {
    bindings[key] = BindingTypes.OPTIONS
  }
}

对应:

vbnet 复制代码
inject: ['token']

👉 结果 { token: 'options' }


(3) methods / computed 分析

python 复制代码
else if (
  property.value.type === 'ObjectExpression' &&
  (property.key.name === 'computed' || property.key.name === 'methods')
)

📘 当 methods: { sayHi(){} }computed: { total(){} } 时,

把每个函数名记录下来:

css 复制代码
{ sayHi: 'options', total: 'options' }

(4) data / setup 分析

python 复制代码
else if (
  property.type === 'ObjectMethod' &&
  (property.key.name === 'setup' || property.key.name === 'data')
)

这时要进入函数体里查找 return 的内容:

javascript 复制代码
data() {
  return { count: 0 }
}
setup() {
  return { foo: ref(0) }
}

📘 分析结果:

  • data 返回的变量 → BindingTypes.DATA
  • setup 返回的变量 → BindingTypes.SETUP_MAYBE_REF

四、辅助函数们(简化理解)

1️⃣ 获取对象的键名

css 复制代码
function getObjectExpressionKeys(node) {
  // 从 { foo: 1, bar: 2 } 中提取出 ['foo', 'bar']
}

2️⃣ 获取数组的键名

css 复制代码
function getArrayExpressionKeys(node) {
  // 从 ['foo', 'bar'] 中提取出 ['foo', 'bar']
}

3️⃣ 自动判断是对象还是数组

javascript 复制代码
export function getObjectOrArrayExpressionKeys(value) {
  // 根据类型选择上面的函数
}

五、整体运行逻辑图

markdown 复制代码
AST语法树
   ↓
找到 export default {}
   ↓
进入 analyzeBindingsFromOptions()
   ↓
循环每个属性:
   - props → PROPS
   - inject → OPTIONS
   - methods/computed → OPTIONS
   - data → DATA
   - setup → SETUP_MAYBE_REF
   ↓
返回 BindingMetadata

六、为什么这么做?

因为 Vue 在模板编译时,需要知道哪些名字是:

  • 响应式变量(data、setup)
  • 只读输入(props)
  • 普通函数(methods)

这样模板里写的:

css 复制代码
<p>{{ count }}</p>

才能被编译成正确的访问代码:

复制代码
_ctx.count

或者:

复制代码
_props.title

七、你可以怎么用它?

如果你想做:

  • 自定义 Vue 编译工具;
  • 分析 .vue 文件中定义的变量;
  • 或者写一个 ESLint 规则来检测组件结构;

就可以直接复用这段逻辑,让它帮你快速"读懂" Vue 组件结构。


八、潜在问题

问题 说明
不支持动态 key [foo]: value 这种会被忽略
不识别 TS 类型 如果写 props: { foo: String as PropType<number> } 不会处理
无法分析复杂 setup 返回逻辑 例如条件 return 不被识别

✅ 总结一句话

这段代码的作用就是让编译器"看懂"一个普通 Vue 组件里的变量来源,区分哪些是 props、data、methods、setup 返回的。


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

相关推荐
无限压榨切图仔1 小时前
一个优惠券需求改了三端,我才明白全栈不是多学一门语言
前端·后端
晚安日记wanna1 小时前
批量请求失败只弹一个 Toast面试官想听五层
前端·面试·架构
晚安日记wanna1 小时前
TS const 类型参数一道面试题的四层追问
前端·面试·typescript
用户921080262861 小时前
前端 Vue 专栏 09:Vue Router 与前端路由
前端
無限進步D2 小时前
Java Web 前端 简介
java·开发语言·前端·css·html·css3·web
码士集团小青2 小时前
从落地到“半停用“:qiankun 微前端在一个 Vue 3 + Vite 项目中的完整实践与反思
前端
the局外人2 小时前
掌控自己的 K 线数据:TradingView 本地部署与数据源接入
前端·vue.js·websocket
deli0073 小时前
配色对比度检查器:一眼看清文字和背景谁看不清
前端
梦诺3 小时前
vue3 keepAlive+记录滚动条
前端·javascript·vue.js
葡萄城技术团队3 小时前
SpreadJS V19.2 新特性揭秘:甘特表的进度线
前端