8、带你一步步实现vue3源码之onStop

342 阅读3分钟

onStop

上一节,我们完成了stop的相关逻辑,通过stop(runner)可以避免响应式数据改变的时候自动执行runner函数,现在又有一个新需求,我想在stop执行的时候做一些处理,这又该怎么实现呢?

单测

先一起来阅读下面的单测

// src/reactivity/test/effect.spec.ts
describe("effect", () => {
  ...
  it("onStop", () => {
    // 创建响应式对象obj
    const obj = reactive({
      foo: 1
    })
    const onStop = jest.fn() //定义了一个jest函数
    let dummy
    // 通过effect返回runner,并在第二个参数定义了一个onStop回调
    const runner = effect(() => {
      dummy = obj.foo
    }, {
      onStop
    })
    // 当执行stop之后,期望onStop回调被执行
    stop(runner)
    expect(onStop).toBeCalledTimes(1)
  })
})

通过上面单测的描述,其实这个功能很简单,就是在执行stop的时候,会调用effect传入onStop函数。

编码

从上面的测试用例来看,onStop的用法与scheduler非常类似,都是通过effect的第二个参数来传递的

1、接收传递的onStop

首先,我们找到effect函数,从options中获取onStop参数,并将其赋值给实例化对象_effect

// src/reactivity/effect.ts
export function effect (fn, options:any = {}) {
  ...
  _effect.onStop = options.onStop
  ...
}

?> 思考:这里我们接收onStop就可以了,为什么还要赋值给_effect实例化对象呢?因为我们上一节写的stop方法实际上调用的是ReactiveEffect类里面的方法,这样,我们把onStop也赋值给这个实例化对象,这样更方便在stop里面去调用。

2、调用onStop方法

上一步我们已经将接收的onStop方法传递给了实例化对象_effect,接下来,我们需要在ReactiveEffect中定义一个onStop,并在stop的时候来调用

// src/reactivity/effect.ts

class ReactiveEffect {
  ...
  onStop?: () => void  //onStop是可选值
  ...
  stop () {
    if (this.active) {
      cleanupEffect(this)
      // 当接收到onStop,执行它便可
      if (this.onStop) {
        this.onStop()
      }
      this.active = false
    }
  }
}

到这里,就是全部的onStop逻辑了

优化方案

下面,我们一起来看下我们这一节写的代码有没有哪里还有优化的余地,_effect.onStop = options.onStop,在effect中,我们通过这种简单赋值的方式将effect第二个参数传入的值一个个的赋值给effect,每次这样操作,有点繁琐,下面就一起来对这段代码进行优化。

?> 分析:其实我们需要做的就是将options的值挨个赋值给effect

我们便可以这么操作,利用Object.assign

// src/shared/index.ts
export const extend = Object.assign

import {extend} from '../shared'
export function effect (fn, options:any = {}) {
  ...
  extend(_effect, options)
  ...
}

上面,我们通过创建一个shared公共函数库,然后在effect中引用它,进行对象的赋值操作,这样不仅简化了这里的代码,还抽出了一个公共的函数,后续可以在其他地方调用。

测试结果

PS D:\user\desktop\mini-vue> yarn test
yarn run v1.22.10
$ jest
 PASS  src/reactivity/tests/reactive.spec.ts
 PASS  src/reactivity/tests/index.spec.ts
 PASS  src/reactivity/tests/effect.spec.ts

Test Suites: 3 passed, 3 total
Tests:       7 passed, 7 total
Snapshots:   0 total
Time:        1.848 s
Ran all test suites.
Done in 4.11s.

总结

在编写onStop的时候,我们参考scheduler的方式,先在effect中进行赋值,然后在ReactiveEffect中定义一个onStop,并在stop中进行调用,不仅如此,我们还对代码进行了抽离公共方法,简化代码逻辑,更达到了代码的复用。

下一节,我们一起来了解readonly!