Object.assign觸發(fā)watch原理示例解析
為什么可以用Object.assign觸發(fā)$watch
Object.assign,這個(gè)api在簡(jiǎn)單拷貝可枚舉對(duì)象的屬性值時(shí)經(jīng)常用到。這里介紹一個(gè)在vue2中Object.assign的用法,這個(gè)用法在官網(wǎng)文檔 有詳細(xì)介紹:
watch: {
someObject(nvalue, ovalue) {
...
}
}
// 為對(duì)象添加新屬性
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
而且要注意的是如果像下面這樣添加上去的新屬性無(wú)法觸發(fā)更新:
this.someObject = Object.assign(this.someObject, { a: 1, b: 2 })
問(wèn)題是為什么前面那種寫法會(huì)有效?
先看vue2文檔
在vue2的文檔中有詳細(xì)說(shuō)明,在組件的依賴收集過(guò)程中,所有property 在被訪問(wèn)和修改時(shí)會(huì)通知變更,對(duì)于對(duì)象來(lái)說(shuō),Vue 無(wú)法檢測(cè) property 的添加或移除。

一般情況下,在vue中,如果要對(duì)data對(duì)象中實(shí)例添加根級(jí)別property,我們可以這樣操作:
Vue.set(someObject, 'name', value)
或者這樣操作
this.$set(this.someObject,'name',2)
但是如果我們要對(duì)一個(gè)對(duì)象添加多個(gè)屬性,同時(shí)還要保持對(duì)象的響應(yīng)性,這種情況下就要用到開篇提到的方法。
在mdn 上,對(duì) Object.assign 有這一句解釋:該方法使用源對(duì)象的[[Get]]和目標(biāo)對(duì)象的[[Set]],所以它會(huì)調(diào)用相關(guān) getter 和 setter。對(duì)這句,我們用下面的例子a來(lái)理解。
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'}) // 觸發(fā)了set!
obj = Object.assign(obj, {a: 'wer23e'}) // 由于事先未用defineProperty定義a,所以無(wú)法監(jiān)聽
// 由于目標(biāo)對(duì)象未定義屬性,無(wú)法監(jiān)聽
obj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'})
通過(guò)這段代碼可以理解上面說(shuō)的“Object.assign會(huì)使用目標(biāo)對(duì)象的[[Set]]”,同時(shí),這段代碼也演示了vue2中響應(yīng)原理,因?yàn)関ue2中所有需要響應(yīng)的屬性都是用 Object.defineProperty 進(jìn)行響應(yīng)綁定,這樣所有的訪問(wèn)和修改動(dòng)作都會(huì)被追蹤到。
但是對(duì)于沒(méi)有事先被 Object.defineProperty定義的屬性,比如添加一個(gè)屬性就無(wú)法監(jiān)聽到了。在上面的示例中,即使我用文檔提到的用法 obj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'}) 仍然無(wú)法觸發(fā)c屬性的 set。走到這一步,是不是得看watch源碼了?其實(shí)不必!
用Object.assign觸發(fā)watch原理
針對(duì)這個(gè)問(wèn)題,watch的源碼不必看,但是 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
});
}
其實(shí)就是把 assign 方法中的參數(shù)的可枚舉屬性全部復(fù)制到此方法的第一參數(shù)上去。回頭再去理解下例子a, obj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'}) 無(wú)法觸發(fā)c屬性的 set 函數(shù)是因?yàn)?,obj引用關(guān)系已經(jīng)被改變了,不再是原來(lái)那個(gè)對(duì)象,也就沒(méi)有了對(duì)應(yīng)的屬性監(jiān)控,但是為什么官方文檔會(huì)建議這么用呢?
接下來(lái),我寫了個(gè)小demo,來(lái)幫你理解,你可以試下效果
<template>
<div style="width: 100px; height: 50px; position: absolute; top: 100px" @click="clickme()"> 點(diǎn)我啊 {{ 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>
可以看到,確實(shí),最后一種Object.assign方法會(huì)觸發(fā)test對(duì)象監(jiān)聽。前面兩種寫法,只能觸發(fā)對(duì)象的屬性更新響應(yīng),如果給obj對(duì)象添加屬性,就無(wú)法監(jiān)測(cè)到obj的變化。到這一步,其實(shí)如果要徹底弄清楚,最好還是看下 watch 源碼。我在仔細(xì)分析了后,才發(fā)現(xiàn)其實(shí)跟watch源碼沒(méi)有什么關(guān)系,所以本文就不展開分析了,這是另一篇文章的事了。
如果看不明白 watch 源碼也沒(méi)關(guān)系,其實(shí)簡(jiǎn)單解釋就是,用obj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'}) 這種方式,改變了obj的引用關(guān)系,也就是obj的值變了,所以如果你在watch函數(shù)中監(jiān)聽了obj,obj是變化了的,只不過(guò)obj的值是一個(gè) Object 對(duì)象而已,所以會(huì)觸發(fā)obj對(duì)象的響應(yīng)。
參考資料:
developer.mozilla.org/zh-CN/docs/…
http://www.dhdzp.com/article/265630.htm
http://www.dhdzp.com/article/266683.htm
以上就是Object.assign觸發(fā)watch原理示例解析的詳細(xì)內(nèi)容,更多關(guān)于Object.assign觸發(fā)watch的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue3自定義組件之v-model實(shí)現(xiàn)父子組件雙向綁定
這篇文章主要介紹了vue3自定義組件之v-model實(shí)現(xiàn)父子組件雙向綁定方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
vue項(xiàng)目使用jszip和file-saver批量打包壓縮圖片或附件方式
這篇文章主要介紹了vue項(xiàng)目使用jszip和file-saver批量打包壓縮圖片或附件方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
詳解Vue 數(shù)據(jù)更新了但頁(yè)面沒(méi)有更新的 7 種情況匯總及延伸總結(jié)
這篇文章主要介紹了詳解Vue 數(shù)據(jù)更新了但頁(yè)面沒(méi)有更新的 7 種情況匯總及延伸總結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05
vue-router重寫push方法,解決相同路徑跳轉(zhuǎn)報(bào)錯(cuò)問(wèn)題
這篇文章主要介紹了vue-router重寫push方法,解決相同路徑跳轉(zhuǎn)報(bào)錯(cuò)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08
vue3響應(yīng)式Object代理對(duì)象的讀取示例詳解
這篇文章主要為大家介紹了vue3響應(yīng)式Object代理對(duì)象的讀取示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
實(shí)現(xiàn)一個(gè)Vue自定義指令懶加載的方法示例
這篇文章主要介紹了實(shí)現(xiàn)一個(gè)Vue自定義指令懶加載的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
一文解決vue2 element el-table自適應(yīng)高度問(wèn)題
在寫公司后臺(tái)項(xiàng)目的時(shí)候遇到一個(gè)需求,要求表格頁(yè)面不能有滾動(dòng)條,所以必須封裝一個(gè)公共方法來(lái)實(shí)現(xiàn)表格自適應(yīng)高度,本問(wèn)小編給大家介紹了如何解決vue2 element el-table自適應(yīng)高度問(wèn)題,需要的朋友可以參考下2023-11-11
Vue實(shí)現(xiàn)獲取后端接口API代碼片段(已封裝Service方法名)
Vue實(shí)現(xiàn)獲取后端接口API代碼片段(已封裝Service方法名),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07

