详解如何在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 相关文章推荐
JS查看对象功能代码
Apr 25 Javascript
Jquery实现搜索框提示功能示例代码
Aug 13 Javascript
javascript 获取图片尺寸及放大图片
Sep 04 Javascript
JS延迟加载加快页面打开速度示例代码
Dec 30 Javascript
JS运动基础框架实例分析
Mar 03 Javascript
JavaScript通过setTimeout实时显示当前时间的方法
Apr 16 Javascript
通过实例理解javascript中没有函数重载的概念
Jun 03 Javascript
AngularJS的表单使用详解
Jun 17 Javascript
详解javascript事件冒泡
Jan 09 Javascript
对Js OOP编程 创建对象的一些全面理解
Jul 26 Javascript
React-Native做一个文本输入框组件的实现代码
Aug 10 Javascript
Element Cascader 级联选择器的使用示例
Jul 27 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
DISCUZ在win2003环境下 Unable to access ./include/common.inc.php in... 的问题终极解决方案
2011/11/21 PHP
PHP实现的MongoDB数据库操作类分享
2014/05/12 PHP
ecshop后台编辑器替换成ueditor编辑器
2015/03/03 PHP
CodeIgniter分页类pagination使用方法示例
2016/03/28 PHP
Laravel中encrypt和decrypt的实现方法
2017/09/24 PHP
php 与 nginx 的处理方式及nginx与php-fpm通信的两种方式
2018/09/28 PHP
Javascript的匿名函数小结
2009/12/31 Javascript
详解jquery uploadify 上传文件
2013/11/09 Javascript
探讨JavaScript中声明全局变量三种方式的异同
2013/12/03 Javascript
JQuery通过AJAX从后台获取信息显示在表格上并支持行选中
2015/09/15 Javascript
浅谈JavaScript中面向对象的的深拷贝和浅拷贝
2016/08/01 Javascript
JavaScript中全选、全不选、反选、无刷新删除、批量删除、即点即改入库(在yii框架中操作)的代码分享
2016/11/01 Javascript
JavaScript遍历Json串浏览器输出的结果不统一问题
2016/11/03 Javascript
JS实现简单的二元方程计算器功能示例
2017/01/03 Javascript
在点击div中的p时,如何阻止事件冒泡
2017/02/07 Javascript
详解vue的数据binding绑定原理
2017/04/12 Javascript
使用node.js搭建服务器
2017/05/20 Javascript
Vue官方文档梳理之全局配置
2017/11/22 Javascript
vue微信分享 vue实现当前页面分享其他页面
2017/12/02 Javascript
js实现点击按钮复制文本功能
2020/07/20 Javascript
Vue-input框checkbox强制刷新问题
2019/04/18 Javascript
微信小程序动画组件使用解析,类似vue,且更强大
2019/08/01 Javascript
Node.js fs模块(文件模块)创建、删除目录(文件)读取写入文件流的方法
2019/09/03 Javascript
[43:58]DOTA2-DPC中国联赛定级赛 LBZS vs SAG BO3第一场 1月8日
2021/03/11 DOTA
深入解读Python解析XML的几种方式
2016/02/16 Python
python使用PyQt5的简单方法
2019/02/27 Python
PowerBI和Python关于数据分析的对比
2019/07/11 Python
postman模拟访问具有Session的post请求方法
2019/07/15 Python
django model的update时auto_now不被更新的原因及解决方式
2020/04/01 Python
Python用类实现扑克牌发牌的示例代码
2020/06/01 Python
CSS3效果:自定义“W”形运行轨迹实例
2017/03/29 HTML / CSS
Guess荷兰官网:美国服饰品牌
2020/01/22 全球购物
static函数与普通函数有什么区别
2015/12/25 面试题
自我鉴定注意事项
2014/01/19 职场文书
药品采购员岗位职责
2014/02/08 职场文书
大学生饮品店创业计划书范文
2019/07/10 职场文书