Scheduler
在开始编写功能之前,我们先一起来看一下什么是scheduler,下面是从vue3中拷贝的单测。
单测
要了解一下功能点,最好的方式就是读懂单测。
// src/reactivity/effect.spec.ts
describe("effect", () => {
...
it("scheduler", () => {
let dummy;
let run:any;
// 定义一个jest函数
const scheduler = jest.fn(() => {
run = runner
})
// 创建一个响应式对象
const obj = reactive({foo: 1})
// 将effect的返回值也就是runner赋值给runner
const runner = effect(() => {
dummy = obj.foo
}, {scheduler})
// 这时候scheduler函数并未执行
expect(scheduler).not.toHaveBeenCalled()
// 默认执行了effect第一个参数fn,所以dummy为1
expect(dummy).toBe(1)
// 再次更新响应式对象的值
obj.foo++;
// 这时候执行的是scheduler,并未执行fn,所以dummy并不会同步更新
expect(scheduler).toHaveBeenCalledTimes(1)
expect(dummy).toBe(1)
// 执行run()的时候便是执行上面的fn函数,这在上一节有讲到
run()
// 这时候dummy的值才会更新
expect(dummy).toBe(2)
})
})
从上面的单测中,我们可以知道schedule是作为effect的第二个参数传入的,当执行effect(fn, {scheduler})的时候,并不会立马执行scheduler函数,而是当响应式对象属性改变的时候才会去执行scheduler,而不会去执行fn,而且此时dummy的值并未改变,当再次执行run函数的时候,dummy才会再次更新。
编码
废话已经讲了太多,直接开干。
1、修改effect函数
作为effect的入口,我们先给effect多加一个参数options,而scheduler是它的一个参数,有人可能会问,为啥不直接在第二个参数传入scheduler,当然为了后续传入更多的参数。
export function effect (fn, options:any = {}) {
const _effect = new ReactiveEffect(fn, options.scheduler)
...
}
2、修改ReactiveEffect类
在effect中,我们给new ReactiveEffect(fn, scheduler),传入了第二个参数,当然,我们也要改下这个类。
class ReactiveEffect {
constructor(fn, public scheduler?) {
...
}
...
}
你没看错,我们只用加上public scheduler?,这里加问号,是因为这个参数也可以不传。
3、修改trigger逻辑
上面,我们已经给ReactiveEffect添加了一个新的属性scheduler,而且这个属性可能没有,接下来,我们就来改下触发依赖的时候,如果scheduler存在,我们就执行它,如果没有我们就还是执行fn。
export function trigger (target, key) {
...
dep.forEach((effect) => {
if (effect.scheduler) {
effect.scheduler()
} else {
effect.run()
}
})
}
上面便是我们实现scheduler的所有流程及代码。
测试结果
PS D:\user\desktop\mini-vue> yarn test
yarn run v1.22.10
$ jest
PASS src/reactivity/tests/effect.spec.ts
PASS src/reactivity/tests/index.spec.ts
PASS src/reactivity/tests/reactive.spec.ts
Test Suites: 3 passed, 3 total
Tests: 5 passed, 5 total
Snapshots: 0 total
Time: 1.035 s
Ran all test suites.
Done in 2.26s.
总结
现在是不是还不明白为什么要加scheduler这个参数,加上之后反而还让我们的数据不会及时更新了,会不会多此一举,其实这时候大家只要一步步看懂相关功能模块的实现,等到后面自然就会理解这其中的奥妙了。
下一节,我们继续完善effect的功能,为它添加stop功能。