下面主要讲一下vue3.x
中一些常用方法的实现。本文主要是基于reactivity/src/ref.ts
的源码进行解读的。下面会将一些暴露到外面api
的源码进行分析一下。
# 1、ref的实现
- 在将
ref(val)
中的值转换成响应式的时候,要判断该值是否已经是响应式的了,如果已经是的话,就直接返回。 - 如果传入的值不是一个对象,则直接返回原值。否则调用
reactive
方法将对象变成响应式对象。响应式对象上有响应式的value
属性(通过Object。defineProperty
来实现)。 - 获取响应式对象的
value
属性时,会对该对象的value
属性进行依赖收集。但是由于ref
函数的创建过程中并没有调用effect
函数,所以在track
函数中的activeEffect
为undefined
,会直接返回,所以并不会对value
属性进行依赖收集。 - 在该响应式对象的
value
属性被修改时,触发更新。 - 在给
value
设置值的时候,还要判断该值是否是对象,如果是对象,需要对该对象进行响应式处理。
function ref(value) {
return createRef(value);
}
//创建ref对象
function createRef(rawValue, shallow) {
//判断shallow是不是undefined
if (shallow === void 0) { shallow = false; }
//判断传入的值是否已经调用过ref方法了
if (isRef(rawValue)) {
return rawValue;
}
return new RefImpl(rawValue, shallow);
}
function isObject(val){
return val!==null&&typeof val==='object';
}
//如果传入的值是对象,就将原始值转换成响应式的,否则不做任何处理
function convert(val) {
return isObject(val) ? reactive_1.reactive(val) : val;
};
//作为构造函数使用
function RefImpl(_rawValue, _shallow) {
//判断_shallow是否为undefined
if (_shallow === void 0) { _shallow = false; }
//原始值
this._rawValue = _rawValue;
this._shallow = _shallow;
//用于表示_rawValue是否已经被创建成ref了
this.__v_isRef = true;
//转换后的最终的值
this._value = _shallow ? _rawValue : convert(_rawValue);
}
Object.defineProperty(RefImpl.prototype, "value", {
get: function () {
//依赖收集 但是因为它不再effect中,所以不会对它进行依赖收集,调用track方法时,activeEffect为undefined
effect_1.track(reactive_1.toRaw(this), operations_1.TrackOpTypes.GET, 'value');
return this._value;
},
set: function (newVal) {
//判断原来的值跟现在的值是否一样
if (shared_1.hasChanged(reactive_1.toRaw(newVal), this._rawValue)) {
this._rawValue = newVal;
//判断值是否是对象,是的话,需要进行响应式处理
this._value = this._shallow ? newVal : convert(newVal);
effect_1.trigger(reactive_1.toRaw(this), operations_1.TriggerOpTypes.SET, 'value', newVal);
}
},
enumerable: true,//可被遍历 默认是false
configurable: true//可被修改 默认是false
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
ref
的用法demo
const foo=ref({
a:123
})
//是一个RefImpl 的实例对象
console.log("foo",foo);//foo RefImpl {_rawValue: {…}, _shallow: false, __v_isRef: true, _value: Proxy}
//是一个调用reactive后的代理对象
console.log("foo.value",foo.value);//Proxy {a: 123}
//foo.value变成了响应式对象
console.log(isReactive(foo.value));//true
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 2、isRef
用于判断是否是响应式的。因为将数据转换成响应式时,会给响应对象添加一个__v_isRef
的属性,并设值为true
。
function isRef(r) {
return Boolean(r && r.__v_isRef === true);
}
1
2
3
2
3
# 3、shallowRef
该方法也是创建一个ref
,但是在创建的过程中不会调用reactive
方法将值转换成响应式对象。对它转换后的value
值也不会做响应式代理。
该方法本质上就是给传入的对象添加一些属性,并对它的value
属性进行劫持。
function shallowRef(value) {
return createRef(value, true);
}
1
2
3
2
3
# 4、unref
如果他本身是一个ref
对象,则获取ref
对象的value
属性对应的值(是一个代理对象),否则返回它自身。所以此时该对象也不是ref
对象了。
function unref(ref) {
return isRef(ref) ? ref.value : ref;
}
//demo
const foo=ref({
name:"james"
})
console.log(foo);
// __v_isRef: true
// _rawValue: {name: "james"}
// _shallow: false
// _value: Proxy {name: "james"}
// value: Proxy
console.log(unref(foo));//=>Proxy {name: "james"}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 5、proxyRefs
对ref
对象进行代理。如果该对象不是响应式的,将不会对它进行依赖收集和触发更新操作。
var shallowUnwrapHandlers = {
get: function (target, key, receiver) { return unref(Reflect.get(target, key, receiver)); },
set: function (target, key, value, receiver) {
var oldValue = target[key];
if (isRef(oldValue) && !isRef(value)) {
oldValue.value = value;
return true;
}
else {
return Reflect.set(target, key, value, receiver);
}
}
};
function proxyRefs(objectWithRefs) {
return reactive_1.isReactive(objectWithRefs)
? objectWithRefs
: new Proxy(objectWithRefs, shallowUnwrapHandlers);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 6、customRef
customRef
用于自定义一个 ref
,可以显式地控制依赖追踪和触发响应,接受一个工厂函数,两个参数分别是用于追踪的 track
与用于触发响应的 trigger
,并返回一个带有 get
和 set
属性的对象。官网上有一个用它来实现防抖的例子 (opens new window)。
//以下是该函数的定义
function customRef<T>(factory: CustomRefFactory<T>): Ref<T>
type CustomRefFactory<T> = (
track: () => void,
trigger: () => void
) => {
get: () => T
set: (value: T) => void
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 7、toRefs
将响应式对象转换成ref对象
//将响应式对象转换成ref对象
function toRefs(object) {
if ( !isProxy(object)) {
console.warn(`toRefs() expects a reactive object but received a plain one.`);
}
const ret = shared.isArray(object) ? new Array(object.length) : {};
for (const key in object) {
ret[key] = toRef(object, key);
}
return ret;
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 8、toRef
将普通对象的转换成ref
对象。可以用来为一个 reactive 对象的属性创建一个 ref。这个 ref 可以被传递并且能够保持响应性。
class ObjectRefImpl {
constructor(_object, _key) {
this._object = _object;
this._key = _key;
this.__v_isRef = true;
}
get value() {
return this._object[this._key];
}
set value(newVal) {
this._object[this._key] = newVal;
}
}
function toRef(object, key) {
return isRef(object[key])
? object[key]
: new ObjectRefImpl(object, key);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//例子
const state = reactive({
foo: 1,
bar: 2,
})
const fooRef = toRef(state, 'foo')
fooRef.value++
console.log(state.foo) // 2
state.foo++
console.log(fooRef.value) // 3
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10