Vue2实现组件props双向绑定


Posted in Javascript onDecember 02, 2016

Vue学习笔记-3 前言

Vue 2.x相比较Vue 1.x而言,升级变化除了实现了Virtual-Dom以外,给使用者最大不适就是移除的组件的props的双向绑定功能。
以往在Vue1.x中利用props的twoWay和.sync绑定修饰符就可以实现props的双向绑定功能,但是在Vue2中彻底废弃了此功能,如果需要双向绑定需要自己来实现。

Vue2的组件props通信方式

在Vue2中组件的props的数据流动改为了只能单向流动,即只能由组件外(调用组件方)通过组件的DOM属性attribute传递props给组件内,组件内只能被动接收组件外传递过来的数据,并且在组件内,不能修改由外层传来的props数据。

Vue2实现组件props双向绑定

关于这一点的修改官方给的解释:

prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解。

虽然废弃了props的双向绑定对于整个项目整体而言是有利且正确的,但是在某些时候我们确实需要从组件内部修改props的需求

案例

假设我要做一个iOS风格的开关按钮,需求就只有两个:

  • 点击按钮实现 开/关 状态切换
  • 不点击按钮,也可以通过外部修改数据切换开关状态,比如级联联动开关。

Vue2实现组件props双向绑定

代码大致是类似这样的:

<div id="app">
 <!--开关组件-->
 <switchbtn :result="result"></switchbtn>
 <!--外部控制-->
 <input type="button" value="change" @click="change">
</div>
//开关组件代码
Vue.component("switchbtn",{
 template:"<div @click='change'>{{result?'开':'关'}}</div>",
 props:["result"],
 methods:{
 change(){
 this.result=!this.result;
 }
 }
});

//调用组件
new Vue({
 el: "#app",
 data:{
 result:true//开关状态数据
 },
 methods:{
 change(){
 this.result=!this.result;
 }
 }
});

但是在vue2.0中上面的代码在点击开关时会报错:

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "result" (found in component )

组件内不能修改props的值,同时修改的值也不会同步到组件外层,即调用组件方不知道组件内部当前的状态是什么

在Vue2.0中,实现组件属性的双向绑定方式

1. 在组件内的data对象中创建一个props属性的副本

因为result不可写,所以需要在data中创建一个副本myResult变量,初始值为props属性result的值,同时在组件内所有需要调用props的地方调用这个data对象myResult

Vue.component("switchbtn", {
 template: "<div @click='change'>{{myResult?'开':'关'}}</div>",
 props: ["result"],
 data: function () {
 return {
 myResult: this.result//data中新增字段
 };
 },
 ......
});

2. 创建针对props属性的watch来同步组件外对props的修改

此时在组件外(父组件)修改了组件的props,会同步到组件内对应的props上,但是不会同步到你刚刚在data对象中创建的那个副本上,所以需要再创建一个针对props属性result的watch(监听),当props修改后对应data中的副本myResult也要同步数据。

Vue.component("switchbtn", {
 template: "<div @click='change'>{{myResult?'开':'关'}}</div>",
 props: ["result"],
 data: function () {
 return {
 myResult: this.result
 };
 },
 watch: {
 result(val) {
 this.myResult = val;//新增result的watch,监听变更并同步到myResult上
 }
 },
 ......

3. 创建针对props副本的watch,通知到组件外

此时在组件内修改了props的副本myResult,组件外不知道组件内的props状态,所以需要再创建一个针对props副本myResult,即对应data属性的watch。
在组件内向外层(父组件)发送通知,通知组件内属性变更,然后由外层(父组件)自己来变更他的数据

最终全部代码:

<div id="app">
 <switchbtn :result="result" @on-result-change="onResultChange"></switchbtn>
 <input type="button" value="change" @click="change">
</div>
Vue.component("switchbtn", {
 template: "<div @click='change'>{{myResult?'开':'关'}}</div>",
 props: ["result"],
 data: function () {
 return {
 myResult: this.result//①创建props属性result的副本--myResult
 };
 },
 watch: {
 result(val) {
 this.myResult = val;//②监听外部对props属性result的变更,并同步到组件内的data属性myResult中
 },
 myResult(val){
 //xxcanghai 小小沧海 博客园
 this.$emit("on-result-change",val);//③组件内对myResult变更后向外部发送事件通知
 }
 },
 methods: {
 change() {
 this.myResult = !this.myResult;
 }
 }
});

new Vue({
 el: "#app",
 data: {
 result: true
 },
 methods: {
 change() {
 this.result = !this.result;
 },
 onResultChange(val){
 this.result=val;//④外层调用组件方注册变更方法,将组件内的数据变更,同步到组件外的数据状态中
 }
 }
});

至此,实现了组件内数据与组件外的数据的双向绑定,组件内外数据的同步。最后归结为一句话就是:组件内部自己变了告诉外部,外部决定要不要变。

Vue2实现组件props双向绑定

4. 什么样的props适合做双向绑定?

首先要声明的是双向绑定的props肯定是不利于组件间的数据状态管理,尤其是在复杂的业务中更是如此,所以要尽可能的少用双向绑定,过于复杂的数据处理建议使用Vuex (http://vuex.vuejs.org/zh-cn/intro.html)

但是在我们平时使用过程中又确实有props双向绑定的需求,个人认为只有在满足以下条件时再使用双向绑定的props。

  • 组件内部需要修改props。
  • 组件需要可以由外部在运行时动态控制,而非单纯初始化。
  • 组件外部需要读取组件内的状态来进行处理

满足上述条件的有比如本例中的switch开关组件,需要外部控制开关状态;再比如Tab多标签页组件的activeIndex属性,需要可以由外部控制标签页当前打开哪一页等等

自动化的props双向绑定处理

Vue的mixin组件——propsync

通过上例也可以看出在Vue2.0中实现props的双向绑定很麻烦,如果有两个props需要做双向绑定上面的代码就要实现两遍,代码极其冗余。
所以我写了一个mixin来自动化处理props的双向绑定的需求——propsync。

主要功能

  • 实现了在组件内自动创建所有prop对应的data属性,方便组件内修改prop使用。解决了vue2.0中不允许组件内直接修改prop的设计。
  • 实现了组件外修改组件prop,组件内自动同步修改到data属性。
  • 实现了组件内修改了data属性(由prop创建的),自动向组件外发出事件通知有内部prop修改。由组件外决定是否要将修改同步到组件外

propsync的使用方法

编写组件

  • 对于编写组件时,如果需要props双向绑定,则直接引入mixin,并在配置中声明mixin即可: mixins: [propsync]
  • 此mixin会根据prop的名称生成对应的data属性名,默认为在prop属性名前面增加"p_",即若prop中有字段名为active,则自动生成名为p_active的data字段(props到data的名称变更方法可自行修改,详见propsync源码开头配置)
  • propsync默认会将所有props创建双向绑定,可通过propsync:false来声明此props不需要创建双向绑定。

例:

import propsync from './mixins/propsync';//引入mixin文件
export default {
 name: "tab",
 mixins: [propsync],//声明使用propsync的mixin
 props: {
 active: {
 type: [String, Number],//会被propsync自动实现双向绑定,在data中创建p_active变量
 },
 width: {
 type: [Number, String],
 propsync:false//不会被propsync实现双向绑定
 }
 },
 methods: {
 setActive(page, index, e) {
 this.p_active = index;//可以直接使用this.p_active
 }
 }
}

调用组件

引入propsync后,会在内部双向绑定的data变更后触发一个onPropsChange事件。遂在调用组件处,增加一个事件监听 onPropsChange(可修改),当组件内修改了props时propsync会触发此事件,返回参与依次为:修改prop名称,修改后值,修改前值。可以由当前组件调用方(父组件)来决定是否要将组件内的变更同步到调用方
例:

<tab :active="active" @onPropsChange="change"></tab>
 
......略
{
 data:{
 active:0
 },
 methods:{
 change:function(propName,newVal,oldVal){
 this[propName]=newVal;
 console.log("组件tab的" +propName+ "属性变更为" +newVal);
 } 
 }
}

源码下载

Vue的mixin组件propsync已经托管至Github:
https://github.com/xxcanghai/cnblogsFiles/blob/master/vue-mixins/propsync.js

相关笔记

Vue学习笔记-1(https://3water.com/article/98869.htm)

Vue学习笔记-2(https://3water.com/article/98878.htm)

Vue学习笔记-3 如何在Vue2中实现组件props双向绑定 (https://3water.com/article/98881.htm)

本文已被整理到了《Vue.js前端组件学习教程》,欢迎大家学习阅读。

关于vue.js组件的教程,请大家点击专题vue.js组件学习教程进行学习。

更多vue学习教程请阅读专题《vue实战教程》

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript onkeydown,onkeyup,onkeypress,onclick,ondblclick
Feb 04 Javascript
jQuery 标题的自动翻转实现代码
Oct 14 Javascript
boxy基于jquery的弹出层对话框插件扩展应用 弹出层选择器
Nov 21 Javascript
Jquery中的CheckBox、RadioButton、DropDownList的取值赋值实现代码
Oct 12 Javascript
Jquery原生态实现表格header头随滚动条滚动而滚动
Mar 18 Javascript
JavaScript表格常用操作方法汇总
Apr 15 Javascript
ECMAScript6 新特性范例大全
Mar 24 Javascript
vue.js前后端数据交互之提交数据操作详解
Apr 24 Javascript
使用 electron 实现类似新版 QQ 的登录界面效果(阴影、背景动画、窗体3D翻转)
Oct 23 Javascript
详解Vue用cmd创建项目
Feb 12 Javascript
详解JavaScript中的函数、对象
Apr 01 Javascript
Javascript类型判断相关例题及解析
Aug 26 Javascript
关于Vue.js一些问题和思考学习笔记(2)
Dec 02 #Javascript
基于jQuery实现表格的排序
Dec 02 #Javascript
利用jQuery插件imgAreaSelect实现图片上传裁剪(放大缩小)
Dec 02 #Javascript
关于Vue.js一些问题和思考学习笔记(1)
Dec 02 #Javascript
利用jQuery来动态为属性添加或者删除属性的简单方法
Dec 02 #Javascript
基于javascript实现的快速排序
Dec 02 #Javascript
微信公众平台开发教程(五)详解自定义菜单
Dec 02 #Javascript
You might like
php中使用__autoload()自动加载未定义类的实现代码
2013/02/06 PHP
php读取本地文件常用函数(fopen与file_get_contents)
2013/09/09 PHP
laravel 数据验证规则详解
2019/10/23 PHP
php设计模式之工厂方法模式分析【星际争霸游戏案例】
2020/01/23 PHP
JS判断对象是否存在的10种方法总结
2013/12/23 Javascript
jquery获取当前元素索引值用法实例
2015/06/10 Javascript
jquery实现的伪分页效果代码
2015/10/29 Javascript
在AngularJS中如何使用谷歌地图把当前位置显示出来
2016/01/25 Javascript
jquery checkbox无法用attr()二次勾选问题的解决方法
2016/07/22 Javascript
BootStrap中的表单大全
2016/09/07 Javascript
Javascript 函数的四种调用模式
2016/11/05 Javascript
jquery实现的table排序功能示例
2017/03/10 Javascript
angular中使用Socket.io实例代码
2017/06/03 Javascript
详解vue 自定义组件使用v-model 及探究其中原理
2019/10/11 Javascript
如何在JavaScript中使用localStorage详情
2021/02/04 Javascript
Python随机生成数模块random使用实例
2015/04/13 Python
Python中的with...as用法介绍
2015/05/28 Python
使用Python简单的实现树莓派的WEB控制
2016/02/18 Python
Python FTP两个文件夹间的同步实例代码
2018/05/25 Python
使用Python制作自动推送微信消息提醒的备忘录功能
2018/09/06 Python
Python如何创建装饰器时保留函数元信息
2020/08/07 Python
Python压缩模块zipfile实现原理及用法解析
2020/08/14 Python
详解利用python识别图片中的条码(pyzbar)及条码图片矫正和增强
2020/11/17 Python
python3定位并识别图片验证码实现自动登录功能
2021/01/29 Python
浅谈html5 响应式布局
2014/12/24 HTML / CSS
世界上最大的铁人三项商店:Tri UK
2020/11/04 全球购物
市场开发计划书
2014/05/07 职场文书
假面舞会策划方案
2014/05/29 职场文书
城市规划应届毕业生自荐信
2014/07/04 职场文书
2015年安全员工作总结范文
2015/04/22 职场文书
社区环境卫生倡议书
2015/04/29 职场文书
2015年高校图书馆工作总结
2015/04/30 职场文书
2015年六年级班主任工作总结
2015/10/15 职场文书
小学运动会入场口号
2015/12/24 职场文书
python实现web邮箱扫描的示例(附源码)
2021/03/30 Python
PostgreSQL并行计算算法及参数强制并行度设置方法
2022/04/06 PostgreSQL