在vue中子組件修改props引發的對js深拷貝和淺拷貝的思考

来源:https://www.cnblogs.com/hutuzhu/archive/2018/12/14/10119698.html
-Advertisement-
Play Games

不管是react還是vue,父級組件與子組件的通信都是通過props來實現的,在vue中父組件的props遵循的是單向數據流,用官方的話說就是,父級的props的更新會向下流動到子組件中,反之則不行。也就是說,子組件不應該去修改props。但實際開發過程中,可能會有一些情況試圖去修改props數據: ...


不管是react還是vue,父級組件與子組件的通信都是通過props來實現的,在vue中父組件的props遵循的是單向數據流,用官方的話說就是,父級的props的更新會向下流動到子組件中,反之則不行。也就是說,子組件不應該去修改props。但實際開發過程中,可能會有一些情況試圖去修改props數據:

1、這個props只是傳遞一個初始值,子組件把它當做一個局部變數來使用,這種情況一般定義一個本地的data屬性,將props的值賦值給它。如下:

props: ['initialCounter'],
data: function () {
  return {
    counter: this.initialCounter
  }
}

2、這個props的值以原始數據傳入,但是子組件對其需要轉換。這種情況,最好使用computed來定義一個計算屬性,如下:

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

以上兩種情況,傳遞的值都是基本數據類型,但是大多數情況下,我們需要向子組件傳遞一個引用類型數據,那麼問題就來了。

JavaScript 中對象和數組是通過引用傳入的,所以對於一個數組或對象類型的 prop 來說,在子組件中改變這個對象或數組本身將會影響到父組件的狀態。

比如,在父組件中有一個列表,雙擊其中一個元素進行編輯,該元素的數據作為props傳遞給一個子組件,在子組件中需要對該數據進行編輯,你會發現如上所說,編輯後父組件的值也發生了變化。實際上我們想父組件影響子組件,但是子組件修改不要影響父組件。vue官網上貌似沒說明這種情況應該如何處理。

這裡情況相對簡單點,在傳遞props時用Object.assign拷貝一份數據(這裡數據是一個單層級對象),然後在子組件裡面對其進行編輯。Object.assign能實現對象的合併,但是它是淺拷貝,也就是說如果對象的熟悉也是對象就不行。

於是查閱了相關資料,再次鞏固下JS中深拷貝與淺拷貝的相關知識。

1、基本數據類型和引用數據類型的存儲位置

 基本數據類型是存儲在棧記憶體中,比如 var a=1;

當進行複製操作b=a時,會在棧記憶體中再開一個記憶體,如下

變數a和變數b的存儲互補影響,如果此時修改b的值不會影響a的值。

引用類型數據存儲在堆記憶體中,引用類型的名是存儲在棧記憶體中,值是存儲在堆記憶體中,但是棧記憶體會提供引用地址指向堆記憶體中的值。

當進行b=a的複製操作時,複製的是引用地址,而不是堆記憶體中的值。

而當我們a[0]=1時進行數組修改時,由於a與b指向的是同一個地址,所以自然b也受了影響,這就是所謂的淺拷貝了。

而實際上我們希望的效果應該是這樣:

好,到這裡,到底什麼是深淺拷貝:

對於僅僅是複製了引用(地址),換句話說,複製了之後,原來的變數和新的變數指向同一個東西,彼此之間的操作會互相影響,為 淺拷貝

而如果是在堆中重新分配記憶體,擁有不同的地址,但是值是一樣的,複製後的對象與原來的對象是完全隔離,互不影響,為 深拷貝

 

回顧下JS里實現拷貝的方法有哪些:

針對數組有這些方法:

Array.slice()

var a=[1,2,3];
var b=a.slice();
b[0]=4;
console.log(b);//[4,2,3]
console.log(a);//[1,2,3]

Array.concat

var a=[1,2,3];
var b=a.concat();
b[0]=4;
console.log(b);//[4,2,3]
console.log(a);//[1,2,3]

 當然,也可以遍曆數組賦值。

但是以上兩種只對單級結構的數組有效,如果數組的元素是一個引用類型,就不行了,比如:

let a=[0,1,[2,3],4],
        b=a.slice();
a[0]=1;
a[2][0]=1;
console.log(a,b);

 

修改二維數組的元素還是會影響原數組,也就是說slice和concat實際上是淺拷貝。

針對對象:

Object.assign()

var a={
  "name":"張三"  
};
b=Object.assign({},a);
b.name="李四";
console.log(b.name);//李四
console.log(a.name);//張三

同樣該方法也是淺拷貝,如果對象屬性值是引用類型也不行;

那麼到底有哪些辦法可以實現深拷貝呢

1、遞歸

function deepClone(obj){
    let objClone = Array.isArray(obj)?[]:{};
    if(obj && typeof obj==="object"){
        for(key in obj){
            if(obj.hasOwnProperty(key)){
                //判斷ojb子元素是否為對象,如果是,遞歸複製
                if(obj[key]&&typeof obj[key] ==="object"){
                    objClone[key] = deepClone(obj[key]);
                }else{
                    //如果不是,簡單複製
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}    
let a=[1,2,3,4],
    b=deepClone(a);
a[0]=2;
console.log(a,b);

2、jquery中的$.extend();

var obj = {name:'xixi',age:20,company : { name : '騰訊', address : '深圳'} };
var obj_extend = $.extend(true,{}, obj); //extend方法,第一個參數為true,為深拷貝,為false,或者沒有為淺拷貝。
console.log(obj === obj_extend);
obj.company.name = "ali";
obj.name = "hei";
console.log(obj);
console.log(obj_extend);

3、JSON對象的JSON.parse()和JSON.stringify();

var obj = {name:'xixi',age:20,company : { name : '騰訊', address : '深圳'} };
var obj_json = JSON.parse(JSON.stringify(obj));
console.log(obj === obj_json);
obj.company.name = "ali";
obj.name = "hei";
console.log(obj);
console.log(obj_json);

4、Lodash中的_.cloneDeep()

var objects = [{ 'a': 1 }, { 'b': 2 }];
 
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false

 

雖然通過拷貝props數據解決了問題,但是拷貝後修改新數據的屬性並不會觸發vue的更新機制,需要強制更新$forceUpdate(),總覺得很奇怪,不知道大家有什麼更好的辦法沒有,歡迎大家留言討論。

 

參考文章:

https://zhuanlan.zhihu.com/p/26282765

https://zhuanlan.zhihu.com/p/26282765

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言: 昨天剛看了插槽,以為可以解決我工作中遇到的問題,非常激動,我今天又仔細想了想,發現並不能解決。。。 不過還是記錄一下插槽吧,加深印象,嗯,就醬。 插槽作用: 插槽即:ReactDOM.createPortal(child, container) ,由ReactDom提供的介面。 可以實現將子 ...
  • 本文是基於Ant design Pro 2.0做的筆記,官方提供的demo(官方demo下載地址),路由是程式配置的,不能滿足項目需求,所以在研究過程中,把所遇到的問題,做一個筆記,最終效果圖如下: 一:需求描述 1 從介面獲取菜單,替換預設demo的菜單。 由於只是測試,所以並沒有使用servic ...
  • 一、Egg連接Mongodb方法一 Plugin.js中配置 Config.default.js配置 查詢語句使用 二、Egg中Mongoose的使用 https://www.npmjs.com/package/egg-mongoose 配置 Schema的建立數據表集合的映射新建app/modul ...
  • 1.在pycharm的已有工程中新建一個html文件。 2.在<body></body>標簽內部寫入要內容: 效果是: 心得: html和css是標簽語言,它的使用的初衷是為了在網頁上顯示出相應的圖像或是排版,所以在使用的時候,我目前感覺到最重要的一點就是註意沒有單元的位置。為了達到這個目的需要1. ...
  • 所有基礎課程鏈接: 1.JavaScript基礎視頻教程總結(001-010章) 2.JavaScript基礎視頻教程總結(011-020章) 3. JavaScript基礎視頻教程總結(021-030章) 4. JavaScript基礎視頻教程總結(031-040章) 5. JavaScript基 ...
  • 往其祖上到三代,祖孫三代一起進名單,全部收錄起來。 為方便查閱,下文元字元閱讀方式直接是 “序號 字元 :解釋” \ :將下一個字元標記為一個特殊字元、或一個原義字元、或一個 向後引用、或一個八進位轉義符。例如,'n' 匹配字元 "n"。'\n' 匹配一個換行符。序列 '\\' 匹配 "\" 而 " ...
  • 今天項目中碰到了這個問題,在網路上試圖找了一下別人的見解,然後得出自己的淺見。 引用: http://blog.sina.com.cn/s/blog_9342cd570102v3pr.html https://segmentfault.com/q/1010000005710307/a-1020000 ...
  • 前言 使用layui的圖片懶載入,發現未載入的圖片沒有loading占點陣圖,顯示的是裂圖,看著不是很好。找了一些解決方法我統一記錄一下。 layui圖片懶載入使用方法 loading占點陣圖實現方法 img標簽src為loading占點陣圖的地址,lay-src為正圖地址,圖片懶載入時會替換src 修改 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...