¿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.assign
hay 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.defineProperty
para 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.defineProperty
definidas , 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.assign
el 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, assign
es 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 set
funció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 watch
código fuente . Después de un análisis cuidadoso, descubrí que no watch
tiene 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 watch
có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 Object
objeto , por lo que se activará la respuesta del objeto obj.
Referencias: