发布于2021-05-30 12:11 阅读(1736) 评论(0) 点赞(3) 收藏(4)
第一篇,新建watcher对象时运行的getter函数,会调用data属性的get修饰器从而触发dep.depend()函数,完成了watcher与dep依赖的收集。让watcher和data建立了联系。那么这一次,让我们看一下依赖收集完成之后,是如何完成依赖更新的。
让我们从还是在initData() 方法里面的defineReactive()方法再次开始吧!
export function defineReactive (obj: Object, key: string, val: any, customSetter?: Function) {
let childOb = observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
...
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = observe(newVal)
dep.notify() // <-- 依赖更新的重点
}
})
}
这次让我们看一下set修饰器的作用,在代码中出现 this.XXX = XXX
这样的语句时。set修饰器的方法就会被触发。首先通过getter得到旧值,判断旧值与新值是否相同,如果相同就不触发更新。然后使用新值替换旧值,再次递归观察新值,最后调用 dep.notify()
通知监听器依赖更新了。
export default class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
// 通知所有监听的watcher更新
notify () {
// stabilize the subscriber list first
const subs = this.subs.slice() // 浅复制subs数组
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
export default class Watcher {
update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) { // 默认值是false
this.run()
} else {
queueWatcher(this) // <-- 会加入watcher更新队列
}
}
}
这个notify()函数很简单,就是运行了subs数组里面的每个Watcher对象的update()函数,调用监听自己属性的所以监听器的更新函数。
而这个update()函数也很简单,根据是否同步走不同的方法,一般sync的默认值都是false,所以我们会走queueWatcher()函数。当然run()函数迟早也会运行的。所以我们就会看一下run()函数。
run () {
if (this.active) {
const value = this.get()
if (
value !== this.value ||
// Deep watchers and watchers on Object/Arrays should fire even
// when the value is the same, because the value may
// have mutated.
isObject(value) ||
this.deep
) {
// set new value
const oldValue = this.value
this.value = value
if (this.user) {
try {
this.cb.call(this.vm, value, oldValue)
} catch (e) {
handleError(e, this.vm, `callback for watcher "${this.expression}"`)
}
} else {
this.cb.call(this.vm, value, oldValue)
}
}
}
}
我们看到 run()
函数首先重新运行了this.get()函数得到了新的watcher监听的值。然后调用this.cb函数将Watcher的新值和旧值都传了进去。这个cb函数就是我们自己定义的监听函数,所以我们在写的时候可以使用新值和旧值去写一些我们自己的逻辑。
const queue: Array<Watcher> = []
export function queueWatcher (watcher: Watcher) {
const id = watcher.id
if (has[id] == null) {
has[id] = true
if (!flushing) {
queue.push(watcher)
} else {
// if already flushing, splice the watcher based on its id
// if already past its id, it will be run next immediately.
let i = queue.length - 1
while (i >= 0 && queue[i].id > watcher.id) {
i--
}
queue.splice(Math.max(i, index) + 1, 0, watcher)
}
// queue the flush
if (!waiting) {
waiting = true
nextTick(flushSchedulerQueue)
}
}
}
queueWatcher() 函数,如果没有在更新,则直接把watcher对象放入队列里面就行了。但是如果正在更新,上次我们讲过,watcher的更新顺序很重要,因为在watcher之间也存在依赖关系,所以后面的watcher是有可能依赖前面的watcher的。而决定watcher前后的就是id,所以使用在队列中比较id的大小,插入到适合的位置。
最后,调用flushSchedulerQueue函数以一定的周期(nextTick)更新队列。
function flushSchedulerQueue () {
flushing = true
let watcher, id, vm
queue.sort((a, b) => a.id - b.id) // 1、重新排序
for (index = 0; index < queue.length; index++) {
watcher = queue[index]
id = watcher.id
has[id] = null
watcher.run() // 2、运行更新方法
if (process.env.NODE_ENV !== 'production' && has[id] != null) {
circular[id] = (circular[id] || 0) + 1
if (circular[id] > config._maxUpdateCount) { // 3、判断是否触发循环更新
warn(
'You may have an infinite update loop ' + (
watcher.user
? `in watcher with expression "${watcher.expression}"`
: `in a component render function.`
),
watcher.vm
)
break
}
}
}
// reset scheduler before updated hook called
const oldQueue = queue.slice()
resetSchedulerState() // 4、重置更新队列
// call updated hooks
index = oldQueue.length
while (index--) {
watcher = oldQueue[index]
vm = watcher.vm
if (vm._watcher === watcher && vm._isMounted) {
callHook(vm, 'updated') // 5、调用updated钩子
}
}
}
function resetSchedulerState () {
queue.length = 0
has = {}
if (process.env.NODE_ENV !== 'production') {
circular = {}
}
waiting = flushing = false
}
更新队列的 flushSchedulerQueue
函数一共有5步。分别是先对watcher进行排序,然后循环调用watcher的run()函数,完成更新。同时判断是否循环更新, config._maxUpdateCount
的值是100,接着更新完之后,会重置更新队列。最后判断 更新的watcher里面是否有视图的watcher对象,如果有调用 updated
钩子。
作者:Jjxj
链接:http://www.qianduanheidong.com/blog/article/116088/5b5e279f4962454853e2/
来源:前端黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 前端黑洞网 All Rights Reserved 版权所有,并保留所有权利。 京ICP备18063182号-3
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!