vue3源码学习(五) ------ ref 、toRef、toRefs、proxyRefs 源码学习
ref 源码学习回顾、复习。
ref 源码学习
- [vue3源码学习(五) ------ ref 、toRef、toRefs、proxyRefs 源码学习](#vue3源码学习(五) —— ref 、toRef、toRefs、proxyRefs 源码学习)
- [实现 ref 、toRef、toRefs、proxyRefs函数](#实现 ref 、toRef、toRefs、proxyRefs函数)
-
-
- [1、实现 ref 函数](#1、实现 ref 函数)
- [2、实现 toRef函数](#2、实现 toRef函数)
- [3、实现 toRefs函数](#3、实现 toRefs函数)
- [4、实现 proxyRefs 函数](#4、实现 proxyRefs 函数)
-
- 二、ref与reactive的区别
- 三、完整代码
使用示例:
javascript
//---------------- ref使用 -------------------
const name = ref('zhangsan')
watchEffect(()=>{
document.getElementById('app').innerHTML = name.vaue
})
setTimeout(()=> {
name.value = 'lisi'
}, 1000)
//---------------- toRef使用 -------------------
const person = reactive({name:'zhagnsan', age: 18})
let name = toRef(person, 'name')
watchEffect(()=>{
document.getElementById('app').innerHTML = name.vaue
})
setTimeout(()=> {
// 这种情况下这两种写法都会触发更新
// person.name = 'lisi'
name.value = 'lisi'
}, 1000)
//---------------- toRefs使用 -------------------
const person = reactive({name:'zhagnsan', age: 18})
const { name, age }= toRefs(person)
watchEffect(()=>{
document.getElementById('app').innerHTML = name.value + age.value
})
setTimeout(()=> {
name.value = 'lisi'
age.value= 40
}, 1000)
//---------------- proxyRefs使用 -------------------
const person = reactive({name:'zhagnsan', age: 18})
watchEffect(()=>{
// 解决解构响应式消失
const { name, age }= toRefs(person)
document.getElementById('app').innerHTML = name.value + age.value
})
setTimeout(()=> {
person.name = 'lisi'
person.value= 40
}, 1000)
实现 ref 、toRef、toRefs、proxyRefs函数
ref一般用于基本类型,还有就是整个对象替换时使用,对象类型一般使用reactive
1、实现 ref 函数
分析:ref函数返回的是RefImpl对象,其数据拦截是通过 getter/setter 拦截 .value(非 Proxy),取值取的是RefImpl中的value。当时ref后的变量在effect中使用时则会收集依赖,在修改时触发effect从而实现更新。

javascript
function ref(value){
return new RefImpl(value)
}
function toReative(value){
// 判断是否是对象,如果是对象就使用reactive转响应式,改与取都是使用的reactive中的get、set
return isObject(value) ? reactive(value) : value
}
class RefImpl {
dep = undefined;
_value;
__is__isRef = true
constructor(public rawValue){
this._value = toReative(rawValue)
}
get value(){
// 当activeEffect为真时,说明当前正在执行一个effect,需要收集依赖
if(activeEffect){
trackEffect(this.dep || (this.dep = new Set()));
}
return this._value
}
set value(newValue){
if(newValue !== this.rawValue){
// 将处理后的值赋值
this._value = toReative(newValue)
this.rawValue = newValue
// 触发更新
triggerEffect(this.dep)
}
}
}
2、实现 toRef函数
分析: toRef的核心价值在于:为响应式对象(reactive/props)的单个属性创建一个保持双向绑定的 ref,避免解构时响应式丢失,同时精准传递属性引用。转换后具体数据结构结构如下图。

javascript
// 定义类
class ObjectRefImpl {
__is__isRef = true
constructor(public _object, public key){}
get value(){
return this._object[this.key]
}
set value(newValue){
this._object[this.key] = newValue
}
}
export function toRef(target, key){
return new ObjectRefImpl(target, key)
}
3、实现 toRefs函数
分析:toRefs解决的就是不写多行toRef。
javascript
export function toRef(target, key){
return new ObjectRefImpl(target, key)
}
4、实现 proxyRefs 函数
proxyRefs 是 Vue 3(3.2+ 版本导出为公开 API)中用于自动解包对象中 ref 属性的工具函数,核心作用是:
- 访问属性时:若属性是 ref,自动返回 .value
- 赋值时:若原属性是 ref,更新其 .value;否则创建新 ref
- 保持响应式连接(修改会同步回源对象)
javascript
export function proxyRefs(objectWithRefs){
return new Proxy(objectWithRefs, {
get(target, key, receiver){
const v = Reflect.get(target, key, receiver)
return v.__is_isRef ? v.value : v
},
set (target, key, newValue, receiver){
const oldValue = target[key]
if(oldValue.__is_isRef){
oldValue.value = newValue
return true;
}
return Reflect.set(target, key, newValue, receiver)
}
})
}
二、ref与reactive的区别
ref :针对单个值,通过 getter/setter 拦截 .value(非 Proxy)。
reactive :针对对象,返回 Proxy 实例,通过 Proxy 的 get/set 拦截属性访问。
(二者底层共享 track/trigger 逻辑,但拦截机制不同)
三、完整代码
javascript
import { isObject } from "@vue/shared";
import { reactive } from "./reactive";
import { activeEffect, trackEffect, triggerEffect } from "./effect";
function toReative(value){
return isObject(value) ? reactive(value) : value
}
// **ref**:针对单个值,通过 getter/setter 拦截 .value(非 Proxy)。
// **reactive**:针对对象,返回 Proxy 实例,通过 Proxy 的 get/set 拦截属性访问。
class RefImpl {
dep = undefined;
_value;
__is__isRef = true
constructor(public rawValue){
this._value = toReative(rawValue)
}
get value(){
// 当获取ref的value时,将ref的dep收集到activeEffect中
// 当activeEffect为真时,说明当前正在执行一个effect,需要收集依赖
if(activeEffect){
trackEffect(this.dep || (this.dep = new Set()));
}
return this._value
}
set value(newValue){
if(newValue !== this.rawValue){
this._value = toReative(newValue)
this.rawValue = newValue
triggerEffect(this.dep)
}
}
}
export function ref(value){
return new RefImpl(value)
}
class ObjectRefImpl {
__is__isRef = true
constructor(public _object, public key){}
get value(){
return this._object[this.key]
}
set value(newValue){
this._object[this.key] = newValue
}
}
export function toRef(target, key){
return new ObjectRefImpl(target, key)
}
export function toRefs(Ojbect){
const ret = {}
for(let key in Ojbect){
ret[key] = toRef(Ojbect, key)
}
return ret
}
export function proxyRefs(objectWithRefs){
return new Proxy(objectWithRefs, {
get(target, key, receiver){
const v = Reflect.get(target, key, receiver)
// __is_isRef判断是否是ref
return v.__is_isRef ? v.value : v
},
set (target, key, newValue, receiver){
const oldValue = target[key]
// __is_isRef判断是否是ref
if(oldValue.__is_isRef){
oldValue.value = newValue
return true;
}
return Reflect.set(target, key, newValue, receiver)
}
})
}
结语:
若没有了解过effect和reactive的实现可阅读相关内容: