详解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 相关文章推荐
用javascript实现给图片加链接
Aug 15 Javascript
javascript函数以及基础写法100多条实用整理
Jan 13 Javascript
关于JQuery($.load)事件的用法和分析
Apr 09 Javascript
js confirm()方法的使用方法实例
Jul 13 Javascript
VUE中的无限循环代码解析
Sep 22 Javascript
js实现各浏览器全屏代码实例
Jul 03 Javascript
JS实现的简单分页功能示例
Aug 23 Javascript
Vue中使用vux配置代码详解
Sep 16 Javascript
vue-cli基础配置及webpack配置修改的完整步骤
Oct 20 Javascript
vue 中 elment-ui table合并上下两行相同数据单元格
Dec 26 Javascript
JS实现前端动态分页码代码实例
Jun 02 Javascript
解决vue项目input输入框双向绑定数据不实时生效问题
Aug 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
与空气斗智斗勇的经典《Overlord》,传说中的“无稽之谈”
2020/04/09 日漫
PHP个人网站架设连环讲(三)
2006/10/09 PHP
PHP时间戳使用实例代码
2008/06/07 PHP
初学CAKEPHP 基础教程
2009/11/02 PHP
destoon实现调用自增数字从1开始的方法
2014/08/21 PHP
Yii框架批量插入数据扩展类的简单实现方法
2017/05/23 PHP
Laravel模型事件的实现原理详解
2018/03/14 PHP
让whoops帮我们告别ThinkPHP6的异常页面
2020/03/02 PHP
给jqGrid数据行添加修改和删除操作链接(之一)
2011/11/04 Javascript
Javascript连接多个数组不用concat来解决
2014/03/24 Javascript
ajax请求乱码的解决方法(中文乱码)
2014/04/10 Javascript
jQuery+PHP实现可编辑表格字段内容并实时保存
2015/10/09 Javascript
基于js实现checkbox批量选中操作
2016/11/22 Javascript
JS与jQuery实现子窗口获取父窗口元素值的方法
2017/04/17 jQuery
用js屏蔽被http劫持的浮动广告实现方法
2017/08/10 Javascript
在vue中读取本地Json文件的方法
2018/09/06 Javascript
jquery 动态遍历select 赋值的实例
2018/09/12 jQuery
解决vue加scoped后就无法修改vant的UI组件的样式问题
2020/09/07 Javascript
如何管理Vue中的缓存页面
2021/02/06 Vue.js
[01:55]2014DOTA2国际邀请赛快报:国土生病 紧急去医院治疗
2014/07/10 DOTA
[01:29]Ti4循环赛第三日精彩回顾
2014/07/13 DOTA
[01:04:06]DOTA2上海特级锦标赛A组资格赛#2 Secret VS EHOME第一局
2016/02/26 DOTA
使用beaker让Facebook的Bottle框架支持session功能
2015/04/23 Python
详解python 拆包可迭代数据如tuple, list
2017/12/29 Python
Python 网络编程之UDP发送接收数据功能示例【基于socket套接字】
2019/10/11 Python
Python常用模块函数代码汇总解析
2020/08/31 Python
百联网上商城:i百联
2017/01/28 全球购物
Tory Burch德国官网:美国时尚生活品牌
2018/01/03 全球购物
斯德哥尔摩通票:Stockholm Pass
2018/01/09 全球购物
美国社交购物市场:MassGenie
2019/02/18 全球购物
高级3D打印市场:Gambody
2019/12/26 全球购物
大学生职业规划前言模板
2013/12/27 职场文书
医学生临床实习自我评价
2014/03/07 职场文书
Python获取百度热搜的完整代码
2021/04/07 Python
常用的Python代码调试工具总结
2021/06/23 Python
mybatis 获取更新记录的id
2022/05/20 Java/Android