¿Por qué se puede activar $watch con Object.assign?

¿Por qué se puede activar $watch con Object.assign?

Object.assign, esta API se usa a menudo cuando simplemente se copia el valor de la propiedad de un objeto enumerable. Aquí hay un uso de Object.assign en vue2, que se describe en detalle en la documentación del sitio web oficial :

watch: {
	someObject(nvalue, ovalue) {
		...
	}
}

// 为对象添加新属性
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
复制代码

También tenga en cuenta que agregar una nueva propiedad como la siguiente no activará una actualización:

this.someObject = Object.assign(this.someObject, { a: 1, b: 2 })
复制代码

La pregunta es ¿por qué funciona de esta manera?

Primer vistazo a la documentación de vue2

Como se detalla en la documentación de vue2 , en el proceso de recopilación de dependencias de componentes, se notificarán los cambios a todas las propiedades cuando se acceda a ellas y se modifiquen Para los objetos, Vue no puede detectar la adición o eliminación de propiedades.

En general, en vue, si queremos agregar propiedades de nivel raíz a instancias en el objeto de datos, podemos hacer esto:

Vue.set(someObject, 'name', value)
复制代码

o haz esto

this.$set(this.someObject,'name',2)
复制代码

Pero si queremos agregar varias propiedades a un objeto manteniendo la capacidad de respuesta del objeto, en este caso se usa el método mencionado al principio.

En mdn , Object.assignhay una explicación para esta oración: el método usa el objeto de origen [[Get]]y el objeto de destino [[Set]], por lo que llamará al getter y setter relevantes. Para esta oración, usamos el siguiente ejemplo a para entender.

var obj = {};
var c = null
Object.defineProperty(obj, 'c', {
  set:function(x){
    console.log('c被赋值:',x);
    c=x
  },
  get:function(){
    console.log('c被取出:',c)
    return c
  }
})

obj.c=3  //c被赋值: 3
obj.c  //c被取出:  3

obj = Object.assign(obj, {c: 'wer23e'}) // 触发了set!
obj = Object.assign(obj, {a: 'wer23e'}) // 由于事先未用defineProperty定义a,所以无法监听
// 由于目标对象未定义属性,无法监听
obj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'}) 
复制代码

A través de este código, puede comprender el "Object.assign" mencionado anteriormente "Usará el objeto de destino [[Set]]". Al mismo tiempo, este código también demuestra el principio de respuesta en vue2, porque se utilizan todas las propiedades que deben responderse en vue2 Object.definePropertypara el enlace de respuesta, por lo que se rastrean todas las acciones de acceso y modificación. Pero para las propiedades que no están Object.definePropertydefinidas , como agregar una propiedad, no se puede monitorear. En el ejemplo anterior, incluso si utilizo el uso mencionado en la documentación, el atributo c obj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'})aún no se puede activar set. En este punto, ¿tienes que mirar el código fuente del reloj? ¡En realidad no tiene por qué serlo!

Active el principio del reloj con Object.assign

En respuesta a este problema, no es necesario leer el código fuente del reloj, pero se debe leer Object.assignel código fuente del reloj.

if (typeof Object.assign !== 'function') {
  // Must be writable: true, enumerable: false, configurable: true
  Object.defineProperty(Object, "assign", {
    value: function assign(target, varArgs) { // .length of function is 2
      'use strict';
      if (target === null || target === undefined) {
        throw new TypeError('Cannot convert undefined or null to object');
      }

      var to = Object(target);

      for (var index = 1; index < arguments.length; index++) {
        var nextSource = arguments[index];

        if (nextSource !== null && nextSource !== undefined) {
          for (var nextKey in nextSource) {
            // Avoid bugs when hasOwnProperty is shadowed
            if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
              to[nextKey] = nextSource[nextKey];
            }
          }
        }
      }
      return to;
    },
    writable: true,
    configurable: true
  });
}
复制代码

De hecho, assignes copiar todas las propiedades enumerables de los parámetros en el método al primer parámetro de este método. Volvamos atrás y entendamos el siguiente ejemplo a. obj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'})La setfunción porque la relación de referencia de obj ha cambiado y ya no es el objeto original, por lo que no hay un seguimiento de atributo correspondiente, pero ¿por qué el oficial? documento recomiendan este uso ?

A continuación, escribí una pequeña demostración para ayudarlo a comprender, puede probar el efecto

<template>
 <div style="width: 100px; height: 50px; position: absolute; top: 100px" @click="clickme()"> 点我啊 {{ test.a }}</div>
</template>

<script>
export default {
	data() {
		test: { a: 3 },
	},
	watch: {
    test(n, o) {
      debugger
      console.log(n)
    }
  },
	methods: {
		clickme() {
      debugger
      this.test = Object.assign(this.test, { a: 1 })
      debugger
      this.test = Object.assign(this.test, { a: 1, b: 2 })
      debugger
      this.test = Object.assign({}, this.test, { a: 1, b: 2 })
    },	
}
}
</script>
复制代码

Como puede ver, de hecho, el último método Object.assign activará el objeto de prueba para escuchar. Las dos primeras formas de escritura solo pueden desencadenar la respuesta de actualización de propiedad del objeto.Si agrega propiedades al objeto obj, no puede monitorear los cambios de obj. En este punto, de hecho, si quieres que te quede completamente claro, lo mejor es que te fijes en el watchcódigo fuente . Después de un análisis cuidadoso, descubrí que no watchtiene nada que ver con el código fuente, por lo que este artículo no lo analizará, este es otro artículo.

No importa si no entiendes el watchcódigo fuente , de hecho, la explicación simple es que de obj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'})esta manera se cambia la relación de referencia de obj, es decir, el valor de obj ha cambiado, por lo que si monitoreas obj en la función de reloj, obj ha cambiado Es solo que el valor de obj es un Objectobjeto , por lo que se activará la respuesta del objeto obj.

Referencias:

Supongo que te gusta

Origin juejin.im/post/7078610313255321608
Recomendado
Clasificación