2023年7月9日发(作者:)
vue3中的watch源码浅析watch源码浅析通过对reactive的解读中明⽩了在vue中将被响应式系统收集的依赖为组件更新,computed,watch三类。⽽watch的具体实现具体是怎样的?这就是本次学习的⽬标。依然通过暴露的composition api⼊⼿,从watch⽅法看整个watch的实现过程。⼀开始就是连续的4个函数重载,就知道vue3中的watch肯定也还是有所变化的了。// // overload #1: array of multiple sources + cbexport function watch< T extends MultiWatchSources, Immediate extends Readonly
转换watch的过程⽐较长,就截取⼀些关键内容,以下代码⾮完整源码function doWatch( source: WatchSource | WatchSource[] | WatchEffect | object, cb: WatchCallback | null, { immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ, instance = currentInstance): WatchStopHandle { // dev环境下的部分提⽰ if (__DEV__ && !cb) { //
打印提⽰的代码 } //
提⽰⾮法数据源,合法的数据源是:getter/effect function, ref, reactive object const warnInvalidSource = (s: unknown) => { warn( `Invalid watch source: `, s, `A watch source can only be a getter/effect function, a ref, ` + `a reactive object, or an array of these types.` ) } //
以getter函数获取数据源的值 let getter: () => any let forceTrigger = false if (isRef(source)) { //
数据源是ref getter = () => (source as Ref).value forceTrigger = !!(source as Ref)._shallow } else if (isReactive(source)) { //
数据源是reactive,deep会设置为true getter = () => source deep = true } else if (isArray(source)) { //
多数据源 getter = () => (s => { if (isRef(s)) { return } else if (isReactive(s)) { //
深度监听watch的数据源 return traverse(s) } else if (isFunction(s)) { return callWithErrorHandling(s, instance, _GETTER) } else { __DEV__ && warnInvalidSource(s) } }) } else if (isFunction(source)) { //
传⼊getter、effect函数 if (cb) { // getter with cb getter = () => callWithErrorHandling(source, instance, _GETTER) } else { // no cb -> simple effect getter = () => { if (instance && unted) { return } if (cleanup) { if (cleanup) { cleanup() } return callWithErrorHandling( source, instance, _CALLBACK, [onInvalidate] ) } } } else { //
数据源异常提⽰ getter = NOOP __DEV__ && warnInvalidSource(source) } // deep在reactive数据中会被修改为true //
因此对于多数据源,reactive数据默认是深度观测的 if (cb && deep) { const baseGetter = getter getter = () => traverse(baseGetter()) } let cleanup: () => void const onInvalidate: InvalidateCbRegistrator = (fn: () => void) => { cleanup = = () => { callWithErrorHandling(fn, instance, _CLEANUP) } } // in SSR there is no need to setup an actual effect, and it should be noop // unless it's eager if (__NODE_JS__ && isInSSRComponentSetup) { //
服务端渲染的逻辑 } let oldValue = isArray(source) ? [] : INITIAL_WATCHER_VALUE //
调度⽅法,在数据源改变后
因为响应式系统的trigger调⽤,内部会将数据源的新旧值传⼊cb中调⽤ const job: SchedulerJob = () => { if (!) { return } if (cb) { // watch(source, cb) //
调⽤runner => getter =>
数据源的最新值 const newValue = runner() if (deep || forceTrigger || hasChanged(newValue, oldValue)) { // cleanup before running cb again if (cleanup) { cleanup() } //
将newValue和oldValue作为参数传⼊cb调⽤ callWithAsyncErrorHandling(cb, instance, _CALLBACK, [ newValue, // pass undefined as the old value when it's changed for the first time oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue, onInvalidate ]) oldValue = newValue } } else { // watchEffect runner() } } // important: mark the job as a watcher callback so that scheduler knows // it is allowed to self-trigger (#1727) ecurse = !!cb let scheduler: ReactiveEffectOptions['scheduler'] //
根据flush模式优化调度⽅式 if (flush === 'sync') { scheduler = job } else if (flush === 'post') { scheduler = () => queuePostRenderEffect(job, instance && se) } else { // default: 'pre' scheduler = () => { if (!instance || ted) { queuePreFlushCb(job) } else { // with 'pre' option, the first call must happen before // the component is mounted so it is called synchronously. job() } } } //
记录getter为待收集依赖 const runner = effect(getter, { lazy: true, onTrack, onTrigger, scheduler //
依赖被触发时的调度⽅法 }) //
记录activeEffect到组件实例中 recordInstanceBoundEffect(runner, instance) // initial run //
⾸次执⾏,immediate
不为true的时候只是调⽤runner⽅法 //
将watch的数据源通过响应式系统收集runner依赖 if (cb) { if (immediate) { job() } else { oldValue = runner() } } else if (flush === 'post') { queuePostRenderEffect(runner, instance && se) } else { runner() } return () => { stop(runner) if (instance) { remove(s!, runner) } }}watch的实现逻辑主要就是在这个doWatch⽅法之中,这⾥⾯的逻辑如上注释主要分为了⼏个部分:1. 通过getter⽅法来获取数据源的值2. 通过job⽅法来调⽤传⼊watch中的cb1. job中通过调⽤runner,runner调⽤getter获取数据源新值2. doWatch中闭包缓存了数据源的旧值3. 将新旧值作为参数调⽤cb3. 将job作为activeEffect的scheduler⽅法,在后续的数据修改导致的trigger中调⽤4. ⾸次调⽤,传⼊了immediate调⽤job,未传⼊调⽤runner,以数据源为被观察者收集依赖实现响应式watch source datagetter sourcejob schedulereffect runnerimmediateYESrun job schedulerNOrun effect runnerjust run effect runnesource data gettersource data gettercall watch cbsource data changesource data settersource data trigger
发布者:admin,转转请注明出处:http://www.yc00.com/news/1688907482a182281.html
评论列表(0条)