Vue3源码III-effect补充
停止effect更新的方法
先看使用Vue源码中停止effect更新的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"></div> <script type="module"> import { reactive, effect} from '../../../node_modules/@vue/reactivity/dist/reactivity.esm-browser.js'
const state = reactive({name:'gzy'}) let a = 1 const runner = effect(()=>{ console.log('runner') app.innerHTML = state.name + a }) runner.effect.stop();
setTimeout(()=>{ state.name = 'ggggg' a = 100 runner()
state.name = 'zzzz' },2000) </script> </body> </html>
|
- 在原生effect种调用effect会 返回一个runner函数
- 调用runner.effect.stop() 会停止effect的响应式能力,不再收集相关依赖,不是响应式了
- 直接调用 runner 相当于 vue2中的 forceUpdate 方法强制更新视图
根据这些特性来一步步给我们的effect中增加相关的方法
1 2 3 4 5 6 7 8 9 10 11 12
| export function effect(fn){ const _effect = new ReactiveEffect(fn) _effect.run()
const runner = _effect.run.bind(_effect) runner.effect = _effect
return runner }
|
- runner方法能直接调用,说明调用的是_effect中的run方法
- runner.effect.stop需要我们把 runner.effect指向_effect,并且在 class ReactiveEffect 上扩展 stop方法
- 并且应该 有一个 active 属性来标识组件是不是正常状态
- active = true 正常执行
- active = false 清除相关依赖,停止依赖收集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| export class ReactiveEffect { constructor(private fn){ } parent = undefined active = true deps = [] run(){ if(!this.active){ return this.fn() }
try{ this.parent = activeEffect activeEffect = this
cleanupEffect(this)
return this.fn() } finally { activeEffect = this.parent this.parent = undefined } } stop(){ if(this.active){ this.active = false cleanupEffect(this) } } }
|
数据变了自己来控制渲染
我们希望数据变化了但是5s后再执行页面渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import { reactive, effect } from './reactivity.js'
const state = reactive({ name: 'gzy' }) let a = 1
const runner = effect(() => { app.innerHTML = state.name + a }, { scheduler: () => { setTimeout(() => { runner() }, 5000) } })
setTimeout(() => { state.name = 'gggggg' }, 1000)
|
1 2 3 4 5 6 7 8 9 10 11 12
| export function effect(fn, options: any = {}){ const _effect = new ReactiveEffect(fn, options.scheduler) _effect.run()
const runner = _effect.run.bind(_effect) runner.effect = _effect
return runner }
|
- 在 class ReactiveEffect 中接受这个参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| export class ReactiveEffect { constructor(private fn, public scheduler){ } parent = undefined active = true deps = [] run(){ if(!this.active){ return this.fn() }
try{ this.parent = activeEffect activeEffect = this
cleanupEffect(this)
return this.fn() } finally { activeEffect = this.parent this.parent = undefined } } stop(){ if(this.active){ this.active = false cleanupEffect(this) } } }
|
- 在渲染时判断有没有传递参数
- 传递了对应的更新函数则调用此函数
- 没有传递则默认就是重新运行effect函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| export function trigger(target,key,newVal,oldVal){ const depsMap = targetMap.get(target) if(!depsMap) { return }
const dep = depsMap.get(key);
const effects = [...dep] effects && effects.forEach(effect => { if(effect !== activeEffect){ if(effect.scheduler){ effect.scheduler() }else{ effect.run() } } }) }
|
reactive深度代理
handler.ts
如果在取值的时候发现取出来的值是对象,那么再次进行代理,返回代理后的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| export const mutableHandlers = { get(target,key,receiver){ if(key === ReactiveFlags.IS_REACTIVE){ return true } if(isObject(target[key])){ return reactive(target[key]) }
const res = Reflect.get(target,key ,receiver) track(target,key) return res }, ... }
|