reactive 最基础的功能,把一个对象封装成响应式对象。
核心的函数:
Proxy(target, handler)
参数: target, 代理对象。
handler 在代理对象出现变动的时候,可以触发的操作函数,
如 get ,set ,del 等
思路流程图:
采用TDD的操作,先写测试。
it('happ path', () => {
const original = { foo: 1} // 原对象
const observed = reactive(original) // 响应式对象
expect(observed).not.toBe(original)
expect(observed.foo).toBe(1)
expect(isReactive(observed)).toBe(true)
expect(isReactive(original)).toBe(false)
expect(isProxy(observed)).toBe(true)
})
实现 reactive
reactive() -> 创建 createReactiveObject()
// 先定义map, 然后把 每一个对象内容以 key= target的形式存储
const targetMap = new Map()
const get = createGetter()
const set = createSetter()
const basehandlers = { get , set }
// 初始化的时候创建
function createReactiveObject() {
const proxy = new Proxy(taget, basehandlers)
}
// 初始化的时候 effect 包裹的函数会触发 get操作,
// 收集effect 中的 fn函数,以便set的时候调用。
function createGetter () {
const res = Reflect.get(traget, key)
// 收集依赖
track(traget, key)
return res
}
//
function createSetter () {
// 参数是 basehandlers
return funciton set (target, key, value) {
// **Reflect** 是一个内置的对象,它提供拦截 JavaScript 操作的方法。通过 Reflect 去把值设置。
const res = Reflect.set(traget, key, value)
// 触发update,依赖收集
trigger(target, key)
return res
}
}
触发依赖收集 :
function tarck (target, key) {
// 取出target
let depsMap = targetMap.get(target)
if(!depsMap){
// 初始化的时候需要创建一个大的对象 ,存储全部dep,用map
depsMap = new Map()
targetMap.set(target, depsMap)
}
let dep = depsMap.get(key)
if(!dep) {
// 初始化
// 不允许有重复的 key值对象,用了 set
dep = new Set()
depsMap.set(key, dep)
}
trackEffect(dep)
}
function trackEffect(dep) {
// 防止重复收集依赖.
if(dep.has(activeEffect)) return
dep.add(activeEffect)
}
触发update :
// update 触发。
export function trigger (target, key){
let depsMap = targetMap.get(target)
let dep = depsMap.get(key)
triggerEffects(dep)
}
function triggerEffects (dep) {
for(effect of dep) {
effect.run()
}
}
reactive 的响应实现完毕了, 现在实现effect函数。
effect 响应
测试代码:
it('happy path', () => {
const user = reactive({
age: 10,
name: 'zhangsan'
})
let nextAge;
let newName;
effect(() => {
nextAge = user.age + 1
})
effect(() => {
newName = user.name + '2'
})
expect(nextAge).toBe(11)
user.age++
//update
// expect(nextAge).toBe(12)
// expect(newName).toBe('zhangsan2')
})
获取到 fn : 创建一个类,定义一个全局变量去收集当前的 fn
// 全局变量
let activeEffect;
export class ReactiveEffect {
private _fn: any
deps = []
constructor(fn, public ){
this._fn = fn
}
run() {
// 把传入的fn 赋值
activeEffect = this
// 执行fn
this._fn()
}
}
// 依赖收集
export function effect (fn, options:any = {}) {
const _effect = new ReactiveEffect(fn, options.scheduler)
_effect.run()
}
Q: 依赖收集的时候使用的 MAP 嵌套 SET A: target -> key -> dep
Q : 在响应式对象, reactive , ref 改变的时候,如何去触发 视图的改变和更新??为什么要实现 effect? mvvm ,数据操作视图, 当数据发生改变,视图应该也要发生改变。 我们再 render函数的时候, 已经把响应式对象,放入 render函数中。所以 当响应式对象发生改变的时候,回去调用 effect 中的 fn。 当响应式值发生改变的时候,触发 get、set ,触发 track ,触发 trigger -> 触发 triggerEffects , 然后触发 effect , effect.scheduler || effect.run 去更新dom。