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,子组件也"随之"改变了。

相关文章
相关标签/搜索