vue.js之props傳遞參數淺析

vue.js之props傳遞參數淺析

前段時間用vue作一個後臺管理系統,其中每一頁都須要一個表格來展現信息。天然就想到了將表格提取出來作成公共組件,將不一樣頁面的數據傳入進行渲染,達到複用的目的。html

demo地址vue

1. 問題發現

在父組件中,須要向表格組件傳遞的數據有表格的內容數據tableData,表格的頁面數據page。react

<div class="content">
    <my-table :table-data="tableData" :page-info="pageInfo" id="myTable"></my-table>
</div>

其中tableData是個Array對象,是全部須要在表格中展現的數據對象組成的一個數組。而pageInfo是個Object對象,包含了表格頁面信息。在父組件對兩個數據對以下初始化,形式以下git

tableData:[],
pageInfo: {
    current: 1, // 當前是第幾頁
    total: 100, // 數據對象的總數
    size: 20 // 每頁顯示的數量
}

按照官方文檔上的說明,prop是單向綁定的,不該該在子組件內部改變prop。之因此有想修改prop中數據的衝動,主要是prop做爲初始值傳入後,子組件想把它看成局部數據來用。對於這種狀況,官方的說法是定義一個局部變量,並用 prop 的值初始化它:github

props: ['tableData', 'pageInfo'],
data() {
    return {
        tData: this.tableData,
        page: this.pageInfo
    }
}

而後根據官方文檔的說法,當每次父組件更新時,子組件的全部prop都會更新爲最新值。而tableData和pageInfo的信息是異步經過api從server端獲取的:segmentfault

{
    error: 0,
    msg: "調用成功.",
    data: {
        restrictioninfo: [...],
        total: 42
    }
}

所以當獲取到數據時父組件須要改變傳入子組件中的值:api

me.tableData = Json.data.restrictioninfo;
me.pageInfo.total = Json.data.total;

按理說這時候子組件中的值應該更新成server返回的值,可是子組件頁面的總數更新了,但table數據依然是初始化時的空數組。(黑人問號???)
image
數組

2.賦值與綁定

首先須要定位數據是在哪一個地方出了問題,因而我作了一個demo來定位問題。
首先看父組件與子組件中各元素的初始值:
image
而後當只改變父組件中數組的引用時能夠看到子組件的props數組隨之改變,而子組件中綁定的數組確並無隨之改變image
所以,能夠發現,問題是出在了這一步異步

props: ['tableData', 'pageInfo'],
data() {
    return {
        tData: this.tableData,
        page: this.pageInfo
    }
}

而要弄清楚問題的根源,就得弄清楚vue文檔中深刻響應式原理
image
"在Vue實例的data選項中,Vue將遍歷此對象全部的屬性,並使用Object.defineProperty把這些屬性所有轉爲 getter/setter","每一個組件實例都有相應的 watcher 實例對象,它會在組件渲染的過程當中把屬性記錄爲依賴,以後當依賴項的 setter 被調用時,會通知 watcher 從新計算,從而導致它關聯的組件得以更新。"文檔中說了這麼一大堆,簡單理解就是Vue將data選項中的vm.$data.a與DOM中的vm.a進行了雙向綁定,即其中一個變化,另外一個也會跟着變化。在Vue源碼中是由defineReactive$$1函數實現的:
imageide

但在子其中主要是利用了Object.defineProperty的get和set方法實現了雙向綁定。而在子組件中,pros數據和子組件的$data是經過以下方式聯繫在一塊兒的:

tData: this.tableData

查詢Vue源碼可知this.tableData與tData之間僅僅是賦值,即"="關係
image

而上述的initData函數是在組件構建時候執行的,所以只會在create時執行一次。這也是爲何官方文檔中"做爲初始值傳入"這一說法,由於他本就只會執行一次。當組件構建完成後,this.tableData與tData就沒有半毛錢關係了,其中一個的變化並不會引發另外一個變化。固然,這種說法並不許確,由於在上文中,咱們動態改變父組件傳入的total,子組件也"隨之"改變,感受就像是綁定在一塊兒了啊,這又是怎麼回事呢?

3.引用類型帶來的假象

固然,咱們仍是要從官方文檔出發來解決這個問題。文檔中有這樣一個提示:
image
這裏就須要理解引用類型的概念,引用數據類型值指保存在堆內存中的對象。也就是,變量中保存的實際上的只是一個指針,這個指針指向內存中的另外一個位置,該位置保存着對象。訪問方式是按引用訪問。例如一個js對象a,他在內存中的存儲形式以下圖所示:

var a = new Object();

image

當操做時,須要先從棧中讀取內存地址,而後再延指針找到保存在堆內存中的值再操做。

a.name = 'xz';

image

引用類型變量賦值,本質上賦值的是存儲在棧中的指針,將指針複製到棧中未新變量分配的空間中,而這個指針副本和原指針指向存儲在堆中的同一個對象;賦值操做結束後,兩個變量實際上將引用同一個對象。所以,在使用時,改變其中的一個變量的值,將影響另外一個變量。

var b = a;

image

在瞭解了引用類型以後,咱們在來看看上文提到的動態改變傳入子組件先後內存中的狀況:

me.tableData = Json.data.restrictioninfo;
me.pageInfo.total = Json.data.total;
========================================
props: ['tableData', 'pageInfo'],
data() {
    return {
        tData: this.tableData,
        page: this.pageInfo
    }
}

首先對tableData的改變是改變了其引用的指針,而對pageInfo則改變了其中一個屬性的值,所以動態改變前:
image
動態改變後:
image這樣就解釋了爲何子組件頁面的總數更新了,但table數據依然是初始化時的空數組。由於引用類型的存在,咱們動態改變父組件傳入的total,子組件也"隨之"改變了。

相關文章
相關標籤/搜索