总的来说:
ref通过对象包装 ({ value })和 getter/setter 拦截 ,在模板编译阶段自动解包.value,并利用响应式系统(track/trigger)实现依赖收集和更新触发,从而让基本类型和对象都能响应式变化。
精简版:👉
ref用对象存值,.value触发响应式,模板自动解包。
Vue 3 模板中 ref 的实现原理详解
在 Vue 3 中,ref 是一个非常重要的响应式 API,用于创建可变的响应式数据 。它在模板 和组合式 API(Composition API) 中广泛使用。
本文将深入探讨:
ref的基本用法ref在模板中的工作原理ref的底层实现机制- 与
reactive的区别 - 示例代码演示
📌 1. ref 的基本用法
ref 用于创建一个响应式引用 ,可以包装基本类型(如 number、string)或对象:
javascript
import { ref } from 'vue';
const count = ref(0); // 创建一个响应式 ref,初始值为 0
console.log(count.value); // 访问值需要使用 .value
count.value++; // 修改值
在 模板 中,Vue 会自动解包 .value,所以可以直接使用:
vue
<template>
<button @click="count++">{{ count }}</button>
</template>
📌 注意:
- 在 JS 中 访问
ref必须用.value。 - 在 模板 中 无需
.value(Vue 自动解包)。
📌 2. ref 在模板中的工作原理
(1)为什么模板里不用 .value?
Vue 的模板编译器 在编译阶段会对 ref 进行特殊处理:
js
<template>
<div>{{ count }}</div>
</template>
会被编译成:
javascript
import { ref } from 'vue';
setup() {
const count = ref(0);
return { count }; // 返回 ref 对象
}
Vue 在渲染时 自动调用 .value:
javascript
// 伪代码:Vue 内部处理
render() {
return h('div', count.value); // 自动解包
}
(2)ref 在模板中的响应式更新
当 ref.value 变化时,Vue 的响应式系统会触发组件更新:
javascript
count.value = 10; // 修改 ref,触发重新渲染
底层机制:
ref内部使用ReactiveEffect进行依赖收集。- 模板中使用
ref时,Vue 会自动追踪依赖。 - 当
ref.value变化时,触发 组件的重新渲染。
📌 3. ref 的底层实现原理
(1)ref 的核心实现
ref 的源码(简化版):
javascript
function ref(value) {
return {
_isRef: true, // 标识是 ref 对象
_value: value, // 存储值
get value() {
track(this, 'value'); // 依赖收集
return this._value;
},
set value(newVal) {
if (newVal !== this._value) {
this._value = newVal;
trigger(this, 'value'); // 触发更新
}
}
};
}
📌 关键点:
ref返回一个对象 ,包含value的 getter/setter。track():在get时收集依赖(模板、计算属性等)。trigger():在set时触发更新。
(2)ref vs reactive
| 特性 | ref |
reactive |
|---|---|---|
| 适用类型 | 基本类型 + 对象 | 仅对象/数组 |
| 访问方式 | .value(JS 中) |
直接访问属性 |
| 模板解包 | 自动解包(无需 .value) |
无需解包 |
| 实现方式 | 基于 getter/setter |
基于 Proxy |
为什么 ref 需要 .value?
因为 ref 可以包装基本类型(如 number),而 JavaScript 的 Proxy 无法代理基本类型,所以必须用对象包装。
📌 4. 示例:ref 在模板中的使用
示例 1:计数器
js
<template>
<button @click="increment">{{ count }}</button>
</template>
<script setup>
import { ref } from 'vue';
const count = ref(0);
function increment() {
count.value++; // 修改 ref
}
</script>
✅ 运行效果:
- 点击按钮,
count自动更新并重新渲染。
示例 2:DOM 引用
js
<template>
<input ref="inputRef" />
<button @click="focusInput">Focus Input</button>
</template>
<script setup>
import { ref } from 'vue';
const inputRef = ref(null); // 存储 DOM 引用
function focusInput() {
inputRef.value.focus(); // 访问 DOM 元素
}
</script>
✅ 运行效果:
- 点击按钮,
input自动聚焦。
📌 5. 总结
ref的作用:创建响应式数据(基本类型 + 对象)。- 模板中的
ref:- 自动解包
.value(无需手动写)。 - 修改
ref.value触发重新渲染。
- 自动解包
- 底层原理 :
- 基于
getter/setter+ 依赖收集(track/trigger)。 - 比
reactive更灵活(支持基本类型)。
- 基于
- 适用场景 :
- 基本类型的响应式数据(如
number、string)。 - 存储 DOM 引用(
<div ref="el">)。 - 需要明确
.value访问的情况(更可控)。
- 基本类型的响应式数据(如
🚀 进阶建议:
- 如果想深入 Vue 响应式系统,可以研究
effect、track、trigger的源码。 - 在组合式 API 中,
ref和reactive可以结合使用。
希望这篇解析能帮助你彻底理解 ref 的工作原理!🎯