¿Por qué el procesamiento asíncrono?
Porque si no se utiliza actualización asíncrona, a continuación, los datos se actualizan cada vez que la componente de corriente es re-emitida, lo que probablemente sería una gran cantidad de re-dom arroyo o vuelve a dibujar, por lo que con el fin de considerar el rendimiento y reducir los datos del navegador en cada actualización Vue Dom apareció por encima, Vue será después de la actual ronda de actualizaciones de datos, vaya a actualizar de forma asíncrona la vista!
src / core / observador / index.js
método setter definido en defineReactive (), esta vez no () método dep.notify
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
let childOb = !shallow && observe(val) // 递归观测
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () { // 数据的取值
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend() // 收集依赖 watcher
if (childOb) {
childOb.dep.depend() // 收集依赖
if (Array.isArray(value)) {
dependArray(value)
}
}
}
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()
}
// #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() // 触发数据对应的依赖进行更新
}
})
}
Luego, busquen en notify()
el método de
src / core / observador / dep.js
export default class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor () {
this.id = uid++
this.subs = []
}
addSub (sub: Watcher) {
this.subs.push(sub)
}
removeSub (sub: Watcher) {
remove(this.subs, sub)
}
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
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() // 依赖中的update方法
}
}
}
Esto se debe principalmente a hacer es notificar a la operación de actualización observador, sino también una operación de ordenación, y, finalmente, a su vez llama la actualización de cada observador () método,
A continuación, busque en este update ()
método
src / core / observador / watcher.js
export default class Watcher {
/**
* Subscriber interface.
* Will be called when a dependency changes.
*/
update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) { // 同步watcher
this.run()
} else {
queueWatcher(this) //
}
}
}
Aquí es una queueWatcher() 方法
src / core / observador / scheduler.js
/**
* Push a watcher into the watcher queue.
* Jobs with duplicate IDs will be skipped unless it's
* pushed when the queue is being flushed.
*/
export function queueWatcher (watcher: Watcher) {
const id = watcher.id // 过滤watcher 多个属性依赖同一个watcher
if (has[id] == null) {
has[id] = true
if (!flushing) {
queue.push(watcher) // 将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)
}
}
}
Dentro de este método hace dos cosas
- Mediante la adición de un atributo id a cada observador, es decir, operación de eliminación de datos duplicados, para filtrar la pluralidad de atributo dependiente mismo observador observador
- queue.push (observador), el observador en la cola
- Método última llamada nextTick (), para dejar en claro las operaciones asincrónicas de cola observador
Este método puede ser un maullido (flushSchedulerQueue), una función de orden superior usando interna
watcher.before ()
se lleva a cabo el método realmente ejecutar
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() // before方法
}
id = watcher.id
has[id] = null
watcher.run() // 执行watcher
// 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
}
}
}
// keep copies of post queues before resetting state
const activatedQueue = activatedChildren.slice()
const updatedQueue = queue.slice()
resetSchedulerState()
// call component updated and activated hooks
callActivatedHooks(activatedQueue)
callUpdatedHooks(updatedQueue) // 更新后调用的 updated
// devtool hook
/* istanbul ignore if */
if (devtools && config.devtools) {
devtools.emit('flush')
}
}
Bajo el principio de breve resumen:
- Llamar para notificar () para informar a las operaciones de actualización del vigilante
- El método de actualización a su vez llama observador
- operación Deduplication se realiza en el observador (por id), en la cola de
- Después de la aplicación de la cola asíncrona vaciar, nextTick (flushSchedulerQueue) la operación de actualización masiva