[Diccionario front-end] El principio de respuesta de Vue es realmente muy fácil de entender

Prefacio

Este es el tercero de una serie de diez artículos de Vue. En este artículo, hablaremos sobre una de las funciones principales de Vue: el principio de capacidad de respuesta.

Cómo entender receptivo

Se puede entender así: cuando un estado cambia, las transacciones relacionadas con este estado también cambian inmediatamente. Desde el punto de vista del front-end, el DOM relacionado cambia después de que cambia el estado de los datos. El modelo de datos es solo un objeto JavaScript normal. Y cuando los modifique, la vista se actualizará.

Lanzar una pregunta

Echemos un vistazo a nuestra escritura común en Vue:

<div id="app" @click="changeNum">

  {{ num }}

</div>

var app = new Vue({

  el: '#app',

  data: {

    num: 1

  },

  methods: {

    changeNum() {

      this.num = 2

    }

  }

})

Esta forma de escribir es muy común, pero ha considerado por qué la vista se actualizará cuando se ejecute this.num = 2. A través de este artículo, me esfuerzo por aclarar este punto.

Si no usamos Vue, ¿cómo deberíamos lograrlo?

Mi primer pensamiento fue implementarlo así:

let data = {

  num: 1

};

Object.defineProperty(data, 'num',{

  value: value,

  set: function( newVal ){

    document.getElementById('app').value = newVal;

  }

});

input.addEventListener('input', function(){

  data.num = 2;

});

De esta manera, puede hacer clic aproximadamente en el elemento y actualizar automáticamente la vista.

Aquí necesitamos manipular las propiedades de acceso del objeto a través de Object.defineProperty. Cuando se monitorean los cambios de datos, se opera el DOM correspondiente.

Aquí se utiliza un patrón común: el patrón de publicación / suscripción.

Dibujé un diagrama de flujo general para ilustrar el modelo de observador y el modelo de publicación / suscripción. como sigue:

[Diccionario front-end] El principio de respuesta de Vue es realmente muy fácil de entender

Los estudiantes cuidadosos encontrarán que la diferencia entre mi proceso aproximado y el uso de Vue es que necesito manipular el DOM para volver a renderizarlo yo mismo.

Si usamos Vue, este paso lo maneja el código dentro de Vue. Es por eso que no necesitamos manipular manualmente el DOM cuando usamos Vue.

Acerca de Object.defineProperty que mencioné en el artículo anterior, así que no lo repetiré aquí.

¿Cómo logra Vue la capacidad de respuesta?

Sabemos que un objeto puede manipular sus propiedades de acceso a través de Object.defineProperty, es decir, el objeto tiene métodos getter y setter. Esta es la piedra angular para lograr la capacidad de respuesta.

Primero mire un diagrama de flujo muy intuitivo:
[Diccionario front-end] El principio de respuesta de Vue es realmente muy fácil de entender

método initData

Cuando se inicializa Vue, su método _init () llamará y ejecutará el método initState (vm). El método initState inicializa principalmente propiedades como accesorios, métodos, datos, calculados y wathcer.

Aquí haremos un análisis más detallado del proceso de inicialización de datos.

function initData (vm: Component) {

  let data = vm.$options.data

  data = vm._data = typeof data === 'function'

    ? getData(data, vm)

    : data || {}

  if (!isPlainObject(data)) {

    ......

  }

  // proxy data on instance

  const keys = Object.keys(data)

  const props = vm.$options.props

  const methods = vm.$options.methods

  let i = keys.length

  while (i--) {

    const key = keys[i]

    ...... // 省略部分兼容代码,但不影响理解

    if (props && hasOwn(props, key)) {

      ......

    } else if (!isReserved(key)) {

      proxy(vm, `_data`, key)

    }

  }

  // observe data

  observe(data, true /* asRootData */)

}

El proceso principal de initData para inicializar datos es hacer dos cosas:

  1. Proxy cada valor vm._data. [Clave] a vm. [Clave] a través del proxy;

  2. Llame al método de observación para observar los cambios de todos los datos y convertir los datos en una respuesta (observable). Puede acceder a los atributos correspondientes en la función de retorno de datos definida a través de vm._data. [Clave].

Secuestro de datos: observar

Mediante este método, todos los atributos de los datos se vuelven receptivos (observables).

// 给对象的属性添加 getter 和 setter,用于依赖收集和发布更新

export class Observer {

  value: any;

  dep: Dep;  

  vmCount: number; 

  constructor (value: any) {

    this.value = value

    // 实例化 Dep 对象

    this.dep = new Dep()

    this.vmCount = 0

    // 把自身实例添加到数据对象 value 的 __ob__ 属性上

    def(value, '__ob__', this)

    // value 是否为数组的不同调用

    if (Array.isArray(value)) {

      const augment = hasProto ? protoAugment : copyAugment

      augment(value, arrayMethods, arrayKeys)

      this.observeArray(value)

    } else {

      this.walk(value)

    }

  }

  // 取出所有属性遍历

  walk (obj: Object) {

    const keys = Object.keys(obj)

    for (let i = 0; i < keys.length; i++) {

      defineReactive(obj, keys[i])

    }

  }

  observeArray (items: Array<any>) {

    for (let i = 0, l = items.length; i < l; i++) {

      observe(items[i])

    }

  }

}

Object.defineProperty está encapsulado en la función def, por lo que console.log (data) encontrará un atributo ob adicional .

método defineReactive para atravesar todos los atributos

// 定义一个响应式对象的具体实现

export function defineReactive (

  obj: Object,

  key: string,

  val: any,

  customSetter?: ?Function,

  shallow?: boolean

) {

  const dep = new Dep()

  ..... // 省略部分兼容代码,但不影响理解

  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()

        if (childOb) {

          childOb.dep.depend()

          if (Array.isArray(value)) {

            dependArray(value)

          }

        }

      }

      return value

    },

    set: function reactiveSetter (newVal) {

      const value = getter ? getter.call(obj) : val

      ..... // 省略部分兼容代码,但不影响理解

      if (setter) {

        setter.call(obj, newVal)

      } else {

        val = newVal

      }

      // 对新的值进行监听

      childOb = !shallow && observe(newVal)

      // 通知所有订阅者,内部调用 watcher 的 update 方法 

      dep.notify()

    }

  })

}

El método defineReactive primero inicializa la instancia del objeto Dep y luego llama de forma recursiva al método observe en el objeto secundario, de modo que todos los atributos secundarios también pueden convertirse en objetos reactivos. Y llame a métodos relacionados con dep en los métodos getter y setter de Object.defineProperty.

cual es:

  1. El trabajo realizado por el método getter es la colección de dependencias-dep.depend ()

  2. El trabajo realizado por el método setter es publicar actualizaciones-dep.notify ()

Descubrimos que existe una relación no despreciable con el objeto Dep. A continuación, veremos el objeto Dep. Este Dep

Dep del centro de despacho

En el artículo anterior mencionamos el modelo publicar / suscribir Hay un centro de despacho antes que el editor y el suscriptor. El rol que juega Dep aquí es el centro de despacho, y su rol principal es:

  1. Recopile suscriptores Watcher y añádalos a la lista de seguidores.

  2. Recibir eventos del editor

  3. Notifique a los suscriptores de las actualizaciones de destino y permita que los suscriptores ejecuten su propio método de actualización

El código detallado es el siguiente:

// Dep 构造函数

export default class Dep {

  static target: ?Watcher;

  id: number;

  subs: Array<Watcher>;

  constructor () {

    this.id = uid++

    this.subs = []

  }

  // 向 dep 的观察者列表 subs 添加 Watcher

  addSub (sub: Watcher) {

    this.subs.push(sub)

  }

  // 从 dep 的观察者列表 subs 移除 Watcher

  removeSub (sub: Watcher) {

    remove(this.subs, sub)

  }

  // 进行依赖收集

  depend () {

    if (Dep.target) {

      Dep.target.addDep(this)

    }

  }

  // 通知所有订阅者,内部调用 watcher 的 update 方法

  notify () {

    const subs = this.subs.slice()

    for (let i = 0, l = subs.length; i < l; i++) {

      subs[i].update()

    }

  }

}

// Dep.target 是全局唯一的观察者,因为在任何时候只有一个观察者被处理。

Dep.target = null

// 待处理的观察者队列

const targetStack = []

export function pushTarget (_target: ?Watcher) {

  if (Dep.target) targetStack.push(Dep.target)

  Dep.target = _target

}

export function popTarget () {

  Dep.target = targetStack.pop()

}

Dep puede entenderse como una gestión de Watcher, y Dep y Watcher están estrechamente relacionados. Por eso debemos echar un vistazo a la implementación de Watcher.

Suscriptor-Vigilante

Hay muchos métodos prototipo definidos en Watcher. Aquí solo hablaré brevemente sobre la actualización y obtendré tres métodos.

  // 为了方便理解,部分兼容代码已被我省去

  get () {

    // 设置需要处理的观察者

    pushTarget(this)

    const vm = this.vm

    let value = this.getter.call(vm, vm)

    // deep 是否为 true 的处理逻辑

    if (this.deep) {

      traverse(value)

    }

    // 将 Dep.target 指向栈顶的观察者,并将他从待处理的观察者队列中移除

    popTarget()

    // 执行依赖清空动作

    this.cleanupDeps()

    return value

  }

  update () {

    if (this.computed) {

      ...

    } else if (this.sync) { 

      // 标记为同步

      this.run()

    } else {      

      // 一般都是走这里,即异步批量更新:nextTick

      queueWatcher(this)

    }

  }

Este es probablemente el caso del proceso reactivo de Vue. Los interesados ​​pueden consultar el código fuente.

Finalmente, lo revisamos a través de este diagrama de flujo:
[Diccionario front-end] El principio de respuesta de Vue es realmente muy fácil de entender

Plan de salida de artículos relacionados con Vue

Recientemente, mis amigos siempre me han preguntado acerca de preguntas relacionadas con Vue, por lo que publicaré 9 artículos relacionados con Vue a continuación, con la esperanza de poder ayudarlo. Mantendré una actualización en 7 a 10 días.

  1. [Diccionario front-end] Vuex inyecta el proceso del ciclo de vida de Vue (completado)

  2. [Diccionario front-end] La reserva de conocimiento necesaria para aprender el código fuente de Vue (completado)

  3. [Diccionario front-end] El principio de respuesta de Vue es realmente muy fácil de entender (completo)

  4. [Diccionario front-end] El proceso de parchear VNodes nuevos y antiguos

  5. [Diccionario front-end] Cómo desarrollar componentes funcionales y cargar npm

  6. [Diccionario front-end] Optimice su proyecto de Vue desde estos aspectos

  7. [Diccionario front-end] Hable sobre el desarrollo del enrutamiento front-end a partir del diseño de Vue-Router

  8. [Diccionario front-end] Cómo usar Webpack correctamente en el proyecto

  9. [Diccionario front-end] Procesamiento del servidor Vue

  10. [Diccionario front-end] Cómo elegir entre Axios y Fetch

Te sugiero que prestes atención a mi cuenta oficial, puedes recibir los últimos artículos lo antes posible.
[Diccionario front-end] El principio de respuesta de Vue es realmente muy fácil de entender
Si desea unirse a la comunicación grupal, también puede agregar un robot inteligente para que lo atraiga automáticamente al grupo:

[Diccionario front-end] El principio de respuesta de Vue es realmente muy fácil de entender

Supongo que te gusta

Origin blog.51cto.com/15077552/2596430
Recomendado
Clasificación