Vue 通过 Object.defineProperty 重写了 get 方法,实现了「依赖收集」,又通过 set 方法来实现了「派发更新」。
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
set: function reactiveSetter (newVal) {
var 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();
}
// #7981: for accessor properties without setter
if (getter && !setter) { return }
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();
}
});
changeTextFn(){
this.text = Math.random();
}
dep.notify();
src/core/observer/dep.js // 文件路径
// notify 方法
notify () {
// stabilize the subscriber list first
const subs = this.subs.slice()
if (process.env.NODE_ENV !== \\\'production\\\' && !config.async) {
// subs aren\\\'t sorted in scheduler if not running async
// we need to sort them now to make sure they fire in correct
// order
subs.sort((a, b) => a.id - b.id)
}
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
src/core/observer/watcher.js // 文件定义位置
// update 方法
update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
queueWatcher(this)
}
}
src/core/observer/scheduler.js // 文件定义位置
// queueWatcher 方法
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 > index && queue[i].id > watcher.id) {
i--
}
queue.splice(i + 1, 0, watcher)
}
// queue the flush
if (!waiting) {
waiting = true
if (process.env.NODE_ENV !== \\\'production\\\' && !config.async) {
flushSchedulerQueue()
return
}
nextTick(flushSchedulerQueue)
}
}
}
function flushSchedulerQueue () {
currentFlushTimestamp = getNow()
flushing = true
let watcher, id
// Sort queue before flush.
// This ensures that:
// 1. Components are updated from parent to child. (because parent is always
// created before the child)
// 2. A component\\\'s user watchers are run before its render watcher (because
// user watchers are created before the render watcher)
// 3. If a component is destroyed during a parent component\\\'s watcher run,
// its watchers can be skipped.
queue.sort((a, b) => a.id - b.id)
// do not cache length because more watchers might be pushed
// as we run existing watchers
for (index = 0; index < queue.length; index++) {
watcher = queue[index]
if (watcher.before) {
watcher.before()
}
id = watcher.id
has[id] = null
watcher.run()
// in dev build, check and stop circular updates.
if (process.env.NODE_ENV !== \\\'production\\\' && has[id] != null) {
circular[id] = (circular[id] || 0) + 1
if (circular[id] > MAX_UPDATE_COUNT) {
warn(
\\\'You may have an infinite update loop \\\' + (
watcher.user
? `in watcher with expression \\\"${watcher.expression}\\\"`
: `in a component render function.`
),
watcher.vm
)
break
}
}
}
}
queue.sort((a, b) => a.id - b.id)
value = this.getter.call(vm, vm)
new Watcher(vm, updateComponent, noop, {
before: function before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, \\\'beforeUpdate\\\');
}
}
}, true /* isRenderWatcher */);
// Watcher 方法
export default class Watcher{
constructor (
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
)
}
this.getter = expOrFn
updateComponent = function () {
vm._update(vm._render(), hydrating);
};
推荐阅读
Vue 在挂载数据前都经历了什么?
原创文章,作者:小道研究,如若转载,请注明出处:https://www.sudun.com/ask/34554.html