详解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 typeof 用法
Dec 28 Javascript
理解Javascript_01_理解内存分配原理分析
Oct 11 Javascript
jQuery实现的Div窗口震动特效
Jun 09 Javascript
jquery datatable后台封装数据示例代码
Aug 07 Javascript
jQuery 1.9.1源码分析系列(十)事件系统之绑定事件
Nov 19 Javascript
DeviceOne 让你一见钟情的App快速开发平台
Feb 17 Javascript
js仿百度登录页实现拖动窗口效果
Mar 11 Javascript
Vue完整项目构建(进阶篇)
Feb 10 Javascript
Vue.set() this.$set()引发的视图更新思考及注意事项
Aug 30 Javascript
jquery图片预览插件实现方法详解
Jul 18 jQuery
vue 实现路由跳转时更改页面title
Nov 05 Javascript
vue中v-for循环选中点击的元素并对该元素添加样式操作
Jul 17 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
这东西价格,可以买几台TECSUN S-2000
2021/03/02 无线电
深入解析php之sphinx
2013/05/15 PHP
PHP读取文件内容的五种方式
2015/12/28 PHP
[原创]php简单隔行变色功能实现代码
2016/07/09 PHP
php json相关函数用法示例
2017/03/28 PHP
yii2中关于加密解密的那些事儿
2018/06/12 PHP
浅谈php调用python文件
2019/03/29 PHP
跟着Jquery API学Jquery之一 选择器
2010/04/07 Javascript
基于jquery的图片的切换(以数字的形式)
2011/02/14 Javascript
为EasyUI的Tab标签添加右键菜单的方法
2012/07/14 Javascript
在JavaScript中重写jQuery对象的方法实例教程
2014/08/25 Javascript
javascript实现无限级select联动菜单
2015/01/02 Javascript
基于RequireJS和JQuery的模块化编程日常问题解析
2016/04/14 Javascript
Javascript使用function创建类的两种方法(推荐)
2016/11/19 Javascript
JS如何设置iOS中微信浏览器的title
2016/11/22 Javascript
js+html5实现半透明遮罩层弹框效果
2020/08/24 Javascript
Iphone手机、安卓手机浏览器控制默认缩放大小的方法总结(附代码)
2017/08/18 Javascript
Vue.js实现按钮的动态绑定效果及实现代码
2017/08/21 Javascript
JavaScript基于对象方法实现数组去重及排序操作示例
2018/07/10 Javascript
JS实现把一个页面层数据传递到另一个页面的两种方式
2018/08/13 Javascript
Python新手在作用域方面经常容易碰到的问题
2015/04/03 Python
Python中的zipfile模块使用详解
2015/06/25 Python
Python爬虫框架scrapy实现downloader_middleware设置proxy代理功能示例
2018/08/04 Python
Python实现二维曲线拟合的方法
2018/12/29 Python
python 利用pandas将arff文件转csv文件的方法
2019/02/12 Python
python 判断三个数字中的最大值实例代码
2019/07/24 Python
Agoda台湾官网:国内外订房2折起
2018/03/20 全球购物
计算机数据库专业职业生涯规划书
2014/02/08 职场文书
社区班子个人对照检查材料思想汇报
2014/10/07 职场文书
公务员年终个人总结
2015/02/12 职场文书
2016年国庆节67周年活动总结
2016/04/01 职场文书
新西兰:最新留学学习计划书写作指南
2019/07/15 职场文书
Python实现byte转integer
2021/06/03 Python
用python修改excel表某一列内容的操作方法
2021/06/11 Python
Oracle 触发器trigger使用案例
2022/02/24 Oracle
Hive导入csv文件示例
2022/06/25 数据库