Vue父子組件傳值的一些坑
在用 Vue 的父子組件傳值時(shí)遇到一個(gè)冷門的問(wèn)題,子組件改變值后父組件的值也隨之改變了,特此記錄下原因和解決方式。
再系統(tǒng)梳理下 JavaScript 的深拷貝與淺拷貝相關(guān)知識(shí)點(diǎn)。
1. 問(wèn)題描述
父組件傳值給子組件,子組件改變傳過(guò)來(lái)的值后,父組件的值也會(huì)跟著改變。
這個(gè)問(wèn)題比較冷門,平時(shí)如果對(duì)組件通信使用得比較簡(jiǎn)單,一般不會(huì)遇到。
2. 原因剖析
- 核心:雙向綁定
父子組件傳值的時(shí)候涉及雙向綁定,當(dāng)傳值為 object 類型時(shí),傳值之后數(shù)據(jù)源會(huì)被改變。
- 深拷貝與淺拷貝
下文詳細(xì)講。
3. 解決方案
我目前采用的解決辦法是:
傳值的時(shí)候不要直接傳數(shù)據(jù)源,而是通過(guò)拷貝或者定義新變量等方式傳值。
簡(jiǎn)單處理就 JSON.parse(JSON.stringify(obj)),但是這種簡(jiǎn)單粗暴的方法有其局限性。當(dāng)值為 undefined、function、symbol 會(huì)在轉(zhuǎn)換過(guò)程中被忽略。所以,對(duì)象值有這三種的話用這種方法會(huì)導(dǎo)致屬性丟失。
剩下的就是自寫深拷貝的工具函數(shù),或者直接借助第三方的庫(kù)函數(shù),下面展開(kāi)講。
4. 深拷貝和淺拷貝
JavaScript中的淺拷貝與深拷貝,只是針對(duì)復(fù)雜數(shù)據(jù)類型(Object,Array)的復(fù)制問(wèn)題。淺拷貝與深拷貝都可以實(shí)現(xiàn)在已有對(duì)象上再生出一份的作用。但是對(duì)象的實(shí)例是存儲(chǔ)在堆內(nèi)存中然后通過(guò)一個(gè)引用值去操作對(duì)象,由此拷貝的時(shí)候就存在兩種情況了:拷貝引用和拷貝實(shí)例,這也是淺拷貝和深拷貝的區(qū)別。
下圖為JavaScript復(fù)雜數(shù)據(jù)類型的淺拷貝示意圖:
- 淺拷貝
淺拷貝是拷貝引用,拷貝后的引用都是指向同一個(gè)對(duì)象的實(shí)例,彼此之間的操作會(huì)互相影響。
值得注意的是:Object.assgin() 是淺拷貝,它只能深拷貝第一層,深層的還是淺拷貝。因?yàn)?Object.assign() 拷貝的是屬性值。假如源對(duì)象的屬性值是一個(gè)對(duì)象的引用,那么它也只指向那個(gè)引用。(摘選自MDN)
MDN講述 assign 的時(shí)候,就有一個(gè)典型的例子,這里是文章鏈接。
下面列舉第一類淺拷貝 - 拷貝原對(duì)象的引用:
/** * 對(duì)象的淺拷貝 */ var obj1 = { name:'wenyuan', age: 22 } var obj2 = obj1; obj2['job'] = 'coder'; console.log(obj1); //Object {name: "wenyuan", age: 22, job: "coder"} console.log(obj2); //Object {name: "wenyuan", age: 0, job: "coder"} /* ------------------------- 華麗的分割線 ------------------------- */ /** * 數(shù)組的淺拷貝 */ var arr1 = [1, 2, 3, '4']; var arr2 = arr1; arr2[1] = "test"; console.log(arr1); // [1, "test", 3, "4"] console.log(arr2); // [1, "test", 3, "4"]
接下來(lái)看第二類淺拷貝 - 源對(duì)象拷貝實(shí)例,其屬性對(duì)象拷貝引用:
這種情況,外層源對(duì)象是拷貝實(shí)例,如果其屬性元素為復(fù)雜數(shù)據(jù)類型(Object、Array)時(shí),內(nèi)層元素拷貝引用。
對(duì)源對(duì)象直接操作,不影響另外一個(gè)對(duì)象,但是對(duì)其屬性操作時(shí)候,會(huì)改變另外一個(gè)對(duì)象的屬性的值。
/** * 對(duì)象的淺拷貝 * jQuery的 $.extend(a,b) 或 $.extend({},a,b) */ var obj1 = { name:'wenyuan', age: 22, social: { blog: 'www.wenyuanblog.com' }, skills: ['js', 'html', 'css', 'python'] } var obj2 = $.extend({},obj1); console.log(obj1 === obj2) // 輸出false,說(shuō)明外層數(shù)組拷貝的是實(shí)例 console.log(obj1.social === obj2.social) // 輸出true,說(shuō)明對(duì)于Object類型的屬性是拷貝引用 console.log(obj1.skills === obj2.skills) // 輸出true,說(shuō)明對(duì)于Array類型的屬性是拷貝引用 /** * 對(duì)象的淺拷貝 * ES6的 Object.assign() 和 對(duì)象擴(kuò)展運(yùn)算符... */ var obj1 = { name:'wenyuan', age: 22, social: { blog: 'www.wenyuanblog.com' }, skills: ['js', 'html', 'css', 'python'] } var obj2 = Object.assign({},obj1); console.log(obj1 === obj2) // 輸出false,說(shuō)明外層數(shù)組拷貝的是實(shí)例 console.log(obj1.social === obj2.social) // 輸出true,說(shuō)明對(duì)于Object類型的屬性是拷貝引用 console.log(obj1.skills === obj2.skills) // 輸出true,說(shuō)明對(duì)于Array類型的屬性是拷貝引用 var obj3 = {...obj1}; console.log(obj1 === obj3) // 輸出false,說(shuō)明外層數(shù)組拷貝的是實(shí)例 console.log(obj1.skills === obj3.skills) // 輸出true,說(shuō)明對(duì)于Array類型的屬性是拷貝引用 console.log(obj1.skills === obj3.skills) // 輸出true,說(shuō)明對(duì)于Array類型的屬性是拷貝引用 /* ------------------------- 華麗的分割線 ------------------------- */ /** * 數(shù)組的淺拷貝 * Array.prototype.slice() */ var arr1 = [{name: "wenyuan"}, {name: "Evan You"}]; var arr2 = arr1.slice(0); console.log(arr1 === arr2); // 輸出false,說(shuō)明外層數(shù)組拷貝的是實(shí)例 console.log(arr1[0] === arr2[0]); // 輸出true,說(shuō)明其元素拷貝的是引用 /** * 數(shù)組的淺拷貝 * Array.prototype.concat() */ var arr1 = [{name: "wenyuan"}, {name: "Evan You"}]; var arr2 = arr1.concat(); console.log(arr1 === arr2); // 輸出false,說(shuō)明外層數(shù)組拷貝的是實(shí)例 console.log(arr1[0] === arr2[0]); // 輸出true,說(shuō)明其元素拷貝的是引用 /** * 數(shù)組的淺拷貝 * ES6的 Object.assign() 和 對(duì)象擴(kuò)展運(yùn)算符... * 由于數(shù)組是特殊的對(duì)象,所以ES6中的這種方式也可以用于數(shù)組 */ var arr1 = [{name: "wenyuan"}, {name: "Evan You"}]; var arr2 = Object.assign([],arr1) var arr3 = { ...arr1 }; console.log(arr1 === arr2); // 輸出false,說(shuō)明外層數(shù)組拷貝的是實(shí)例 console.log(arr1 === arr3); // 輸出false,說(shuō)明外層數(shù)組拷貝的是實(shí)例 console.log(arr1[0] === arr2[0]); // 輸出true,說(shuō)明其元素拷貝的是引用 console.log(arr1[0] === arr3[0]); // 輸出true,說(shuō)明其元素拷貝的是引用
- 深拷貝
在堆中重新分配內(nèi)存,并且把源對(duì)象所有屬性都進(jìn)行新建拷貝,以保證深拷貝的對(duì)象的引用圖不包含任何原有對(duì)象或?qū)ο髨D上的任何對(duì)象,拷貝后的對(duì)象與原來(lái)的對(duì)象是完全隔離,互不影響。
下面列舉一些深拷貝的例子:
/** * 對(duì)象的深拷貝 * JSON.stringify()和JSON.parse() * 這種深拷貝最簡(jiǎn)單,但有其局限性,上文已經(jīng)提到過(guò)了 */ var obj1 = { name:'wenyuan', age: 22, social: { blog: 'www.wenyuanblog.com' }, skills: ['js', 'html', 'css', 'python'] } var obj2 = JSON.parse(JSON.stringify(obj1)); console.log(obj1 === obj2) // 輸出false,說(shuō)明外層數(shù)組拷貝的是實(shí)例 console.log(obj1.social === obj2.social) // 輸出false,說(shuō)明對(duì)于Object類型的屬性也是拷貝實(shí)例 console.log(obj1.skills === obj2.skills) // 輸出false,說(shuō)明對(duì)于Array類型的屬性也是拷貝實(shí)例 /** * 對(duì)象的深拷貝 * jQuery的 $.extend(true,a,b) */ var obj1 = { name:'wenyuan', age: 22, social: { blog: 'www.wenyuanblog.com' }, skills: ['js', 'html', 'css', 'python'] } var obj2 = $.extend(true,obj1); console.log(obj1 === obj2) // 輸出false,說(shuō)明外層數(shù)組拷貝的是實(shí)例 console.log(obj1.social === obj2.social) // 輸出false,說(shuō)明對(duì)于Object類型的屬性也是拷貝實(shí)例 console.log(obj1.skills === obj2.skills) // 輸出false,說(shuō)明對(duì)于Array類型的屬性也是拷貝實(shí)例 /** * 對(duì)象的深拷貝 * 也可以自己寫一個(gè)函數(shù)實(shí)現(xiàn),用遞歸+判斷,注意別進(jìn)入死循環(huán)就好 * 這里不舉例了,以前我整理過(guò)一篇常用工具類函數(shù)的博客,里面包含了深拷貝函數(shù) */ /** * 對(duì)象的深拷貝 * lodash的_.cloneDeep */ var obj1 = { name:'wenyuan', age: 22, social: { blog: 'www.wenyuanblog.com' }, skills: ['js', 'html', 'css', 'python'] } var obj2 = _.cloneDeep(obj1); console.log(obj1 === obj2) // 輸出false,說(shuō)明外層數(shù)組拷貝的是實(shí)例 console.log(obj1.social === obj2.social) // 輸出false,說(shuō)明對(duì)于Object類型的屬性也是拷貝實(shí)例 console.log(obj1.skills === obj2.skills) // 輸出false,說(shuō)明對(duì)于Array類型的屬性也是拷貝實(shí)例
以上就是JavaScript中的淺拷貝與深拷貝的知識(shí)點(diǎn),以代碼的形式記錄下來(lái),方便回顧。
到此這篇關(guān)于Vue父子組件傳值的一些坑的文章就介紹到這了,更多相關(guān)Vue父子組件傳值內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue自適應(yīng)布局postcss-px2rem詳解
這篇文章主要介紹了vue自適應(yīng)布局(postcss-px2rem)的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2022-05-05解決vue-cli項(xiàng)目webpack打包后iconfont文件路徑的問(wèn)題
今天小編就為大家分享一篇解決vue-cli項(xiàng)目webpack打包后iconfont文件路徑的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09基于vuejs實(shí)現(xiàn)一個(gè)todolist項(xiàng)目
這篇文章主要為大家詳細(xì)介紹了基于vuejs實(shí)現(xiàn)一個(gè)todolist項(xiàng)目,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04vue-resource 攔截器interceptors使用詳解
這篇文章主要介紹了vue-resource 攔截器interceptors使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01解決el-date-picker日期選擇控件少一天的問(wèn)題
這篇文章主要介紹了解決el-date-picker日期選擇控件少一天的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10關(guān)于vue項(xiàng)目一直出現(xiàn) sockjs-node/info?t=XX的解決辦法
sockjs-node 是一個(gè)JavaScript庫(kù),提供跨瀏覽器JavaScript的API,創(chuàng)建了一個(gè)低延遲、全雙工的瀏覽器和web服務(wù)器之間通信通道,這篇文章主要介紹了vue項(xiàng)目一直出現(xiàn) sockjs-node/info?t=XX的解決辦法,需要的朋友可以參考下2023-12-12vue 2.1.3 實(shí)時(shí)顯示當(dāng)前時(shí)間,每秒更新的方法
今天小編就為大家分享一篇vue 2.1.3 實(shí)時(shí)顯示當(dāng)前時(shí)間,每秒更新的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09