详解vue.js之props传递参数


Posted in Javascript onDecember 12, 2017

本篇文章通过demo实例给大家详细分析了props传递参数的用法以及遇到问题后的解决办法,以下是全部内容。

前段时间用vue做一个后台管理系统,其中每一页都需要一个表格来展示信息。自然就想到了将表格提取出来做成公共组件,将不同页面的数据传入进行渲染,达到复用的目的。

demo地址

1. 问题发现

在父组件中,需要向表格组件传递的数据有表格的内容数据tableData,表格的页面数据page。

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

其中tableData是个Array对象,是所有需要在表格中展示的数据对象组成的一个数组。而pageInfo是个Object对象,包含了表格页面信息。在父组件对两个数据对如下初始化,形式如下

tableData:[],
pageInfo: {
 current: 1, // 当前是第几页
 total: 100, // 数据对象的总数
 size: 20 // 每页显示的数量
}

 

按照官方文档上的说明,prop是单向绑定的,不应该在子组件内部改变prop。之所以有想修改prop中数据的冲动,主要是prop作为初始值传入后,子组件想把它当作局部数据来用。对于这种情况,官方的说法是定义一个局部变量,并用 prop 的值初始化它:

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

然后根据官方文档的说法,当每次父组件更新时,子组件的所有prop都会更新为最新值。而tableData和pageInfo的信息是异步通过api从server端获取的:

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

 

因此当获取到数据时父组件需要改变传入子组件中的值:

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

按理说这时候子组件中的值应该更新成server返回的值,但是子组件页面的总数更新了,但table数据依然是初始化时的空数组。(黑人问号???)

详解vue.js之props传递参数

2.赋值与绑定

首先需要定位数据是在哪个地方出了问题,于是我做了一个demo来定位问题。

首先看父组件与子组件中各元素的初始值:

详解vue.js之props传递参数

然后当只改变父组件中数组的引用时可以看到子组件的props数组随之改变,而子组件中绑定的数组确并没有随之改变

详解vue.js之props传递参数

因此,可以发现,问题是出在了这一步

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

 

而要弄清楚问题的根源,就得弄清楚vue文档中深入响应式原理。

详解vue.js之props传递参数

"在Vue实例的data选项中,Vue将遍历此对象所有的属性,并使用Object.defineProperty把这些属性全部转为 getter/setter","每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。"文档中说了这么一大堆,简单理解就是Vue将data选项中的vm.$data.a与DOM中的vm.a进行了双向绑定,即其中一个变化,另一个也会跟着变化。在Vue源码中是由defineReactive$$1函数实现的:

详解vue.js之props传递参数

但在子其中主要是利用了Object.defineProperty的get和set方法实现了双向绑定。而在子组件中,pros数据和子组件的$data是通过如下方式联系在一起的:

tData: this.tableData

 

查询Vue源码可知this.tableData与tData之间仅仅是赋值,即"="关系

详解vue.js之props传递参数

而上述的initData函数是在组件构建时候执行的,因此只会在create时执行一次。这也是为什么官方文档中"作为初始值传入"这一说法,因为他本就只会执行一次。当组件构建完成后,this.tableData与tData就没有半毛钱关系了,其中一个的变化并不会引起另一个变化。当然,这种说法并不准确,因为在上文中,我们动态改变父组件传入的total,子组件也"随之"改变,感觉就像是绑定在一起了啊,这又是怎么回事呢?

3.引用类型带来的假象

当然,我们还是要从官方文档出发来解决这个问题。文档中有这样一个提示:

详解vue.js之props传递参数

这里就需要理解引用类型的概念,引用数据类型值指保存在堆内存中的对象。也就是,变量中保存的实际上的只是一个指针,这个指针指向内存中的另一个位置,该位置保存着对象。访问方式是按引用访问。例如一个js对象a,他在内存中的存储形式如下图所示:

var a = new Object();

 

详解vue.js之props传递参数

当操作时,需要先从栈中读取内存地址,然后再延指针找到保存在堆内存中的值再操作。

a.name = 'xz';

 

详解vue.js之props传递参数
引用类型变量赋值,本质上赋值的是存储在栈中的指针,将指针复制到栈中未新变量分配的空间中,而这个指针副本和原指针指向存储在堆中的同一个对象;赋值操作结束后,两个变量实际上将引用同一个对象。因此,在使用时,改变其中的一个变量的值,将影响另一个变量。

var b = a;

 

详解vue.js之props传递参数
在了解了引用类型之后,我们在来看看上文提到的动态改变传入子组件前后内存中的情况:

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

 

首先对tableData的改变是改变了其引用的指针,而对pageInfo则改变了其中一个属性的值,因此动态改变前:

详解vue.js之props传递参数

动态改变后:

详解vue.js之props传递参数

这样就解释了为什么子组件页面的总数更新了,但table数据依然是初始化时的空数组。因为引用类型的存在,我们动态改变父组件传入的total,子组件也"随之"改变了。

Javascript 相关文章推荐
Jquery AJAX POST与GET之间的区别
Nov 14 Javascript
jquery showModelDialog的使用方法示例详解
Nov 19 Javascript
JavaScript获取网页表单提交方式的方法
Apr 02 Javascript
PHP+jQuery+Ajax+Mysql如何实现发表心情功能
Aug 06 Javascript
深入理解jQuery layui分页控件的使用
Aug 17 Javascript
jQuery无缝轮播图代码
Dec 22 Javascript
微信小程序 生命周期函数详解
May 24 Javascript
js移动端事件基础及常用事件库详解
Aug 15 Javascript
vue.js获得当前元素的文字信息方法
Mar 09 Javascript
基于Vue+element-ui 的Table二次封装的实现
Jul 20 Javascript
bootstrap实现点击删除按钮弹出确认框的实例代码
Aug 16 Javascript
如何实现iframe父子传参通信
Feb 05 Javascript
react实现菜单权限控制的方法
Dec 11 #Javascript
Angular 作用域scope的具体使用
Dec 11 #Javascript
angularjs实现柱状图动态加载的示例
Dec 11 #Javascript
Vue响应式原理深入解析及注意事项
Dec 11 #Javascript
js时间戳与日期格式之间转换详解
Dec 11 #Javascript
js时间戳与日期格式之间相互转换
Dec 11 #Javascript
vue.js默认路由不加载linkActiveClass问题的解决方法
Dec 11 #Javascript
You might like
php empty函数 使用说明
2009/08/10 PHP
PHP邮箱验证示例教程
2016/06/01 PHP
详谈phpAdmin修改密码后拒绝访问的问题
2017/04/03 PHP
laravel 框架结合关联查询 when()用法分析
2019/11/22 PHP
javascript (用setTimeout而非setInterval)
2011/12/28 Javascript
简单的Jquery全选功能
2013/11/07 Javascript
JavaScript中实现PHP的打乱数组函数shuffle实例
2014/10/11 Javascript
Jquery中find与each方法用法实例
2015/02/04 Javascript
jQuery实现带有动画效果的回到顶部和底部代码
2015/11/04 Javascript
jQuery stop()用法实例详解
2016/07/28 Javascript
使用JavaScript获取URL中的参数(两种方法)
2016/11/16 Javascript
BetterScroll 在移动端滚动场景的应用
2017/09/18 Javascript
删除table表格行的实例讲解
2017/09/21 Javascript
Node.js 中使用 async 函数的方法
2017/11/20 Javascript
使用Vue完成一个简单的todolist的方法
2017/12/01 Javascript
Vue下路由History模式打包后页面空白的解决方法
2018/06/29 Javascript
详解nvm管理多版本node踩坑
2019/07/26 Javascript
[02:19]DOTA2女子战队FOX视频专访:希望更多美眉一起加入
2013/10/15 DOTA
简单谈谈python中的多进程
2016/11/06 Python
Python3删除排序数组中重复项的方法分析
2019/01/31 Python
python爬取cnvd漏洞库信息的实例
2019/02/14 Python
详解python列表(list)的使用技巧及高级操作
2019/08/15 Python
PyCharm中如何直接使用Anaconda已安装的库
2020/05/28 Python
Elasticsearch py客户端库安装及使用方法解析
2020/09/14 Python
英国翻新电子产品购物网站:Tech Trade
2017/12/25 全球购物
怎样创建、运行java程序
2014/08/01 面试题
致跳高运动员加油稿
2014/02/12 职场文书
新品发布会主持词
2014/04/02 职场文书
环保建议书500字
2014/05/14 职场文书
领导干部遵守党的政治纪律情况思想汇报
2014/09/14 职场文书
2015年电工工作总结
2015/04/10 职场文书
《和时间赛跑》读后感3篇
2019/12/16 职场文书
2019年中学生的思想品德评语集锦
2019/12/19 职场文书
解析在浏览器地址栏输入一个URL后发生了什么
2021/06/21 Servers
OpenCV实现普通阈值
2021/11/17 Java/Android
PostgreSQL13基于流复制搭建后备服务器的方法
2022/01/18 PostgreSQL