Vue3响应式原理深度解析

前言

Vue3相比Vue2最大的变化之一就是响应式系统的重写,从Object.defineProperty到Proxy,带来了更强大的功能和更好的性能。本文将深入剖析Vue3响应式系统的实现原理,帮助你理解Vue3的核心机制。


一、Vue2 vs Vue3响应式对比

1.1 Vue2的局限性

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
// Vue2使用Object.defineProperty
const data = {
count: 0
};

Object.defineProperty(data, 'count', {
get() {
console.log('get count');
return this._count;
},
set(val) {
console.log('set count');
this._count = val;
}
});

// 问题1: 无法监听新增属性
data.newProp = 'new'; // 不会触发更新

// 问题2: 无法监听数组索引和长度变化
const arr = [1, 2, 3];
arr[0] = 4; // 不会触发更新
arr.length = 0; // 不会触发更新

// 问题3: 需要递归遍历所有属性
// 性能开销大

1.2 Vue3的改进

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
// Vue3使用Proxy
const data = {
count: 0
};

const proxy = new Proxy(data, {
get(target, key) {
console.log('get', key);
return target[key];
},
set(target, key, value) {
console.log('set', key, value);
target[key] = value;
return true;
}
});

// ✅ 可以监听新增属性
proxy.newProp = 'new'; // 触发set

// ✅ 可以监听数组变化
const arr = reactive([1, 2, 3]);
arr[0] = 4; // 触发更新
arr.length = 0; // 触发更新

// ✅ 惰性监听,按需代理
// 性能更好

二、Proxy基础

2.1 Proxy核心API

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
// 创建Proxy
const proxy = new Proxy(target, handler);

// handler支持的拦截操作
const handler = {
// 读取属性
get(target, key, receiver) {
console.log(`getting ${key}`);
return Reflect.get(target, key, receiver);
},

// 设置属性
set(target, key, value, receiver) {
console.log(`setting ${key} = ${value}`);
return Reflect.set(target, key, value, receiver);
},

// 检查属性
has(target, key) {
console.log(`checking ${key}`);
return key in target;
},

// 删除属性
deleteProperty(target, key) {
console.log(`deleting ${key}`);
return delete target[key];
},

// 获取自身属性
ownKeys(target) {
console.log('ownKeys');
return Reflect.ownKeys(target);
}
};

2.2 Reflect的作用

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
// Reflect提供了更合理的返回值
// 以及与Proxy一一对应的方法

const obj = {};

// 老方式
try {
Object.defineProperty(obj, 'prop', { value: 1 });
console.log('success');
} catch (e) {
console.log('fail');
}

// Reflect方式
if (Reflect.defineProperty(obj, 'prop', { value: 1 })) {
console.log('success');
} else {
console.log('fail');
}

// 在Proxy中使用Reflect
const proxy = new Proxy(obj, {
get(target, key, receiver) {
// receiver确保this指向正确
return Reflect.get(target, key, receiver);
}
});

三、响应式核心实现

3.1 reactive实现

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
// 简化版reactive实现
const targetMap = new WeakMap();

function reactive(target) {
// 如果不是对象,直接返回
if (typeof target !== 'object' || target === null) {
return target;
}

const handler = {
get(target, key, receiver) {
// 追踪依赖
track(target, key);

const result = Reflect.get(target, key, receiver);

// 如果是对象,递归创建响应式
if (typeof result === 'object' && result !== null) {
return reactive(result);
}

return result;
},

set(target, key, value, receiver) {
const oldValue = target[key];
const result = Reflect.set(target, key, value, receiver);

// 触发更新
if (oldValue !== value) {
trigger(target, key);
}

return result;
},

deleteProperty(target, key) {
const hadKey = hasOwn(target, key);
const result = Reflect.deleteProperty(target, key);

if (hadKey && result) {
trigger(target, key);
}

return result;
}
};

return new Proxy(target, handler);
}

function hasOwn(target, key) {
return Object.prototype.hasOwnProperty.call(target, key);
}

3.2 依赖收集(track)

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
let activeEffect = null;
const effectStack = [];

function track(target, key) {
if (!activeEffect) return;

// targetMap: WeakMap<target, Map<key, Set<effect>>>
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}

let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}

// 收集依赖
dep.add(activeEffect);

// 反向收集,用于cleanup
activeEffect.deps.push(dep);
}

// 示例
const state = reactive({ count: 0 });

// 读取count时,track(state, 'count')
// 收集当前的effect作为依赖

3.3 触发更新(trigger)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;

const dep = depsMap.get(key);
if (!dep) return;

// 执行所有收集的effect
const effects = new Set(dep);
effects.forEach(effect => {
if (effect !== activeEffect) {
effect.run();
}
});
}

// 示例
const state = reactive({ count: 0 });

// 修改count时,trigger(state, 'count')
// 执行所有依赖count的effect

3.4 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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class ReactiveEffect {
constructor(fn, scheduler = null) {
this.fn = fn;
this.scheduler = scheduler;
this.deps = [];
this.active = true;
}

run() {
if (!this.active) {
return this.fn();
}

try {
activeEffect = this;
effectStack.push(activeEffect);

// 清理旧的依赖
this.cleanup();

// 执行函数,触发依赖收集
return this.fn();
} finally {
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
}
}

cleanup() {
// 从所有依赖集合中移除自己
for (const dep of this.deps) {
dep.delete(this);
}
this.deps.length = 0;
}

stop() {
if (this.active) {
this.cleanup();
this.active = false;
}
}
}

function effect(fn, options = {}) {
const _effect = new ReactiveEffect(fn, options.scheduler);

// 立即执行一次
_effect.run();

// 返回runner
const runner = _effect.run.bind(_effect);
runner.effect = _effect;

return runner;
}

// 使用示例
const state = reactive({ count: 0 });

effect(() => {
console.log(state.count); // 读取count,收集依赖
});

state.count++; // 修改count,触发effect重新执行

四、ref实现

4.1 基础ref

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
class RefImpl {
constructor(value) {
this._value = convert(value);
this.__v_isRef = true;
this.dep = new Set();
}

get value() {
// 依赖收集
if (activeEffect) {
this.dep.add(activeEffect);
activeEffect.deps.push(this.dep);
}
return this._value;
}

set value(newValue) {
if (newValue !== this._value) {
this._value = convert(newValue);

// 触发更新
this.dep.forEach(effect => {
if (effect !== activeEffect) {
effect.run();
}
});
}
}
}

function convert(value) {
return typeof value === 'object' && value !== null
? reactive(value)
: value;
}

function ref(value) {
return new RefImpl(value);
}

// 使用
const count = ref(0);

effect(() => {
console.log(count.value); // 读取.value,收集依赖
});

count.value++; // 修改.value,触发更新

4.2 shallowRef

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
// shallowRef不会对对象进行深层响应式
function shallowRef(value) {
const ref = {
_value: value,
__v_isRef: true,
dep: new Set(),

get value() {
if (activeEffect) {
this.dep.add(activeEffect);
activeEffect.deps.push(this.dep);
}
return this._value;
},

set value(newValue) {
// 不使用convert,直接赋值
if (newValue !== this._value) {
this._value = newValue;
this.dep.forEach(effect => effect.run());
}
}
};

return ref;
}

4.3 toRef和toRefs

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
function toRef(object, key) {
return {
__v_isRef: true,

get value() {
return object[key];
},

set value(newValue) {
object[key] = newValue;
}
};
}

function toRefs(object) {
const ret = {};

for (const key in object) {
ret[key] = toRef(object, key);
}

return ret;
}

// 使用
const state = reactive({
name: 'John',
age: 25
});

const { name, age } = toRefs(state);

// name和age是ref,但修改它们会影响state
name.value = 'Jane';
console.log(state.name); // 'Jane'

五、computed实现

5.1 基础computed

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
class ComputedRefImpl {
constructor(getter) {
this._dirty = true;
this._value = undefined;
this.dep = new Set();

// 创建effect
this.effect = new ReactiveEffect(getter, () => {
// scheduler: 依赖变化时不立即执行,而是标记为dirty
if (!this._dirty) {
this._dirty = true;
// 触发computed的依赖
this.dep.forEach(effect => effect.run());
}
});
}

get value() {
// 依赖收集
if (activeEffect) {
this.dep.add(activeEffect);
activeEffect.deps.push(this.dep);
}

// 懒计算
if (this._dirty) {
this._value = this.effect.run();
this._dirty = false;
}

return this._value;
}
}

function computed(getter) {
return new ComputedRefImpl(getter);
}

// 使用
const state = reactive({ count: 0 });

const double = computed(() => {
console.log('computing...');
return state.count * 2;
});

console.log(double.value); // computing... 0
console.log(double.value); // 0 (不会重新计算)

state.count++;
console.log(double.value); // computing... 2

六、watch实现

6.1 基础watch

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
64
65
66
67
68
69
function watch(source, cb, options = {}) {
let getter;

// 处理source
if (typeof source === 'function') {
getter = source;
} else if (source.__v_isRef) {
getter = () => source.value;
} else {
getter = () => traverse(source);
}

let oldValue;

// 创建effect
const job = () => {
const newValue = effectFn.run();
cb(newValue, oldValue);
oldValue = newValue;
};

const effectFn = new ReactiveEffect(getter, () => {
if (options.flush === 'post') {
// 在DOM更新后执行
Promise.resolve().then(job);
} else {
job();
}
});

if (options.immediate) {
job();
} else {
oldValue = effectFn.run();
}

// 返回停止函数
return () => {
effectFn.stop();
};
}

// 递归遍历对象,触发依赖收集
function traverse(value, seen = new Set()) {
if (typeof value !== 'object' || value === null || seen.has(value)) {
return value;
}

seen.add(value);

for (const key in value) {
traverse(value[key], seen);
}

return value;
}

// 使用
const state = reactive({ count: 0 });

watch(
() => state.count,
(newValue, oldValue) => {
console.log(`count changed: ${oldValue} -> ${newValue}`);
},
{ immediate: true }
);

state.count++; // count changed: 0 -> 1

七、响应式工具函数

7.1 isReactive和isRef

1
2
3
4
5
6
7
8
9
10
11
function isReactive(value) {
return !!(value && value.__v_isReactive);
}

function isRef(value) {
return !!(value && value.__v_isRef);
}

function isProxy(value) {
return isReactive(value) || isReadonly(value);
}

7.2 unref和toValue

1
2
3
4
5
6
7
8
9
10
function unref(ref) {
return isRef(ref) ? ref.value : ref;
}

// Vue 3.3+
function toValue(source) {
return typeof source === 'function'
? source()
: unref(source);
}

7.3 proxyRefs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 自动解包ref
function proxyRefs(objectWithRefs) {
return new Proxy(objectWithRefs, {
get(target, key, receiver) {
const value = Reflect.get(target, key, receiver);
return isRef(value) ? value.value : value;
},

set(target, key, newValue, receiver) {
const oldValue = target[key];
if (isRef(oldValue) && !isRef(newValue)) {
oldValue.value = newValue;
return true;
}
return Reflect.set(target, key, newValue, receiver);
}
});
}

// setup返回的对象会被自动proxyRefs
// 所以模板中可以直接使用ref而不需要.value

八、性能优化

8.1 shallowReactive

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 只代理第一层
function shallowReactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
track(target, key);
return Reflect.get(target, key, receiver);
// 不递归reactive
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
trigger(target, key);
return result;
}
});
}

8.2 readonly

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
function readonly(target) {
return new Proxy(target, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver);

// 深度readonly
if (typeof result === 'object' && result !== null) {
return readonly(result);
}

// 不收集依赖
return result;
},

set() {
console.warn('Set operation failed: target is readonly');
return true;
},

deleteProperty() {
console.warn('Delete operation failed: target is readonly');
return true;
}
});
}

九、实战应用

9.1 自定义响应式状态

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
// 创建一个响应式Store
import { reactive, computed, watch } from 'vue';

class Store {
constructor(state) {
this.state = reactive(state);
this.getters = {};
this.mutations = {};
this.actions = {};
}

registerGetter(name, fn) {
this.getters[name] = computed(() => fn(this.state));
}

commit(type, payload) {
const mutation = this.mutations[type];
if (mutation) {
mutation(this.state, payload);
}
}

dispatch(type, payload) {
const action = this.actions[type];
if (action) {
return action(this, payload);
}
}
}

// 使用
const store = new Store({
count: 0
});

store.registerGetter('doubleCount', (state) => state.count * 2);

store.mutations.increment = (state) => {
state.count++;
};

watch(
() => store.state.count,
(val) => console.log('count:', val)
);

9.2 响应式表单

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
import { reactive, computed } from 'vue';

function useForm(initialValues) {
const form = reactive({
values: { ...initialValues },
errors: {},
touched: {}
});

const isValid = computed(() => {
return Object.keys(form.errors).length === 0;
});

function setFieldValue(field, value) {
form.values[field] = value;
}

function setFieldTouched(field) {
form.touched[field] = true;
}

function validate(rules) {
form.errors = {};

for (const [field, fieldRules] of Object.entries(rules)) {
const value = form.values[field];

for (const rule of fieldRules) {
const error = rule(value);
if (error) {
form.errors[field] = error;
break;
}
}
}
}

return {
form,
isValid,
setFieldValue,
setFieldTouched,
validate
};
}

十、总结

Vue3响应式系统的核心:

关键概念

  1. Proxy: 代理对象,拦截操作
  2. track: 依赖收集
  3. trigger: 触发更新
  4. effect: 副作用函数
  5. ref/reactive: 创建响应式数据

优势

  • ✅ 更完善的拦截能力
  • ✅ 更好的性能(惰性代理)
  • ✅ 更好的TypeScript支持
  • ✅ 更灵活的组合式API

理解Vue3响应式原理,让你更好地驾驭Vue3!


参考资料