总的来说:
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
的工作原理!🎯