1、执行下面代码
packages\vue\examples\composition\test.html
<script src="../../dist/vue.global.js"></script>
<div id="demo">
<h1>
<header>{{count}}</header>
</h1>
</div>
<script>
const { createApp, ref, toRefs, reactive, watch, onMounted, computed } = Vue
var app = createApp({
setup() {
var count = ref(1)
setTimeout(() => {
count.value++
}, 1000);
watch(()=>count.value,(x,y)=>{
debugger
console.log(x,y);
})
return { count }
}
})
app.mount('#demo')
</script>
2、代码分析
packages\runtime-core\src\apiWatch.ts
setupComponent时,执行setup函数,执行watch函数- 进一步执行
doWatch函数 - 获取用户传的
getter,和cb函数 - 构建job函数
const newValue = effect.run()- 如果
hasChanged(newValue, oldValue))为true,则 - 执行
cb函数,且oldValue = newValue
const effect = new ReactiveEffect(getter, () => { queuePreFlushCb( job ) })- 首次执行
oldValue = effect.run() - 触发上面获取的getter函数,于是触发了响应式数据的track函数取关联当前的effect
- 当响应式数据发生变化时,触发函数() => { queuePreFlushCb( job ) }
function watch(
source,
cb,
options
){
return doWatch(source, cb, options)
}
function doWatch(
source,
cb,
{ immediate, deep, flush, onTrack, onTrigger }
){
const instance = currentInstance
let getter
if (isFunction(source)) {
getter = () => callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
}
let oldValue = isMultiSource ? [] : INITIAL_WATCHER_VALUE
const job = () => {
const newValue = effect.run()
if ( hasChanged(newValue, oldValue)) ) {
callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
newValue,
oldValue
])
oldValue = newValue
}
}
job.allowRecurse = !!cb
const effect = new ReactiveEffect(getter, () => { queuePreFlushCb( job ) })
if (immediate) {
job()
} else {
oldValue = effect.run()
}
return () => {
effect.stop()
if (instance && instance.scope) {
remove(instance.scope.effects, effect)
}
}
}
export function queuePreFlushCb(cb) {
queueCb(cb, activePreFlushCbs, pendingPreFlushCbs, preFlushIndex)
}
function queueCb(
cb,
activeQueue,
pendingQueue,
index
) {
if (!isArray(cb)) {
if (
!activeQueue ||
!activeQueue.includes(cb, cb.allowRecurse ? index + 1 : index)
) {
pendingQueue.push(cb)
}
} else {
pendingQueue.push(...cb)
}
queueFlush()
}
function queueFlush() {
if (!isFlushing && !isFlushPending) {
isFlushPending = true
currentFlushPromise = resolvedPromise.then(flushJobs)
}
}
const resolvedPromise = Promise.resolve()
function flushJobs(seen) {
isFlushPending = false
isFlushing = true
flushPreFlushCbs(seen)
queue.sort((a, b) => getId(a) - getId(b))
const check = NOOP
try {
for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {
const job = queue[flushIndex]
if (job && job.active !== false) {
callWithErrorHandling(job, null, ErrorCodes.SCHEDULER)
}
}
} finally {
flushIndex = 0
queue.length = 0
flushPostFlushCbs(seen)
isFlushing = false
currentFlushPromise = null
if (
queue.length ||
pendingPreFlushCbs.length ||
pendingPostFlushCbs.length
) {
flushJobs(seen)
}
}
}