Object.assignで$watchをトリガーできるのはなぜですか
Object.assign、このAPIは、列挙可能なオブジェクトのプロパティ値を単にコピーするときによく使用されます。vue2でのObject.assignの使用法は次のとおりです。これについては、公式Webサイトのドキュメントで詳しく説明されています。
watch: {
someObject(nvalue, ovalue) {
...
}
}
// 为对象添加新属性
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
复制代码
また、次のような新しいプロパティを追加しても、更新はトリガーされないことに注意してください。
this.someObject = Object.assign(this.someObject, { a: 1, b: 2 })
复制代码
問題は、なぜそれがこのように機能するのかということです。
最初にvue2のドキュメントを見てください
vue2のドキュメントで詳しく説明されているように、コンポーネントの依存関係の収集プロセスでは、すべてのプロパティにアクセスして変更すると、すべてのプロパティに変更が通知されます。オブジェクトの場合、Vueはプロパティの追加または削除を検出できません。
一般に、vueでは、データオブジェクトのインスタンスにルートレベルのプロパティを追加する場合は、次のように実行できます。
Vue.set(someObject, 'name', value)
复制代码
またはこれを行う
this.$set(this.someObject,'name',2)
复制代码
ただし、オブジェクトの応答性を維持しながらオブジェクトに複数のプロパティを追加する場合は、この場合、最初に説明したメソッドが使用されます。
mdnには、この文の説明があります。メソッドはソースオブジェクトとターゲットオブジェクトを使用するため、関連するゲッターとセッターを呼び出します。この文では、次の例aを使用して理解します。Object.assign
[[Get]]
[[Set]]
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'})
复制代码
このコードを通じて、上記の「Object.assignはターゲットオブジェクトを使用する」を理解できます[[Set]]
。同時に、vue2で応答する必要のあるすべてのプロパティが使用されるため、このコードはvue2での応答原理も示します。Object.defineProperty
応答バインディングの ただし、プロパティの追加など、事前にObject.defineProperty
定義さ、監視できません。上記の例では、ドキュメントに記載されている使用法を使用してobj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'})
もをトリガーできませんset
。この時点で、時計のソースコードを確認する必要がありますか?実際、そうである必要はありません!
Object.assignを使用して監視の原則をトリガーします
この問題に対応するため、時計のソースコードを読み取る必要はありませんが、時計Object.assign
のソースコードを読み取る必要があります。
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
});
}
复制代码
実際、メソッド内のパラメーターの列挙可能なすべてのプロパティを、このメソッドの最初のパラメーターassign
にです。戻って次の例を理解しましょうa。obj参照関係が変更され、元のオブジェクトではなくなったためobj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'})
、c属性のset
関数。したがって、対応する属性の監視はありませんが、なぜ公式ドキュメントはこの使用を推奨しますか?
次に、私はあなたが理解するのを助けるために小さなデモを書きました、あなたは効果を試すことができます
<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>
复制代码
ご覧のとおり、実際、最後のObject.assignメソッドは、テストオブジェクトをリッスンするようにトリガーします。最初の2つの書き込み方法では、オブジェクトのプロパティ更新応答のみをトリガーできます。objオブジェクトにプロパティを追加すると、objの変更を監視できなくなります。この時点で、実際、完全に明確にしたい場合は、watch
ソースコード。注意深く分析したところ、ソースコードとは関係がないことがわかりましたwatch
。この記事では分析しません。これは別の記事です。
watch
ソースコードがわからなくても構いません。実はobj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'})
このようにobjの参照関係が変わったり、objの値が変わったりするので、objをモニターするとウォッチ機能では、objが変更されています。objの値がObject
オブジェクトので、objオブジェクトの応答がトリガーされます。
参照: