思考功能
- ref的数据是双向绑定的
- ref的数据通过.value取
根据功能写单侧
新建 ref.spec.ts
import { effect } from "../effect";
import { ref } from "../ref";
describe("ref", () => {
/** 看得见的思考
* 1. ref对象的值包在value
* 2. ref的数据也要收集,触发侦听,做双向绑定
* 3. 做缓存,重新赋值时判断值是否相等
*/
it("should be reactive", () => {
const a = ref(1);
let dummy;
let calls = 0;
effect(() => {
calls++;
dummy = a.value;
});
expect(calls).toBe(1);
expect(dummy).toBe(1);
/** 思考
* 1. 依赖ref的数据发生变动,对应也要变动
* 2. ref要触发effect,才能侦听到数据变化
*/
a.value = 2;
expect(calls).toBe(2);
expect(dummy).toBe(2);
// same value should not trigger
/** 思考
* 1. ref做缓存,要加个标识
*/
a.value = 2;
expect(calls).toBe(2);
expect(dummy).toBe(2);
});
/** 看得见的思考
* 1. ref支持基本类型和引用类型
* 2. ref可以深度侦听数据变化
*/
it("should make nested properties reactive", () => {
const a = ref({
count: 1,
});
let dummy;
effect(() => {
dummy = a.value.count;
});
expect(dummy).toBe(1);
a.value.count = 2;
expect(dummy).toBe(2);
});
核心逻辑
实现ref.value
/*
* @Author: Lin zefan
* @Date: 2022-03-17 18:23:36
* @LastEditTime: 2022-03-20 15:11:23
* @LastEditors: Lin zefan
* @Description: ref
* @FilePath: \mini-vue3\src\reactivity\ref.ts
*
*/
export function ref(ref) {
return new RefImpl(ref);
}
class RefImpl {
private _value: any;
constructor(value) {
this._value = value;
}
get value() {
return this._value;
}
set value(newVal) {
this._value = newVal;
}
}
实现ref缓存
shared/index.ts
/*
* @Author: Lin zefan
* @Date: 2022-03-15 19:28:09
* @LastEditTime: 2022-03-17 19:03:20
* @LastEditors: Lin zefan
* @Description: 公用hook
* @FilePath: \mini-vue3\src\shared\index.ts
*
*/
export function hasChanged(val, newVal) {
return Object.is(val, newVal);
}
reactivity/ref.ts
import { hasChanged } from "../shared";
/*
* @Author: Lin zefan
* @Date: 2022-03-17 18:23:36
* @LastEditTime: 2022-03-20 15:11:23
* @LastEditors: Lin zefan
* @Description: ref
* @FilePath: \mini-vue3\src\reactivity\ref.ts
*
*/
export function ref(ref) {
return new RefImpl(ref);
}
class RefImpl {
private _value: any;
constructor(value) {
this._value = value;
}
get value() {
return this._value;
}
set value(newVal) {
+ /** 思考
+ * 1. 先判断新老值,值不相等再做更新
+ */
+ if (hasChanged(this._value, newVal)) return;
this._value = newVal;
}
}
实现ref动态侦听(完整代码)
import { reactive } from ".";
import { isObject, hasChanged } from "../shared";
import { isTracking, trackEffect, triggerEffect } from "./effect";
class RefImpl {
_dep: any;
private _value: any;
__v_isRef = true;
constructor(value) {
/**
* 1. 需要判断value是基本类型还是引用类型
* 2. 引用类型需要用reactive包裹,做到深度侦听
*/
this._value = convert(value);
this._dep = new Set();
}
get value() {
/** 思考
* 1. get要收集依赖
*/
isTracking() && trackEffect(this._dep);
return this._value;
}
set value(newVal) {
/** 思考
* 1. 先判断新老值,值不相等再做更新
* 2. 更新ref.value
* 3. 更新依赖的值
*/
if (hasChanged(this._value, newVal)) return;
this._value = convert(newVal);
triggerEffect(this._dep);
}
}
function convert(value) {
return isObject(value) ? reactive(value) : value;
}