vue2响应式弊端:
响应化过程需要递归遍历,消耗较大
新加或删除属性无法监听
数组响应化需要额外实现
Map、Set、Class等无法响应式
修改语法有限制
Vue3响应式原理剖析 step1
function reactive(obj) {
if (typeof obj !== null && typeof obj != 'object') {
return obj
}
//代理
const observed = new Proxy(obj, {
// target 代理目标
get(target, key, receiver) {
// Reflect 是把Object 上面的方法移到这里
// 比Object更健壮 避免一些异常
const res = Reflect.get(target,key, receiver)
console.log('获取'+ key +":"+ res)
return res
},
set(target, key, value,receiver) {
const res = Reflect.get(target,key,value, receiver)
console.log('设置'+ key+ ':'+ value)
return res
},
deleteProperty(target, key) {
const res = Reflect.deleteProperty(target, key)
console.log('删除'+ key)
return res
}
})
return observed
}
const state = reactive({
foo: 'foo',
bar: {a: 1},
arr: [0, 1,2,3]
})
state.foo
state.foo = 'f000000'
state.abai = 'a Bai'
delete state.abai
//
state.arr[0] = 10
step2
state.bar.a = 10 // 获取bar: [object Object]
**嵌套对象响应式 测试:嵌套对象不能响应 **
Q: 访问bar.a属性的时候没有代理了
A: 对代理的方法get 重新做一层代理
get(target, key, receiver) {
// Reflect 是把Object 上面的方法移到这里
// 比Object更健壮 避免一些异常
const res = Reflect.get(target,key, receiver)
console.log('获取'+ key +":"+ res)
// 返回的结果是对象 重新做一层代理
// 好处是 访问的时候才递归
return isObject(res) ? reactive(res) : res
},
step3
Q:重复代理问题? A:把代理过的对象缓存起来
const toProxy = new WeakMap() // {obj: observed}
const toRaw = new WeakMap() // {observed: obj}
...
function reactive(obj) {
// 查找缓存
if (toProxy.has(obj)) {
console.log('toProxy', toProxy.get(obj))
return toProxy.get(obj)
}
// 用户传的代理过的对象 直接返回
if (toRaw.has(obj)) {
return obj
}
const observed = new Proxy(obj, {...
})
// 缓存
toProxy.set(obj, observed)
toRaw.set(observed, obj)
return observed
}
console.log(reactive(state) === state) // true
step4
依赖收集:
建立响应数据key和更新函数之间的对应关系。
// 设置响应函数
effect(() => console.log(state.foo))
// 用户修改关联数据会触发响应函数
state.foo = 'xxx'
设计
实现三个函数: effect:将回调函数保存起来备用,立即执行一次回调函数触发它里面一些响应数据的getter
track:getter中调用track,把前面存储的回调函数和当前
target,key之间建立映射关系
trigger:setter中调用trigger,把target,key对应的响应函数都执行一遍
target,key和响应函数映射关系
// 大概结构如下所示
// target | depsMap
// obj | key | Dep
// k1 | effect1,effect2...
// k2 | effect3,effect4...
// {target: {key: [effect1,...]}}
实现的代码
在observed中的get函数中调用
//收集依赖 track(target, key) 在set()中取出 trigger(target, key)
// 保存响应函数fn
const effectStack = []
function effect(fn) {
// 执行之前入栈, 执行完 出栈
const Effect = function () {
try {
effectStack.push(fn)
// 执行函数 触发getter ->track
fn()
} catch(error) {
} finally {
// 出战
effectStack.pop()
}
}
Effect()
return Effect
}
// 建立tager/key/cb之间的映射关系
const targetMap = new WeakMap()
function track(target,key) {
// 取出响应函数
const effect = effectStack[effectStack.length -1]
if (effect) {
// 获取依赖表
let depsMap = targetMap.get(target) //
if (!depsMap) {
// 初始化时不存在,需要传教一个新的空的Map
depsMap = new Map()
targetMap.set(target, depsMap) // 对应数据结构: target | depsMap
}
// 获取key 对应的函数集合
let deps = depsMap.get(key)
if (!deps) {
deps = new Set()
depsMap.set(key, deps)
}
// 把当前effect 加入deps中
if (!deps.has(effect)) {
deps.add(effect)
}
}
}
function trigger(target, key) {
// 获取依赖 set
const depsMap = targetMap.get(target) // 获取第一层结构
if (depsMap) {
const deps = depsMap.get(key) // key层 所有的回调函数
// deps 存在 遍历 执行
deps && deps.forEach(effect => effect())
}
}
完整demo
const { displayPartsToString } = require("typescript")
const isObject = val => val !== null && typeof val === 'object'
const toProxy = new WeakMap() // {obj: observed}
const toRaw = new WeakMap() // {observed: obj}
function reactive(obj) {
if (!isObject(obj)) {
return obj
}
// 查找缓存
if (toProxy.has(obj)) {
console.log('toProxy', toProxy.get(obj))
return toProxy.get(obj)
}
// 用户传的代理过的对象 直接返回
if (toRaw.has(obj)) {
return obj
}
//代理
const observed = new Proxy(obj, {
// target 代理目标
get(target, key, receiver) {
// Reflect 是把Object 上面的方法移到这里
// 比Object更健壮 避免一些异常
const res = Reflect.get(target,key, receiver)
console.log('获取'+ key +":"+ res)
//收集依赖
track(target, key)
// 返回的结果是对象 重新做一层代理
// 好处是 访问的时候才递归
return isObject(res) ? reactive(res) : res
},
set(target, key, value,receiver) {
const res = Reflect.get(target,key,value, receiver)
trigger(target, key)
console.log('设置'+ key+ ':'+ value)
return res
},
deleteProperty(target, key) {
const res = Reflect.deleteProperty(target, key)
console.log('删除'+ key)
return res
}
})
// 缓存
toProxy.set(obj, observed)
toRaw.set(observed, obj)
return observed
}
// 保存响应函数fn
const effectStack = []
function effect(fn) {
// 执行之前入栈, 执行完 出栈
const Effect = function () {
try {
effectStack.push(fn)
// 执行函数 触发getter ->track
fn()
} catch(error) {
} finally {
// 出战
effectStack.pop()
}
}
Effect()
return Effect
}
// 建立tager/key/cb之间的映射关系
const targetMap = new WeakMap()
function track(target,key) {
// 取出响应函数
const effect = effectStack[effectStack.length -1]
if (effect) {
// 获取依赖表
let depsMap = targetMap.get(target) //
if (!depsMap) {
// 初始化时不存在,需要传教一个新的空的Map
depsMap = new Map()
targetMap.set(target, depsMap) // 对应数据结构: target | depsMap
}
// 获取key 对应的函数集合
let deps = depsMap.get(key)
if (!deps) {
deps = new Set()
depsMap.set(key, deps)
}
// 把当前effect 加入deps中
if (!deps.has(effect)) {
deps.add(effect)
}
}
}
function trigger(target, key) {
// 获取依赖 set
const depsMap = targetMap.get(target) // 获取第一层结构
if (depsMap) {
const deps = depsMap.get(key) // key层 所有的回调函数
// deps 存在 遍历 执行
deps && deps.forEach(effect => effect())
}
}
const state = reactive({
foo: 'foo',
bar: {a: 1},
arr: [0, 1,2,3]
})
effect(() => {
console.log('effect1:'+ state.foo)
})
// effect(() => {
// console.log('effect2'+ state.foo)
// })
// state.foo
state.foo = 'f000000'
// state.abai = 'a Bai'
// delete state.abai
// //
// state.arr[0] = 10
// state.bar.a = 10 // 获取bar: [object Object]
// console.log(reactive(state) === state)