🧩 深入浅出讲解: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 辅助生成,并由作者整理审核。

相关推荐
蓝瑟2 小时前
AI时代程序员如何高效提问与开发工作?
前端·ai编程
林晓lx3 小时前
使用Git钩子+ husky + lint语法检查提高前端项目代码质量
前端·git·gitlab·源代码管理
王同学要变强3 小时前
【深入学习Vue丨第二篇】构建动态Web应用的基础
前端·vue.js·学习
程序定小飞4 小时前
基于springboot的web的音乐网站开发与设计
java·前端·数据库·vue.js·spring boot·后端·spring
Hello_WOAIAI4 小时前
2.4 python装饰器在 Web 框架和测试中的实战应用
开发语言·前端·python
FinClip4 小时前
凡泰极客亮相香港金融科技周,AI助力全球企业构建超级应用
前端
阿四4 小时前
【Nextjs】为什么server action中在try/catch内写redirect操作会跳转失败?
前端·next.js
申阳4 小时前
Day 6:04. 基于Nuxt开发博客项目-LOGO生成以及ICON图标引入
前端·后端·程序员
中国lanwp4 小时前
npm中@your-company:registry 和 registry 的区别
前端·npm·node.js