Capítulo 11 del principio de respuesta al aprendizaje del código fuente VUE (suscripción)

1. Descripción general

     En el capítulo anterior, la clase de observación y el editor están establecidos por las propiedades definidas por prop y datos, pero la subclase está vacía en la clase de lanzamiento Dep en este momento. Cómo registrar el observador y actualizarlo cuando la propiedad cambia. Este capítulo continuará presentando la suscripción a Watcher.

Antes de presentarnos, pensemos primero, ¿en qué métodos o expresiones de accesorios y datos definidos por el usuario necesitamos implementar cambios receptivos?

En primer lugar, se necesita un cálculo definido por el usuario, calculado, y nuestra vista también utilizará muchas expresiones de atributos (como el ejemplo anterior {{item.id}}), que también es necesario. Aunque estos datos se utilizan en el método, todos se llaman a tiempo, por lo que no son necesarios.

De hecho, el código fuente también crea observadores en estos tres escenarios. Si leí mi blog anterior, los introduje brevemente en initWatcher, initComputer (Parte 5) y mount (Parte 6), y obtuve algunos conocimientos. .

Los tres observadores son observadores de usuarios, observadores de computación y observadores de procesamiento. Este artículo combina los conocimientos previos e introduce los principios de realización de estos observadores en detalle.

二 、 usuario observador

Utilizamos el siguiente reloj como ejemplo en ejecución:

 data:{
      msg:'this is msg',
      items:[
      {id:1},
      {id:2},
      {id:3}
      ]
    }
watch: {
    msg: function (val, oldVal) {
      console.log('new: %s, old: %s', val, oldVal)
    },
    ....
}

Revise la quinta parte del proceso de creación de vigilantes initWatcher, de la siguiente manera:

  Vue.prototype.$watch = function (
    expOrFn: string | Function,
    cb: any,
    options?: Object
  ): Function {
    ...
    //创建watcher对象,进行监听
    const watcher = new Watcher(vm, expOrFn, cb, options)
    ...
  }

 

Los principales parámetros de entrada son vm, que es el objeto vue; expOrFn, la expresión a observar, que es el objeto msg de atributo en este ejemplo; cb, la función de devolución de llamada, que es el método correspondiente del atributo msg en este ejemplo. La definición de vigilante se encuentra en src / core / observer / watcher.js.

  export default class Watcher {
  vm: Component;
  expression: string;
  cb: Function;
  id: number;
  deep: boolean;
  user: boolean;
  computed: boolean;
  sync: boolean;
  dirty: boolean;
  active: boolean;
  dep: Dep;
  deps: Array<Dep>;
  newDeps: Array<Dep>;
  depIds: SimpleSet;
  newDepIds: SimpleSet;
  before: ?Function;
  getter: Function;
  value: any;

  constructor (
    vm: Component,//组件对象
    expOrFn: string | Function,//待观察的表达式
    cb: Function,//回调函数,更新的时候调用
    options?: ?Object,
    isRenderWatcher?: boolean
  ) {
    //1、初始化变量
    this.vm = vm
    if (isRenderWatcher) {
      vm._watcher = this
    }
    vm._watchers.push(this)
    // options
    if (options) {
      this.deep = !!options.deep
      this.user = !!options.user
      this.computed = !!options.computed
      this.sync = !!options.sync
      this.before = options.before
    } else {
      this.deep = this.user = this.computed = this.sync = false
    }
    this.cb = cb
    this.id = ++uid // uid for batching
    this.active = true
    this.dirty = this.computed // for computed watchers
    this.deps = []
    this.newDeps = []
    this.depIds = new Set()
    this.newDepIds = new Set()
    this.expression = process.env.NODE_ENV !== 'production'
      ? expOrFn.toString()
      : ''
    // parse expression for getter
    //2、解析表达式,获取getter方法
    if (typeof expOrFn === 'function') {
      this.getter = expOrFn
    } else {
      this.getter = parsePath(expOrFn)
      if (!this.getter) {
        this.getter = function () {}
        process.env.NODE_ENV !== 'production' && warn(
          `Failed watching path: "${expOrFn}" ` +
          'Watcher only accepts simple dot-delimited paths. ' +
          'For full control, use a function instead.',
          vm
        )
      }
    }
    //3、依赖收集
    if (this.computed) {
      //对于计算属性watcher,此时没有立即进行依赖收集,在执行render函数时收集
      this.value = undefined
      this.dep = new Dep()
    } else {
      //对于普通watcher,调用get方法,进行依赖收集
      this.value = this.get()
    }
  }
...
}

1. Método de construcción

Veamos primero su método de construcción. El proceso principal incluye:

(1) Inicializar variables. Las principales variables son las siguientes:

profundo indica si se debe monitorear profundamente, es decir, variables con atributos anidados.

calculado, indicando si es un atributo calculado.

sincronización, indica si se debe realizar la sincronización al actualizar.

deps, newDeps, la matriz de editor mantenida correspondiente a este observador.

(2). Analice la expresión y obtenga el método getter. Si expOrFn es el tipo de función, configúrelo directamente al método getter. De lo contrario, llame al método parsePath para devolver el objeto de atributo como método getter. En este caso, este último se utiliza y el resultado de la ejecución es vm msg].

export function parsePath (path: string): any {
  ...
  return function (obj) {
    for (let i = 0; i < segments.length; i++) {
      if (!obj) return
      obj = obj[segments[i]]
    }
    return obj
  }
}

(3), colección dependiente, esta parte se maneja de manera muy inteligente, para los atributos no calculados, el método get se llama directamente (para los observadores de los atributos calculados, estaremos en la tabla). Continúa mirando el método get.

2. El método get

 get () {
    //1、将当前的watcher压栈
    pushTarget(this)
    let value
    const vm = this.vm
    try {
      //2、核心代码,依赖收集
      value = this.getter.call(vm, vm)
    } catch (e) {
      if (this.user) {
        handleError(e, vm, `getter for watcher "${this.expression}"`)
      } else {
        throw e
      }
    } finally {
      // "touch" every property so they are all tracked as
      // dependencies for deep watching
      //3、后置处理
      if (this.deep) {
        traverse(value)
      }
      popTarget()
      this.cleanupDeps()
    }
    return value
  }

(1) Empuje su propio objeto observador en la pila y establezca la variable global Dep.target como el objeto observador actual.

export function pushTarget (_target: ?Watcher) {
  if (Dep.target) targetStack.push(Dep.target)
  Dep.target = _target
}

(2) Ejecute el método getter para activar el secuestro de este atributo, revisamos el método de secuestro definido en el capítulo anterior.

Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      //依赖收集
      if (Dep.target) {
        dep.depend()
        if (childOb) {
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      return value
    }
...

La variable global Dep.target representa el objeto observador actual, que no está vacío, continúe llamando al método Dep de la clase Dep

depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }

Finalmente, se volvió a llamar al método addDep en el observador,

addDep (dep: Dep) {
    const id = dep.id
    if (!this.newDepIds.has(id)) {
      //加入到watcher的dep数组
      this.newDepIds.add(id)
      this.newDeps.push(dep)
      if (!this.depIds.has(id)) {
        //加入到dep的sub数组
        dep.addSub(this)
      }
    }
  }

Este método primero guarda el dep en la matriz newDeps del observador, luego llama a addSub de Dep para agregar el objeto observador a la matriz secundaria.

  addSub (sub: Watcher) {
    this.subs.push(sub)
  }

Todo el proceso de llamada es relativamente redondo, con el propósito de establecer una lista doblemente vinculada entre dep y observador.

(3) El procesamiento posterior, incluido el procesamiento profundo, borra el objeto observador actual, etc.

Una vez que se completa todo el proceso, el objeto observador se agrega al sub del objeto Dep del atributo msg. El modelo de dependencia de msg es el siguiente:

三 、 compute watcher

El siguiente atributo de cálculo computeMsg es un ejemplo en ejecución:

<div id="app">
   {{computeMsg}}
  ...
</div>

var vm = new Vue({
data:{
      msg:'this is msg',
      items:[
      {id:1},
      {id:2},
      {id:3}
      ]
    }
watch: {
    msg: function (val, oldVal) {
      console.log('new: %s, old: %s', val, oldVal)
    },
computed:{
    computeMsg:function(){
      return "this is computed msg:"+this.msg
    }
  }
    ....
}

   En este ejemplo, el atributo de cálculo computeMsg se crea y se usa en la plantilla. El atributo msg se llama en la expresión de atributo de cálculo computeMsg.

Repasemos el método initComputed en el quinto capítulo.

function initComputed (vm: Component, computed: Object) {
  // $flow-disable-line
  const watchers = vm._computedWatchers = Object.create(null)
  // computed properties are just getters during SSR
  const isSSR = isServerRendering()
  //1、循环计算属性
  for (const key in computed) {
    const userDef = computed[key]
    const getter = typeof userDef === 'function' ? userDef : userDef.get
    ...

    if (!isSSR) {
      // create internal watcher for the computed property.
      //2、为每个属性创建watcher
      watchers[key] = new Watcher(
        vm,
        getter || noop,
        noop,
        computedWatcherOptions
      )
    }
     
    ...
  //3、劫持数据变化,创建监听方法
    defineComputed(vm, key, userDef)
  }
}

export function defineComputed (
  target: any,
  key: string,
  userDef: Object | Function
) {
 const shouldCache = !isServerRendering()
  if (typeof userDef === 'function') {
    sharedPropertyDefinition.get = shouldCache
      ? createComputedGetter(key)
      : userDef
    sharedPropertyDefinition.set = noop
  } else {
    ....
  }
  //4、并对计算属性的getter和setter进行劫持
  Object.defineProperty(target, key, sharedPropertyDefinition)
}
}

//getter方法劫持
function createComputedGetter (key) {
  return function computedGetter () {
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
      //依赖收集,将订阅类添加到
      watcher.depend()
      //返回值
      return watcher.evaluate()
    }
  }
}

En este método, los atributos calculados definidos se enlazan, se crea un observador para cada atributo calculado y se establece un método getter para monitorear los cambios de datos (el método setter para los atributos calculados se usa menos). Los principales parámetros de entrada del observador: vm, que es vue object; getter, la expresión del atributo calculado, que es la expresión correspondiente a computeMsg en este ejemplo. Continúe observando el método de construcción del observador.

1. Constructor

constructor (
    vm: Component,//组件对象
    expOrFn: string | Function,//待观察的表达式
    cb: Function,//回调函数
    options?: ?Object,
    isRenderWatcher?: boolean
  ) {
    ...
    // parse expression for getter
    //1、对于计算属性,表达式即为getter
    if (typeof expOrFn === 'function') {
      this.getter = expOrFn
    } else {
      this.getter = parsePath(expOrFn)
      if (!this.getter) {
        this.getter = function () {}
        process.env.NODE_ENV !== 'production' && warn(
          `Failed watching path: "${expOrFn}" ` +
          'Watcher only accepts simple dot-delimited paths. ' +
          'For full control, use a function instead.',
          vm
        )
      }
    }
    
    if (this.computed) {
      //2、对于计算属性,创建了dep,此时没有立即进行依赖收集,
      this.value = undefined
      this.dep = new Dep()
    } else {
      this.value = this.get()
    }
  }

El parámetro de entrada del observador, vm representa el objeto vue, y el captador es la expresión a observar, es decir, la función de atributo de cálculo.

En comparación con el observador del usuario, hay dos lugares diferentes.

(1) Dado que expOrFn es una expresión para calcular atributos, el tipo es función, así que tome una rama y configure el captador en la expresión para calcular atributos.

(2). El método get no se llama para la recopilación de dependencias, pero se crea el objeto dep y el objeto guarda el observador que depende del atributo calculado.

La dependencia del atributo calculado es diferente del atributo ordinario, depende del atributo ordinario contenido en su expresión, como el atributo msg en este ejemplo, y también depende de otros llamadores, como la expresión de plantilla llamada computeMsg en este ejemplo. ({{ComputeMsg}}). El modelo de dependencia para este ejemplo es el siguiente:

Entonces surge la pregunta, ya que estas dependencias no se recopilan en el momento de la definición, ¿cuándo se hizo? La respuesta es cuando se llama a la plantilla.

La intención original del atributo de cálculo es simplificar la expresión de la plantilla y evitar demasiada lógica que conduzca a la complejidad de la plantilla. Por lo tanto, la dependencia se activará solo cuando se llame a la plantilla.Si solo no se llama a la definición, la dependencia causará desperdicio.

Presentemos el observador (observador de procesamiento) relacionado con el procesamiento de la plantilla y veamos cómo completar la colección de dependencias marcadas en rojo.

四 、 render observador

Primero revisamos el sexto capítulo que presenta el método mountComponent al montar.

export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  ....
   //2、定义updateComponent,vm._render将render表达式转化为vnode,vm._update将vnode渲染成实际的dom节点
    updateComponent = () => {
      vm._update(vm._render(), hydrating)
    }
   //3、首次渲染,并监听数据变化,并实现dom的更新
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher */)
  ....
}

En ese momento, dijimos que este observador tiene dos funciones: 1. Realizar la primera representación de dom y completar la colección de dependencias. 2. Monitoree los cambios en las expresiones contenidas en la plantilla para implementar la actualización.

El observador es el "puente" de la plantilla de "procesamiento" de datos, llamado observador de procesamiento, y su parte central se refleja en la expresión del parámetro de entrada (el segundo parámetro, updateComponent). Echemos un vistazo al proceso principal de inicialización del observador:

constructor (
    vm: Component,//组件对象
    expOrFn  : string | Function,//待观察的表达式
    cb: Function,//回调函数
    options?: ?Object,
    isRenderWatcher?: boolean
  ) {
   ....
    if (typeof expOrFn === 'function') {//1、设置getter方法为表达式
      this.getter = expOrFn
    } else {
      .....
    }
    
    if (this.computed) {
     ....
    } else {
      //2、执行get方法,进行依赖收集
      this.value = this.get()
    }
  }

1. expOrFn es función, por lo tanto, establezca el método getter en expresión, que es el método updateComponent.

2. Como no es un atributo calculado, como el observador del usuario, la ejecución del método get realmente ejecuta el método updateComponent.

updateComponent = () => {
      vm._update(vm._render(), hydrating)
    }

Este método incluye dos etapas de ejecución, vm._render () y vm._update. Lo analizamos en el Capítulo 6 y no lo presentaremos en detalle. Hoy nos enfocamos en responder las preguntas planteadas anteriormente, cómo implementar la colección de dependencias.

Tomando el atributo calculado anterior como ejemplo, en la plantilla, usamos "{{computeMsg}}".

<div id="app">
   {{computeMsg}}
  ...
</div>

Después de compilar la plantilla (consulte la sección de compilación anterior), la expresión de representación "_s (computeMsg)" en esta sección activará el método getter establecido por computeMsg cuando se ejecuta vm._render.

function createComputedGetter (key) {
  return function computedGetter () {
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
      //1、依赖收集,将订阅类添加到dep中
      watcher.depend()
      //2、返回值
      return watcher.evaluate()
    }
  }
}

1. Llame a watcher.depend para agregar el objeto actual del observador de renderización al dep de compteMsg para completar la colección de dependencias.

 depend () {
    if (this.dep && Dep.target) {
      this.dep.depend()
    }
  }

Tenga en cuenta que Dep.target aquí se refiere al observador de renderizado. El modelo de dependencia es el siguiente:

2. Llame a watcher.evaluate ().

  evaluate () {
    //如果有更新,则重新计算,否则返回缓存值
    if (this.dirty) {
      this.value = this.get()
      this.dirty = false
    }
    return this.value
  }

Esto es esto se refiere al observador de cómputo. El método get es una expresión de atributo.

function(){
      return "this is computed msg:"+this.msg
    }

Debido a que el atributo msg se incluye en la expresión, durante el proceso de ejecución, se activa el método de monitoreo de obtención de msg y el observador de cómputo se agrega al depósito de msg para completar la recopilación de las dependencias. El modelo de dependencia final es el siguiente:

Hasta ahora, se recopilan las dependencias y dependencias de computeMsg. También puede intentar analizar el proceso de recopilación de dependencias de invocar atributos comunes en la plantilla.

5. Distribuir actualizaciones

Actualmente hay dos objetos observadores dependientes recopilados en el editor dep del atributo msg. ¿Qué sucede cuando restablecemos el valor msg?

Cuando se restablece el valor de msg, se activa el método establecido.

Object.defineProperty(obj, key, {
    ...
    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 = !shallow && observe(newVal)
      dep.notify()
    }
  })

Llame al método de notificación de su editor.

//通知相关的watcher类更新
  notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }

La clase de observador asociada con el atributo de bucle se conoce por lo anterior. En este momento, hay dos objetos en la subcolección de dep: observador de usuario y observador calculado. Llame a sus métodos de actualización, respectivamente.

 update () {
    /* istanbul ignore else */
    if (this.computed) {//计算属性watcher处理
      // A computed property watcher has two modes: lazy and activated.
      // It initializes as lazy by default, and only becomes activated when
      // it is depended on by at least one subscriber, which is typically
      // another computed property or a component's render function.
      //当没有订阅计算属性时,不需要计算,仅仅设置dirty为true,表示下次重新计算
      if (this.dep.subs.length === 0) {
        // In lazy mode, we don't want to perform computations until necessary,
        // so we simply mark the watcher as dirty. The actual computation is
        // performed just-in-time in this.evaluate() when the computed property
        // is accessed.
        this.dirty = true
      } else {
        // In activated mode, we want to proactively perform the computation
        // but only notify our subscribers when the value has indeed changed.
        this.getAndInvoke(() => {
          this.dep.notify()
        })
      }
    } else if (this.sync) {//同步处理,立即执行
      this.run()
    } else {//异步处理,进入堆栈
      queueWatcher(this)
    }
  }

La actualización se divide en tres ramas, y analizamos las tres situaciones por separado.

(1) Calcular el procesamiento de atributos.

(2), procesamiento sincrónico, ejecutado inmediatamente.

(3), procesamiento asincrónico, agregado a la pila.

1. Calcular atributos

Para el objeto observador del atributo calculado, determine si el atributo calculado se ha suscrito. Si no (es decir, si el subconjunto del dep del atributo calculado está vacío), establezca sucio en verdadero y vuelva a calcular la próxima vez.

Como en este ejemplo, computeMsg depende del observador de renderizado, por lo que ingresará a la rama else y ejecutará el método getAndInvoke.

 this.getAndInvoke(() => {
          this.dep.notify()
        })

getAndInvoke (cb: Function) {
    //获取最新的值
    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) ||//object对象
      this.deep//深度watcher
    ) {
      // set new value
      //更新值
      const oldValue = this.value
      this.value = value
      this.dirty = false
      //执行回调函数
      if (this.user) {
        try {
          cb.call(this.vm, value, oldValue)
        } catch (e) {
          handleError(e, this.vm, `callback for watcher "${this.expression}"`)
        }
      } else {
        cb.call(this.vm, value, oldValue)
      }
    }
  }

La parte central de este método es ejecutar el método cb de devolución de llamada entrante, es decir, this.dep.notify (). En este momento, la actualización del renderizador se llama de forma recursiva y se devuelve el método de actualización.

2. Procesamiento sincrónico

Si no es un atributo calculado (observador de usuario, observador de representación en este ejemplo), y se establece el proceso de sincronización, se llama al método de ejecución. Tales como:

 run () {
    if (this.active) {
      this.getAndInvoke(this.cb)
    }
  }

El método de ejecución llama a getAndInvoke. La parte central del método es:

(1) Ejecute el método get para obtener el valor. Para el observador de renderizado, ejecute el método updateComponent, regenere el Vnode y aplique un parche para actualizar el dom (el siguiente capítulo lo explicará en detalle).

(2) Método de devolución de llamada cb, como la expresión watch de msg en este ejemplo (nota: la cb del observador de render es mediodía)

3. Procesamiento asincrónico

Para asíncrono, llame a queueWatcher para empujar al observador a la pila. Antes de usar este método, veamos primero cómo actualizar el observador en la cola, es decir, el método flushSchedulerQueue, en src / core / observer / Scheduler.js

function flushSchedulerQueue () {
  //1、设置标识位flush为true,标识正在刷新中
  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.
  //2、将queue数组从小到大排序
  queue.sort((a, b) => a.id - b.id)

  // do not cache length because more watchers might be pushed
  // as we run existing watchers
  //3、循环队栈queue,执行watcher.run方法,实现更新。
  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
      }
    }
  }

  // keep copies of post queues before resetting state
  const activatedQueue = activatedChildren.slice()
  const updatedQueue = queue.slice()
  //4、重置相关的状态
  resetSchedulerState()

  // call component updated and activated hooks
  callActivatedHooks(activatedQueue)
  callUpdatedHooks(updatedQueue)

  // devtool hook
  /* istanbul ignore if */
  if (devtools && config.devtools) {
    devtools.emit('flush')
  }
}

Veamos los pasos principales:

(1) Establezca el vaciado del bit de bandera, lo que indica que la cola se está procesando.

(2) De acuerdo con la identificación del observador en la cola, ordenar de pequeño a grande (es decir, el orden de creación)

(3) Reproduzca el observador en la consulta, ejecute el método de ejecución e implemente la actualización.

(4) Después de que se completa la ejecución en la cola, se restablece el estado relacionado, incluido el vaciado, la espera, etc., esperando la próxima ejecución.

Todo el proceso de procesamiento es relativamente claro. Observe aquí que en el paso 3, la cola puede cambiar dinámicamente.

Ahora mirando hacia atrás, continúe mirando el queueWatcher, cómo agregar el observador a la cola y cómo activar la ejecución de flushSchedulerQueue.

export function queueWatcher (watcher: Watcher) {
  const id = watcher.id
  if (has[id] == null) {//对于同一个watcher,不会重复加入到queue,避免多次触发
    has[id] = true
    //1、将watcher加入队列中
    if (!flushing) {//尚未刷新,则加入队栈,待执行
      queue.push(watcher)
    } else {//2、正在刷新中,则动态的插入到到对应位置。
      // 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
    //3、通过nexttick执行queue中的watcher
    if (!waiting) {
      waiting = true
      nextTick(flushSchedulerQueue)
    }
  }
}

Agregue observador a la cola del equipo de cola, hay dos casos

(1) Si no se ejecuta flushSchedulerQueue, agregue el observador a la cola y espere la próxima ejecución.

(2) Cuando se ejecuta flushSchedulerQueue, se busca la posición correspondiente de atrás hacia adelante y luego se inserta en la cola.

(3) Llame a flushSchedulerQueue a través de nextTick para actualizar el observador en la cola. El DOM de Vue se actualiza de forma asíncrona, nextTick garantiza que se ejecute después de que se actualiza el DOM, y aquí puede considerarse como el "tic" del siguiente bucle de eventos. El mecanismo nextTick implementa actualizaciones "masivas", lo que es más eficiente.

Cabe señalar aquí que para el mismo observador, no se puede agregar a la cola repetidamente para evitar múltiples desencadenantes.

6. Resumen

La lógica de este capítulo es más complicada. Resumimos la relación de llamada entre los diversos métodos para que todos entiendan.

33 artículos originales publicados · Me gustaron 95 · Visitantes 30,000+

Supongo que te gusta

Origin blog.csdn.net/tcy83/article/details/90543287
Recomendado
Clasificación