Vue3
响应式原理
从零手写API
实现
一.响应式const { reactive, shallowReactive, readonly, shallowReadonly } = VueReactivity
let obj = { name: 'zf', age: { n: 11 } }
const state = reactive(obj)
const state = shallowReactive(obj)
const state = readonly(obj)
const state = shallowReadonly(obj)
1
2
3
4
5
6
2
3
4
5
6
针对不同的
API
创建不同的响应式对象
import {
mutableHandlers,
readonlyHandlers,
shallowReactiveHandlers,
shallowReadonlyHandlers
} from "./baseHandlers"; // 不同的拦截函数
export function reactive(target) {
return createReactiveObject(target, false, mutableHandlers)
}
export function shallowReactive(target) {
return createReactiveObject(target, false, shallowReactiveHandlers)
}
export function readonly(target) {
return createReactiveObject(target, true, readonlyHandlers)
}
export function shallowReadonly(target) {
return createReactiveObject(target, true, shallowReadonlyHandlers)
}
/**
*
* @param target 拦截的目标
* @param isReadonly 是不是仅读属性
* @param baseHandlers 对应的拦截函数
*/
function createReactiveObject(target, isReadonly, baseHandlers) {}
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
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
shared
模块实现
二.{
"name": "@vue/shared",
"version": "1.0.0",
"description": "",
"main": "index.js",
"module": "dist/shared.esm-bundler.js",
"buildOptions": {
"formats": [
"esm-bundler",
"cjs"
]
},
"keywords": [],
"author": "",
"license": "ISC"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
配置
tsconfig.json
识别引入第三方模块
{
"baseUrl": ".",
"moduleResolution": "node",
"paths": {
"@vue/*": [
"packages/*/src"
]
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
使用
yarn install
将shared
模块注入到node_modules中
createReactiveObject
实现
三.Vue3中采用proxy实现数据代理, 核心就是拦截
get
方法和set
方法,当获取值时收集effect
函数,当修改值时触发对应的effect重新执行
import { isObject } from '@vue/shared'
const reactiveMap = new WeakMap();
const readonlyMap = new WeakMap();
function createReactiveObject(target, isReadonly, baseHandlers) {
// 1.如果不是对象直接返回
if(!isObject(target)){
return target
}
const proxyMap = isReadonly ? readonlyMap : reactiveMap; // 获取缓存对象
const existingProxy = proxyMap.get(target);
// 2.代理过直接返回即可
if(existingProxy){
return existingProxy;
}
// 3.代理的核心
const proxy = new Proxy(target,baseHandlers);
proxyMap.set(target,proxy);
// 4.返回代理对象
return proxy;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
baseHandlers
实现
import { isObject } from "@vue/shared";
import { reactive, readonly } from "./reactive";
const get = createGetter();
const shallowGet = createGetter(false, true);
const readonlyGet = createGetter(true);
const shallowReadonlyGet = createGetter(true, true)
const set = createSetter();
const shallowSet = createSetter(true);
/**
* @param isReadonly 是不是仅读
* @param shallow 是不是浅响应
*/
function createGetter(isReadonly = false, shallow = false) {
return function get(target, key, receiver) {
const res = Reflect.get(target, key, receiver);
if (!isReadonly) { // 如果是仅读的无需收集依赖
console.log('依赖收集')
}
if (shallow) { // 浅无需返回代理
return res
}
if (isObject(res)) { // 取值时递归代理
return isReadonly ? readonly(res) : reactive(res)
}
return res;
}
}
function createSetter(shallow = false) {
return function set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
return result;
}
}
export const mutableHandlers = {
get,
set
};
export const readonlyHandlers = {
get: readonlyGet,
set(target, key) {
console.warn(`Set operation on key "${String(key)}" failed: target is readonly.`)
return true;
}
};
export const shallowReactiveHandlers = {
get: shallowGet,
set: shallowSet
};
export const shallowReadonlyHandlers = {
get: shallowReadonlyGet,
set(target, key) {
console.warn(`Set operation on key "${String(key)}" failed: target is readonly.`)
return true;
}
};
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
52
53
54
55
56
57
58
59
60
61
62
63
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
52
53
54
55
56
57
58
59
60
61
62
63
四.effect实现
实现响应式effect
export function effect(fn, options: any = {}) {
// 创建响应式effect
const effect = createReactiveEffect(fn, options);
// 默认会让effect先执行一次
if (!options.lazy) {
effect();
}
return effect
}
let uid = 0;
function createReactiveEffect(fn, options) {
// 返回响应式effect
const effect = function reactiveEffect() {
// todo...
}
effect.id = uid++;// 用于做标识的
effect._isEffect = true; // 标识是响应式effect
effect.raw = fn; // 记录原本的fn
effect.deps = []; // 用于收集effect对应的相关属性
effect.options = options;
return effect;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
利用栈型结构存储effect,保证依赖关系
const state =reactive({name:'zf',age:12,address:'回龙观'})
effect(()=>{ // effect1
console.log(state.name); // 收集effect1
effect(()=>{ // effect2
console.log(state.age); // 收集effect2
});
console.log(state.address); // 收集effect1
})
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
const effect = function reactiveEffect() {
if (!effectStack.includes(effect)) {
try {
effectStack.push(effect);
activeEffect = effect; // 记录当前的effect
return fn(); // 执行用户传递的fn -> 取值操作
} finally {
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
track
依赖收集
五.function createGetter(isReadonly = false, shallow = false) {
return function get(target, key, receiver) {
// ...
if (!isReadonly) { // effect函数执行时,进行取值操作,让属性记住对应的effect函数
track(target, TrackOpTypes.GET, key);
}
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
const targetMap = new WeakMap();
export function track(target, type, key) {
if(activeEffect === undefined){ // 如果不在effect中取值,则无需记录
return
}
let depsMap = targetMap.get(target);
// WeakMap({name:'zf',age:11},{name:{Set},age:{Set}})
if(!depsMap){ // 构建存储结构
targetMap.set(target,(depsMap = new Map))
}
let dep = depsMap.get(key);
if(!dep){
depsMap.set(key,(dep = new Set));
}
if(!dep.has(activeEffect)){
dep.add(activeEffect);
activeEffect.deps.push(dep);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
trigger
触发更新
六.对新增属性和修改属性做分类
function createSetter(shallow = false) {
return function set(target, key, value, receiver) {
const oldValue = target[key];
const hadKey =
isArray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key);
const result = Reflect.set(target, key, value, receiver);
if (!hadKey) {
// 新增属性
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
// 修改属性
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
return result;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
将需要触发的effect找到依次执行
export function trigger(target, type, key?, newValue?, oldValue?) {
const depsMap = targetMap.get(target);
if (!depsMap) { // 属性没有对应的effect
return
}
const effects = new Set(); // 设置集合
const add = (effectsToAdd) => {
if (effectsToAdd) {
effectsToAdd.forEach(effect => {
effects.add(effect);
})
}
}
if (key === 'length' && isArray(target)) { // 如果修改的是长度
depsMap.forEach((dep, key) => {
// 如果有长度的依赖要更新 如果依赖的key小于设置的长度也要更新
if (key == 'length' || key >= newValue) {
add(dep)
}
});
} else {
if (key !== void 0) {
// 修改key
add(depsMap.get(key));
}
switch (type) {
case TriggerOpTypes.ADD:
if (isArray(target)) {
if (isIntegerKey(key)) { // 给数组新增属性,直接触发length即可
add(depsMap.get('length'))
}
}
break;
default:
break;
}
}
effects.forEach((effect: any) => {
effect();
})
}
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
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
Ref
七.实现ref本质就是通过类的属性访问器来实现的,可以将一个普通值类型进行包装。
import { hasChanged, isObject } from "@vue/shared";
import { track, trigger } from "./effect";
import { TrackOpTypes, TriggerOpTypes } from "./operations";
import { reactive } from "./reactive";
export function ref(value) { // ref Api
return createRef(value);
}
export function shallowRef(value) { // shallowRef Api
return createRef(value, true);
}
function createRef(rawValue, shallow = false) {
return new RefImpl(rawValue, shallow)
}
const convert = (val) => isObject(val) ? reactive(val) : val; // 递归响应式
class RefImpl {
private _value;
public readonly __v_isRef = true; // 标识是ref
constructor(private _rawValue, public readonly _shallow) {
this._value = _shallow ? _rawValue : convert(_rawValue)
}
get value() {
track(this, TrackOpTypes.GET, 'value');
return this._value;
}
set value(newVal) {
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal; // 保存值
this._value = this._shallow ? newVal : convert(newVal);
trigger(this, TriggerOpTypes.SET, 'value', newVal);
}
}
}
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
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
toRefs
八.实现class ObjectRefImpl{
public readonly __v_isRef = true
constructor(private readonly _object, private readonly _key) {}
get value(){
return this._object[this._key]
}
set value(newVal){
this._object[this._key] = newVal
}
}
export function toRef(object,key){
return new ObjectRefImpl(object,key);
}
export function toRefs(object) {
const ret = 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
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
将对象中的属性转换成ref属性
Computed
九.实现computed的整体思路和
Vue2.0
源码基本一致,也是基于缓存来实现的。
import { effect, track, trigger } from './effect';
import { isFunction } from '@vue/shared';
class ComputedRefImpl {
private _value;
private _dirty = true; // 默认是脏值
public readonly effect;
public readonly __v_isRef = true;
constructor(getter, private readonly _setter) {
this.effect = effect(getter, {
lazy: true, // 计算属性特性
scheduler: () => {
if (!this._dirty) { // 依赖属性变化时
this._dirty = true; // 标记为脏值,触发视图更新
trigger(this, 'set', 'value');
}
}
})
}
get value() {
if (this._dirty) {
// 取值时执行effect
this._value = this.effect();
this._dirty = false;
}
track(this, TrackOpTypes.GET ,'value'); // 进行属性依赖收集
return this._value
}
set value(newValue) {
this._setter(newValue);
}
}
export function computed(getterOrOptions) {
let getter;
let setter;
if (isFunction(getterOrOptions)) { // computed两种写法
getter = getterOrOptions;
setter = () => {
console.warn('computed value is readonly')
}
} else {
getter = getterOrOptions.get;
setter = getterOrOptions.set;
}
return new ComputedRefImpl(getter, setter)
}
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
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
effect.ts
effects.forEach((effect: any) => {
if(effect.options.scheduler){
return effect.options.scheduler(effect); // 如果有自己提供的scheduler,则执行scheduler逻辑
}
effect();
})
1
2
3
4
5
6
2
3
4
5
6