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 setterif (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 = expOrFnupdateComponent = function () { vm._update(vm._render(), hydrating);};
推荐阅读
Vue 在挂载数据前都经历了什么?
原创文章,作者:小道研究,如若转载,请注明出处:https://www.sudun.com/ask/34554.html