详解如何在Vue2中实现组件props双向绑定


Posted in Javascript onMarch 29, 2017

Vue学习笔记-3 前言

Vue 2.x相比较Vue 1.x而言,升级变化除了实现了Virtual-Dom以外,给使用者最大不适就是移除的组件的props的双向绑定功能。

以往在Vue1.x中利用propstwoWay.sync绑定修饰符就可以实现props的双向绑定功能,但是在Vue2中彻底废弃了此功能,如果需要双向绑定需要自己来实现。

Vue2的组件props通信方式

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

详解如何在Vue2中实现组件props双向绑定 

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

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

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

案例

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

1.点击按钮实现 开/关 状态切换

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

详解如何在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。

1.组件内部需要修改props。

2.组件需要可以由外部在运行时动态控制,而非单纯初始化。

3.组件外部需要读取组件内的状态来进行处理

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

自动化的props双向绑定处理

Vue的mixin组件——propsync

通过上例也可以看出在Vue2.0中实现props的双向绑定很麻烦,如果有两个props需要做双向绑定上面的代码就要实现两遍,代码极其冗余。

 所以我写了一个mixin来自动化处理props的双向绑定的需求——propsync

主要功能

1.实现了在组件内自动创建所有prop对应的data属性,方便组件内修改prop使用。解决了vue2.0中不允许组件内直接修改prop的设计。

2.实现了组件外修改组件prop,组件内自动同步修改到data属性。

3.实现了组件内修改了data属性(由prop创建的),自动向组件外发出事件通知有内部prop修改。由组件外决定是否要将修改同步到组件外

propsync的使用方法

编写组件

1.对于编写组件时,如果需要props双向绑定,则直接引入mixin,并在配置中声明mixin即可: mixins: [propsync]

2.此mixin会根据prop的名称生成对应的data属性名,默认为在prop属性名前面增加"p_",即若prop中有字段名为active,则自动生成名为p_active的data字段(props到data的名称变更方法可自行修改,详见propsync源码开头配置)

3.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
  }
 }
}

调用组件

1.引入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代码:vue_3water.rar

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

Javascript 相关文章推荐
关于this和self的使用说明
Aug 01 Javascript
读jQuery之十一 添加事件核心方法
Jul 31 Javascript
js和html5实现手机端刮刮卡抽奖效果完美兼容android/IOS
Nov 18 Javascript
js检测浏览器版本、核心、是否移动端示例
Apr 24 Javascript
网页中表单按回车就自动提交的问题的解决方案
Nov 03 Javascript
JavaScript基础知识学习笔记
Dec 02 Javascript
Node.js中安全调用系统命令的方法(避免注入安全漏洞)
Dec 05 Javascript
Bootstrap modal 多弹窗之叠加引起的滚动条遮罩阴影问题
Feb 27 Javascript
Vue中img的src属性绑定与static文件夹实例
May 18 Javascript
Vue编写多地区选择组件
Aug 21 Javascript
Vue项目部署的实现(阿里云+Nginx代理+PM2)
Mar 26 Javascript
Vue.js实现tab切换效果
Jul 24 Javascript
整理关于Bootstrap警示框的慕课笔记
Mar 29 #Javascript
node.js程序作为服务并在windows下开机自启动(用forever)
Mar 29 #Javascript
jQuery实现字体颜色渐变效果的方法
Mar 29 #jQuery
整理关于Bootstrap导航的慕课笔记
Mar 29 #Javascript
javascript 中Cookie读、写与删除操作
Mar 29 #Javascript
移动端利用H5实现压缩图片上传功能
Mar 29 #Javascript
浅析JS中的 map, filter, some, every, forEach, for in, for of 用法总结
Mar 29 #Javascript
You might like
十天学会php(3)
2006/10/09 PHP
php错误、异常处理机制(补充)
2012/05/07 PHP
php实现的常见排序算法汇总
2014/09/08 PHP
PHP如何使用Memcached
2016/04/05 PHP
一段实用的php验证码函数
2016/05/19 PHP
php慢查询日志和错误日志使用详解
2021/02/27 PHP
改善你的jQuery的25个步骤 千倍级效率提升
2010/02/11 Javascript
包含中国城市的javascript对象实例
2015/08/03 Javascript
jQuery实现本地预览上传图片功能
2016/01/08 Javascript
jQuery遍历DOM的父级元素、子级元素和同级元素的方法总结
2016/07/07 Javascript
Bootstrap Table使用方法详解
2016/08/01 Javascript
Angular2内置指令NgFor和NgIf详解
2016/08/03 Javascript
js实现弹窗居中的简单实例
2016/10/09 Javascript
Windows下使用Nodejs运行js的方法
2017/09/02 NodeJs
vue生命周期和react生命周期对比【推荐】
2018/09/19 Javascript
微信小程序后端(java)开发流程的详细步骤
2019/11/13 Javascript
[52:32]完美世界DOTA2联赛PWL S2 Magma vs LBZS 第三场 11.18
2020/11/18 DOTA
python调用fortran模块
2016/04/08 Python
Python使用pymongo模块操作MongoDB的方法示例
2018/07/20 Python
Python3实现的简单工资管理系统示例
2019/03/12 Python
pandas.cut具体使用总结
2019/06/24 Python
Python工程师必考的6个经典面试题
2020/06/28 Python
python 列表推导和生成器表达式的使用
2021/02/01 Python
html5中JavaScript removeChild 删除所有节点
2014/05/16 HTML / CSS
英国高档时尚男装购物网站:MR PORTER
2016/08/09 全球购物
中学生爱国演讲稿
2013/12/31 职场文书
元旦获奖感言
2014/03/08 职场文书
单位承诺书格式
2014/05/21 职场文书
小学毕业典礼演讲稿
2014/09/09 职场文书
大三学生英语考试作弊检讨书
2015/01/01 职场文书
英文导游词
2015/02/13 职场文书
食堂采购员岗位职责
2015/04/03 职场文书
尼克胡哲观后感
2015/06/08 职场文书
2016猴年开门红标语口号
2015/12/26 职场文书
python blinker 信号库
2022/05/04 Python
Oracle中日期的使用方法实例
2022/07/07 Oracle