当前位置: 首页 > wzjs >正文

莱芜金点子广告电子版最新一期免费seo网站诊断免费

莱芜金点子广告电子版最新一期,免费seo网站诊断免费,wordpress同步文章插件,奇墙网站建设watch的作用是监听数据的变化,当数据发生变化时,执行一个回调函数,它的实现依赖于 Vue 的响应式系统(reactive和ref) 案例 通过以下案例来理解,首先引入reactive、effect、watch三个函数,声明…

watch的作用是监听数据的变化,当数据发生变化时,执行一个回调函数,它的实现依赖于 Vue 的响应式系统(reactiveref

案例

通过以下案例来理解,首先引入reactiveeffectwatch三个函数,声明obj响应式数据,接着执行watch函数,第一个参数为监听数据,第二个参数为监听回调,最后两秒后修改objname

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><script src="../../dist/vue.global.js"></script></head><body><div id="app"></div><script>const { reactive, effect, watch } = Vue// 1. reactive 构建响应性数据const obj = reactive({name: 'jc'})// 2. 执行了 watch 函数watch(obj, (value, oldValue) => {console.log('watch 触发了')console.log(value)})// 3. 两秒后触发 setter 行为setTimeout(() => {obj.name = 'cc'}, 2000)</script></body>
</html>

doWatch方法

watch函数定义在packages/runtime-core/src/apiWatch.ts文件下:

在这里插入图片描述

该函数实际执行的是doWatch方法,它会根据传入的参数( source cb )生成一个监听器

1)根据 source 的类型生成 getter

getter是一个函数,用于获取监听数据的值,根据source的类型:

  • reactive类型,直接返回source
  • ref类型,返回source.value
  • 如果source是一个函数,getter会执行这个函数

2)定义 job 函数

jobwatch的核心逻辑,它会在数据变化时执行,它的主要任务是:

  • 获取新的值newValue
  • 检查新值和旧值是否不同
  • 如果不同,执行回调函数cb

3)调度器 scheduler

scheduler决定了job的执行时机,Vue 提供了三种调度方式:

  • sync:同步执行
  • post:在渲染后执行
  • pre:在渲染前执行(默认)
function doWatch(source: WatchSource | WatchSource[] | WatchEffect | object,cb: WatchCallback | null,{ immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
): WatchStopHandle {// 省略const instance = currentInstancelet getter: () => anylet forceTrigger = falselet isMultiSource = falseif (isRef(source)) {// 是否 ref 类型getter = () => source.valueforceTrigger = isShallow(source)} else if (isReactive(source)) {// 是否 reactive 类型getter = () => sourcedeep = true // 主动开启 深度监听} else if (isArray(source)) {isMultiSource = trueforceTrigger = source.some(s => isReactive(s) || isShallow(s))getter = () =>source.map(s => {if (isRef(s)) {return s.value} else if (isReactive(s)) {return traverse(s)} else if (isFunction(s)) {return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER)} else {__DEV__ && warnInvalidSource(s)}})} else if (isFunction(source)) {if (cb) {// getter with cbgetter = () =>callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)} else {// no cb -> simple effectgetter = () => {if (instance && instance.isUnmounted) {return}if (cleanup) {cleanup()}return callWithAsyncErrorHandling(source,instance,ErrorCodes.WATCH_CALLBACK,[onCleanup])}}} else {getter = NOOP__DEV__ && warnInvalidSource(source)}// 省略if (cb && deep) {const baseGetter = getter // getter 为 () => sourcegetter = () => traverse(baseGetter())}// 省略// 定义 oldValue  isMultiSource 是否有多个源 [value1, value2] 需要监听let oldValue = isMultiSource ? [] : INITIAL_WATCHER_VALUE// job 核心逻辑const job: SchedulerJob = () => {if (!effect.active) {return}if (cb) {// watch(source, cb)const newValue = effect.run()if (deep ||forceTrigger ||(isMultiSource? (newValue as any[]).some((v, i) =>hasChanged(v, (oldValue as any[])[i])): hasChanged(newValue, oldValue)) ||(__COMPAT__ &&isArray(newValue) &&isCompatEnabled(DeprecationTypes.WATCH_ARRAY, instance))) {// cleanup before running cb againif (cleanup) {cleanup()}callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [newValue,// pass undefined as the old value when it's changed for the first timeoldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue,onCleanup])oldValue = newValue}} else {// watchEffecteffect.run()}}// important: mark the job as a watcher callback so that scheduler knows// it is allowed to self-trigger (#1727)job.allowRecurse = !!cblet scheduler: EffectSchedulerif (flush === 'sync') {scheduler = job as any // the scheduler function gets called directly} else if (flush === 'post') {scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)} else {// default: 'pre'scheduler = () => queuePreFlushCb(job) // 调度器赋值  也是核心逻辑}const effect = new ReactiveEffect(getter, scheduler)if (__DEV__) {effect.onTrack = onTrackeffect.onTrigger = onTrigger}// initial runif (cb) {if (immediate) {// 默认自动执行 watch 一次job() // job 触发意味着 watch 被立即执行一次} else {oldValue = effect.run() // 等于执行 fn 函数 即 () => traverse(baseGetter())  即 () => source 即 传入的监听数据}} else if (flush === 'post') {queuePostRenderEffect(effect.run.bind(effect),instance && instance.suspense)} else {effect.run()}return () => {effect.stop() // 监听停止if (instance && instance.scope) {remove(instance.scope.effects!, effect)}}
}

根据传入source监听数据类型不同走不同逻辑,当前sourcereactive类型,所以getter直接赋值为() => source,另外还可以看到类型为reactive时,默认开启深度监听 deep = true

由于存在cb监听回调和deep,所以baseGetter等于getter ,即() => sourcegetter赋值为() => traverse(baseGetter())

在这里插入图片描述

之后又定义了oldValue值,默认为空对象,也是回调函数中的oldValue接着定义了一个 job 函数,这是 watch 的核心逻辑,后面再分析

然后又创建了一个调度器scheduler ,在computed中提到过,在依赖触发时,会执行该方法。此时scheduler被赋值为() => queuePreFlushCb(job),将job函数传入到queuePreFlushCb方法中,该逻辑之后来分析

在这里插入图片描述

接着又创建了一个ReactiveEffect实例,将赋值后的getterscheduler传入

由于存在cb回调函数,根据判断配置中immediate存在时,就执行job方法,我们可以理解为job的触发watch被立即执行一次。否则执行effect.run即执行fn方法

在这里插入图片描述

当前fngetter() => traverse(baseGetter()),就是执行() => source,结果为传入的监听对象source

在这里插入图片描述

此时watch函数执行完毕,两秒后触发objsetter行为,依赖触发trigger执行,当前effects为:

在这里插入图片描述

之后再遍历执行每个 effect ,此时存在 scheduler 调度器,执行scheduler方法。当前scheduler为之前赋值的() => queuePreFlushCb(job),再来看下queuePreFlushCb方法,该方法定义在packages/runtime-core/src/scheduler.ts文件中:

export function queuePreFlushCb(cb: SchedulerJob) {queueCb(cb, activePreFlushCbs, pendingPreFlushCbs, preFlushIndex)
}

queueCb方法

实际执行的是queueCb方法:

在这里插入图片描述

该方法定义了一个 pendingQueue 队列数组,插入传入的cb回调即传入的job函数,执行queueFlush方法:

function queueFlush() {if (!isFlushing && !isFlushPending) {isFlushPending = truecurrentFlushPromise = resolvedPromise.then(flushJobs)}
}

可以看出watchjob执行都是一个微任务当前同步任务执行完毕后,执行微任务,之后执行flushJobs方法:

function flushJobs(seen?: CountMap) {isFlushPending = falseisFlushing = trueif (__DEV__) {seen = seen || new Map()}flushPreFlushCbs(seen)// Sort queue before flush.// This ensures that:// 1. Components are updated from parent to child. (because parent is always//    created before the child so its render effect will have smaller//    priority number)// 2. If a component is unmounted during a parent component's update,//    its update can be skipped.queue.sort((a, b) => getId(a) - getId(b))// conditional usage of checkRecursiveUpdate must be determined out of// try ... catch block since Rollup by default de-optimizes treeshaking// inside try-catch. This can leave all warning code unshaked. Although// they would get eventually shaken by a minifier like terser, some minifiers// would fail to do that (e.g. https://github.com/evanw/esbuild/issues/1610)const check = __DEV__? (job: SchedulerJob) => checkRecursiveUpdates(seen!, job): NOOPtry {for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {const job = queue[flushIndex]if (job && job.active !== false) {if (__DEV__ && check(job)) {continue}// console.log(`running:`, job.id)callWithErrorHandling(job, null, ErrorCodes.SCHEDULER)}}} finally {flushIndex = 0queue.length = 0flushPostFlushCbs(seen)isFlushing = falsecurrentFlushPromise = null// some postFlushCb queued jobs!// keep flushing until it drains.if (queue.length ||pendingPreFlushCbs.length ||pendingPostFlushCbs.length) {flushJobs(seen)}}
}

然后执行flushPreFlushCbs(seen)方法:

export function flushPreFlushCbs(seen?: CountMap,parentJob: SchedulerJob | null = null
) {if (pendingPreFlushCbs.length) {currentPreFlushParentJob = parentJob // job 函数activePreFlushCbs = [...new Set(pendingPreFlushCbs)] // 取代 pendingPreFlushCbspendingPreFlushCbs.length = 0 // 置空 下次不会再触发if (__DEV__) {seen = seen || new Map()}for (preFlushIndex = 0;preFlushIndex < activePreFlushCbs.length;preFlushIndex++) {if (__DEV__ &&checkRecursiveUpdates(seen!, activePreFlushCbs[preFlushIndex])) {continue}activePreFlushCbs[preFlushIndex]() // 当前 job 函数执行}activePreFlushCbs = nullpreFlushIndex = 0currentPreFlushParentJob = null// recursively flush until it drainsflushPreFlushCbs(seen, parentJob)}
}

当前pendingPreFlushCbs为传入的job方法,之后将去重后的pendingPreFlushCbs赋值给activePreFlushCbs,遍历执行activePreFlushCbspreFlushIndex,实际是执行每个job函数:

const job: SchedulerJob = () => {if (!effect.active) {return}if (cb) {// watch(source, cb)const newValue = effect.run()if (deep ||forceTrigger ||(isMultiSource? (newValue as any[]).some((v, i) =>hasChanged(v, (oldValue as any[])[i])): hasChanged(newValue, oldValue)) ||(__COMPAT__ &&isArray(newValue) &&isCompatEnabled(DeprecationTypes.WATCH_ARRAY, instance))) {// cleanup before running cb againif (cleanup) {cleanup()}callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [newValue,// pass undefined as the old value when it's changed for the first timeoldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue,onCleanup])oldValue = newValue}} else {// watchEffecteffect.run()}}

run方法执行实际执行getter() => traverse(baseGetter()),此时newValuecc

再看下traverse方法,它用于深度遍历对象的所有属性,确保对象的所有依赖都被追踪

export function traverse(value: unknown, seen?: Set<unknown>) {if (!isObject(value) || (value as any)[ReactiveFlags.SKIP]) {return value}seen = seen || new Set()if (seen.has(value)) {return value}seen.add(value)if (isRef(value)) {traverse(value.value, seen)} else if (isArray(value)) {for (let i = 0; i < value.length; i++) {traverse(value[i], seen)}} else if (isSet(value) || isMap(value)) {value.forEach((v: any) => {traverse(v, seen)})} else if (isPlainObject(value)) {for (const key in value) {traverse((value as any)[key], seen)}}return value
}

该方法由于值类型不同,会递归处理返回最终的值,接着执行callWithAsyncErrorHandling方法:

export function callWithAsyncErrorHandling(fn: Function | Function[],instance: ComponentInternalInstance | null,type: ErrorTypes,args?: unknown[]
): any[] {if (isFunction(fn)) {const res = callWithErrorHandling(fn, instance, type, args)if (res && isPromise(res)) {res.catch(err => {handleError(err, instance, type)})}return res}const values = []for (let i = 0; i < fn.length; i++) {values.push(callWithAsyncErrorHandling(fn[i], instance, type, args))}return values
}export function callWithErrorHandling(fn: Function,instance: ComponentInternalInstance | null,type: ErrorTypes,args?: unknown[]
) {let res// 统一处理监听 错误try {res = args ? fn(...args) : fn()} catch (err) {handleError(err, instance, type)}return res
}

执行了cb回调函数即watch传入的匿名函数,callWithAsyncErrorHandling主要是对错误统一监听处理,最后将 newValue 赋值给 oldValuewatch至此执行完毕

在这里插入图片描述

总结

1)watch函数实际执行的是doWatch方法,调度器schedulerwatch中很关键

2)schedulerReactiveEffect两者之间存在互相作用的关系,一旦effect触发了scheduler,那么会导致queuePreFlushCb(job)执行,job 方法就被塞入微任务的队列中

3)只要job()触发,那么就表示watch触发了一次

http://www.dtcms.com/wzjs/563164.html

相关文章:

  • 有哪些网站可以做青旅义工广东网站建设服务
  • 网站收录查询系统wordpress主题基本文件配置
  • 如皋市建设局网站在哪百度最新财报
  • 淄博网站制作定制升级discuz论坛门户网站模板
  • 郑州网站建设公司锐客科技东莞诚信通代运营
  • 怎么开个网站导航网站 wordpress
  • 建设银行大冶支行网站百度链接插件 wordpress
  • 项目招商网站大全dede 友情链接 网站简况 调用
  • 青岛团购网站建设在linux系统上用什么做网站
  • 安徽网站建设获客企业wordpress主题编辑
  • 房地产公司网站模板做破解网站合法
  • 路由器做php网站吗惠州有做网站的吗
  • 友情链接如何选择网站福田公司旗下子公司
  • 怎么进入网站空间如何做好推广
  • 专门做高端网站设计的云华设计重庆 seo
  • 网站建设需要保存什么网站网站制作服务
  • 网站开发文档教学宜昌网站开发公司
  • iis添加网站主机名律师事务所手机网站
  • 金昌大型网站建设费用页面设计在哪打开
  • 深圳网站定制公司dedecms 网站还原数据之后 乱码
  • 北京网站建设公司华网天下下wordpress导入数据ftp
  • 合肥网站开发需要多前端开源项目网站
  • 免费的图片做视频在线观看网站如何让网站显示404
  • 响应式网站实例人才网招聘网招聘
  • 西安建设网站的公司简介系统开发步骤
  • 什么网站可以做软件网站程序怎么备份
  • 网页与网站的区别与联系是什么营销网站建设哪里便宜
  • 有些中小网站汕尾网站建设 生意好吗
  • 网站建设新报价图片欣赏什么是搜索引擎优化的核心
  • 空投注册送币网站怎么做网站改版 收录