ref的概念
proxy
代理的目标必须是非原始值,所以reactive
不支持原始值类型。所以我们需要将原始值类型进行包装将原始值类型包装成对象(
RefImpl类
的实例就是对象)
ref
不关心数据类型 数组对象基础数据类型都可以检测到得到的是对象的话就 用
reactive
转成proxy
转换结果
{
_value: false,
get(){
return this._value
},
ser(newValue){
this._value = newValue
}
}
Ref & ShallowRef
Ref将原始值类型包装成对象
ShallowRef创建浅ref 不会进行深层代理
使用
const {ref, effect, reactive, toRefs, proxyRefs} = VueReactivity
const flag = ref(false) // 将原始值类型包装成对象
effect(()=>{
document.body.innerHTML = flag.value ? 'jdlyp':'lyp'
});
setTimeout(()=>{
flag.value = true
},1000);
实现
入口函数
class RefImpl{
constructor(public rawValue, public _shallow) {
}
}
// 将原始类型包装成对象, 同时也可以包装对象 进行深层代理
export function ref(value) {
return new RefImpl(value, false);
}
// 创建浅ref 不会进行深层代理
export function shallowRef(value) {
return new RefImpl(value, true);
}
RefImpl 具体实现
- 1、判断如果是对象 使用
reactive
将对象转为响应式的 - 2、
set
的值不等于初始值 判断新值是否是对象 进行赋值
import { reactive } from "./reactive";
import { trackEffects, triggerEffects } from './effect'
export const isObject = (value) => {
return typeof value === 'object' && value !== null
}
// 将对象转化为响应式的
function toReactive(value) {
return isObject(value) ? reactive(value) : value
}
class RefImpl {
public _value;
public dep = new Set; // 依赖收集
public __v_isRef = true; // 是ref的标识
// rawValue 传递进来的值
constructor(public rawValue, public _shallow) {
// 1、判断如果是对象 使用reactive将对象转为响应式的
// 浅ref不需要再次代理
this._value = _shallow ? rawValue : toReactive(rawValue);
}
get value(){
// 取值的时候依赖收集
trackEffects(this.dep)
return this._value;
}
set value(newVal){
if(newVal !== this.rawValue){
// 2、set的值不等于初始值 判断新值是否是对象 进行赋值
this._value = this._shallow ? newVal : toReactive(newVal);
// 赋值完 将初始值变为本次的
this.rawValue = newVal
triggerEffects(this.dep)
}
}
}
toRef&toRefs 解决解构时响应式丢失问题
toRefs
将所有的属性转换成ref
(再包装了一层对象)
toRefs
参数必须是响应式的对象
const company = reactive({name:'jd',age:'20'})
// let {name,age} = company // 会丧失响应式
let {name,age} = toRefs(company)
effect(()=>{
document.body.innerHTML = `${name.value}今年${age.value}岁了`
});
setTimeout(()=>{
age.value = 31;
},1000)
实现
// 只是将 响应式对象的.value属性 代理到原始类型上
class ObjectRefImpl {
public __v_isRef = true
constructor(public _object, public _key) { }
get value() {
return this._object[this._key];
}
set value(newVal) {
this._object[this._key] = newVal;
}
}
// 将响应式对象中的某个属性转化成ref
export function toRef(object, key) {
return new ObjectRefImpl(object, key);
}
// 循环 将所有的属性转换成ref 要识别是对象还是数组
export function toRefs(object){
const ret = Array.isArray(object) ? new Array(object.length) : {};
for (const key in object) {
ret[key] = toRef(object, key);
}
return ret;
}
自动脱ref(proxyRefs方法)
不使用
.value
就能取到值反向取ref
一般在模板中使用后续我们取值都是在模板中取值的
在使用数据的时候 都是把响应式的数据
toRefs
,在使用的时候不需要+.value
let name = ref('lyp')
let age = ref('20')
let person = proxyRefs({name,age})
effect(()=>{
document.body.innerHTML = `${person.name}今年${person.age}岁了`
})
setTimeout(()=>{
person.age = 31;
},1000)
实现
export function proxyRefs(object){
return new Proxy(object,{
get(target,key,receiver){
let v = Reflect.get(target,key,receiver);
// 看一下是不是ref 是的话自动.value 不是的话返回原值
return v.__v_isRef? v.value:v;
},
set(target,key,value,receiver){
const oldValue = target[key];
// 看一下是不是ref 是的话自动给.value赋值 不是的话直接用Reflect去设置
if(oldValue.__v_isRef){
oldValue.value = value;
return true
}else{
return Reflect.set(target,key,value,receiver)
}
}
})
}
不支持脱ref的情况
数组中的ref
是不支持脱ref的,访问的时候必须加上.value
使用数组 + 下标的方式访问ref的时候
(proxyArr[0])
不进行脱ref
const proxyArr = reactive([ref(1),2,3])
console.log(proxyArr[0].value) // 这种情况不支持脱ref 必须加上.value