proxy代理的目标必须是非原始值,所以reactive不支持原始值类型。所以我们需要将原始值类型进行包装。
ref 和 shallowRef
const count = ref(0)
effect(() => {
console.log(count.value) // 0 , 1
})
count.value = 1
先实现基本的一个结构, 在 package/reactivity/src 新建 ref.ts 文件
class RefImpl {
public _value;
public __v_isRef = true;
// _rawValue原始值, 后续可以用来做比对, _shallow 为 true 也是直接返回该值就可以
// _shallow 是否浅处理
constructor(public _rawValue, public _shallow) {
}
get value() {}
set value(newValue) {}
}
function createRef(value, shallow) {
return new RefImpl(value, shallow);
}
export function ref(value) {
return createRef(value, false);
}
// 区别在于代理的是对象的时候, 修改对象的属性值不会触发更新
export function shallowRef(value) {
return createRef(value, true);
}
接下来实现 RefImpl 中的内容
import { isReactive, reactive } from "./reactive";
import { trackEffects, triggerEffects } from "./effect";
function toReactive(value) {
// 如果传进来的值是对象, 那么就返回一个 reactive 对象
return isObject(value) ? reactive(value) : value
}
class RefImpl {
public _value;
public __v_isRef = true;
public dep = new Set;
// _rawValue原始值, 后续可以用来做比对, _shallow 为 true 也是直接返回该值就可以
// _shallow 是否浅代理
constructor(public _rawValue, public _shallow) {
// 赋值, 如果是浅代理对象修改属性不会触发依赖
this._value = _shallow ? _rawValue : toReactive(_rawValue)
}
get value() {
// 收集依赖
trackEffects(this.dep);
return this._value
}
set value(newValue) {
// 先判断新旧值是否一致
if(newValue !== this._rawValue) {
this._rawValue = newValue; // 新值变旧值, 下次对比继续用
this._value = this._shallow ? newValue : toReactive(newValue);
// 更新依赖
triggerEffects(this.dep);
}
}
}
toRef 和 toRefs
将 reactive 对象转为单个ref, 因为一般的结构赋值会使 reactive 对象的属性失去响应式
const state = reactive({
foo: 1,
bar: 2
})
let {foo, bar} = state
foo = 2 // 无法触发依赖更新
// 都可以触发更新
let foo = toRef(state, 'foo')
foo.value = 2
let {foo, bar} = toRefs(state)
foo.value = 2
toRef(object, key, defaultValue?)
// bashHandle.ts
export const enum reactiveFlags {
IS_REACTIVE = "__v_isReactive",
IS_REF = "__v_isRef", // 新增
}
// ref.ts
import { reactiveFlags } from "./bashHandle";
// 判断是否是ref数据
export function isRef(r) {
return !!(r && r[reactiveFlags.IS_REF]);
}
// 本质上是利用了原reactive对象, 获取设置属性的时候实际上是对原对象做处理
class ObjectRefImpl {
public __v_isRef = true;
constructor(public _object, public _key, public _defaultValue) {}
get value() {
let val = this._object[this._key];
return val === undefined ? this._defaultValue : val;
}
set value(newValue) {
this._object[this._key] = newValue;
}
}
// object 就是 reactive对象
export function toRef(object, key, defaultValue?) {
const value = object[key];
return isRef(value) ? value : new ObjectRefImpl(object, key, defaultValue);
}
toRefs(object)
export function toRefs(object) {
// 不是代理对象提示警告
if (!isReactive(object)) {
console.warn(
`toRefs() expects a reactive object but received a plain one.`
);
}
// 判断是对象还是数组
let result = Array.isArray(object) ? new Array(object.length) : {};
for (const key in object) {
// 复制一个一模一样的对象, 但是值都是ref, 这样结构拿到的就是ref对象而不是原始数据了
result[key] = toRef(object, key);
}
return result;
}