Vue2作为 MVVM框架
/*
Vue2 通过 Object.defineProperty 监听、挟持数据,实现响应式
并通过 Dep(依赖收集器) 和 Watcher 实现依赖收集,通知视图更新
*/
/*
但是 Vue2用Object.defineProperty 无法监听新增属性、无法监听数组索引变化、无法监听数组长度变化
*/
// 依赖收集器
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
Dep.target = null;
// Observer 数据劫持
// 激活 get,set 函数
function Vue2Reactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
// 依赖收集
if (Dep.target) {
dep.addSub(Dep.target);
}
return val;
},
set(newVal) {
if (newVal !== val) {
val = newVal;
// 通知视图更新
dep.notify();
}
}
});
}
// 通过 Object.keys(obj) + forEach 遍历对象的所有属性都进行激活
function activeAllProperties(obj) {
if (typeof obj !== 'object' || obj === null) return;
Object.keys(obj).forEach(key => {
Vue2Reactive(obj, key, obj[key]);
});
}
// Watcher 类
class Watcher {
constructor(vm, key, callback) {
this.vm = vm;
this.key = key;
this.callback = callback;
this.value = this.get();
}
get() {
Dep.target = this;
const value = this.vm[this.key];
Dep.target = null;
return value;
}
update() {
const newValue = this.vm[this.key];
if (newValue !== this.value) {
this.value = newValue;
this.callback(newValue);
}
}
}
Vue3
Vue3不再强调 MVVM,其底层是由 TypeScript 编写的,将 Object.defineProperty 换成 Proxy 实现响应式系统,弥补了前者的缺陷,使用编译器将模版换成高效的渲染函数,并通过虚拟 DOM 和调度系统实现高性能更新
/* 为什么要用 Reflect
统一为函数式写法,符合面向对象
报错不会中断,而是返回 false(比如只读的时候)
通过 receiver 参数可以指定 this上下文
*/
const bucket = new WeakMap(); // 用于依赖收集
function Vue3Reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
track(target, key); // 收集依赖
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
trigger(target, key); // 触发更新
return result;
}
});
}
// 收集依赖函数
function track(target, key) {
if (!activeEffect) return; // 如果没有激活的 effect,直接返回
let depsMap = bucket.get(target); // 获取 target 的依赖映射
if (!depsMap) bucket.set(target, (depsMap = new Map()));
let deps = depsMap.get(key); // 获取 key 的依赖集合
if (!deps) depsMap.set(key, (deps = new Set()));
deps.add(activeEffect); // 将当前激活的 effect 添加到依赖集合
}
// 触发更新函数
function trigger(target, key) {
const depsMap = bucket.get(target); // 获取 target 的依赖映射
if (!depsMap) return; // 如果没有依赖,直接返回
const deps = depsMap.get(key); // 获取 key 的依赖集合
if (deps) deps.forEach(fn => fn()); // 遍历依赖集合,执行每个依赖的函数
}
// 示例
let activeEffect;
function effect(fn) {
activeEffect = fn;
fn(); // 立即执行一次
activeEffect = null;
}
const state = Vue3Reactive({ count: 0 });
effect(() => {
console.log('count is:', state.count); // 依赖收集
});
state.count++; // 触发更新
实际上,Vue3在底层上还做了许多细节的优化,比如惰性代理节省操作、微任务批次处理节流更新、自动对依赖进行清理等等