热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

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

Vue学习笔记-3 前言 Vue 2.x相比较Vue 1.x而言,升级变化除了实现了Virtual-Dom以外,给使用者最大不适就是移除的

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数据。

 

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

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

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

案例

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

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

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

 

代码大致是类似这样的:

//开关组件代码
Vue.component("switchbtn",{
  template:"
{{result?'开':'关'}}
", 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: "
{{myResult?'开':'关'}}
", 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: "
{{myResult?'开':'关'}}
", 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。

在组件内向外层(父组件)发送通知,通知组件内属性变更,然后由外层(父组件)自己来变更他的数据

最终全部代码:

Vue.component("switchbtn", {
  template: "
{{myResult?'开':'关'}}
", 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;//④外层调用组件方注册变更方法,将组件内的数据变更,同步到组件外的数据状态中 } } });

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

 

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名称,修改后值,修改前值。可以由当前组件调用方(父组件)来决定是否要将组件内的变更同步到调用方

例:


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

源码下载

Vue的mixin组件propsync代码:vue_jb51.rar

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


推荐阅读
  • 丽江客栈选择问题
    本文介绍了一道经典的算法题,题目涉及在丽江河边的n家特色客栈中选择住宿方案。两位游客希望住在色调相同的两家客栈,并在晚上选择一家最低消费不超过p元的咖啡店小聚。我们将详细探讨如何计算满足条件的住宿方案总数。 ... [详细]
  • JSOI2010 蔬菜庆典:树结构中的无限大权值问题
    本文探讨了 JSOI2010 的蔬菜庆典问题,主要关注如何处理非根非叶子节点的无限大权值情况。通过分析根节点及其子树的特性,提出了有效的解决方案,并详细解释了算法的实现过程。 ... [详细]
  • 本文详细介绍如何使用CSS自定义HTML5视频播放器的样式,涵盖常见属性及跨浏览器兼容性问题。发布时间:2020-09-14 14:46:29;来源:亿速云;阅读量:58;作者:小新。 ... [详细]
  • Appium + Java 自动化测试中处理页面空白区域点击问题
    在进行移动应用自动化测试时,有时会遇到某些页面没有返回按钮,只能通过点击空白区域返回的情况。本文将探讨如何在Appium + Java环境中有效解决此类问题,并提供详细的解决方案。 ... [详细]
  • 如何清除Chrome浏览器地址栏的特定历史记录
    在使用Chrome浏览器时,你可能会发现地址栏保存了大量浏览记录。有时你可能希望删除某些特定的历史记录而不影响其他数据。本文将详细介绍如何单独删除地址栏中的特定记录以及批量清除所有历史记录的方法。 ... [详细]
  • 利用Selenium与ChromeDriver实现豆瓣网页全屏截图
    本文介绍了一种使用Selenium和ChromeDriver结合Python代码,轻松实现对豆瓣网站进行完整页面截图的方法。该方法不仅简单易行,而且解决了新版Selenium不再支持PhantomJS的问题。 ... [详细]
  • 本题来自WC2014,题目编号为BZOJ3435、洛谷P3920和UOJ55。该问题描述了一棵不断生长的带权树及其节点上小精灵之间的友谊关系,要求实时计算每次新增节点后树上所有可能的朋友对数。 ... [详细]
  • Ulysses Mac v29:革新文本编辑与写作体验
    探索Ulysses Mac v29,这款先进的纯文本编辑器为Mac用户带来了全新的写作和编辑环境。它不仅具备简洁直观的界面,还融合了Markdown等标记语言的最佳特性,支持多种格式导出,并提供强大的组织和同步功能。 ... [详细]
  • 本文介绍了如何使用JavaScript的Fetch API与Express服务器进行交互,涵盖了GET、POST、PUT和DELETE请求的实现,并展示了如何处理JSON响应。 ... [详细]
  • 探索新一代API文档工具,告别Swagger的繁琐
    对于后端开发者而言,编写和维护API文档既繁琐又不可或缺。本文将介绍一款全新的API文档工具,帮助团队更高效地协作,简化API文档生成流程。 ... [详细]
  • 本文探讨了在构建应用程序时,如何对不同类型的数据进行结构化设计。主要分为三类:全局配置、用户个人设置和用户关系链。每种类型的数据都有其独特的用途和应用场景,合理规划这些数据结构有助于提升用户体验和系统的可维护性。 ... [详细]
  • Python 内存管理机制详解
    本文深入探讨了Python的内存管理机制,涵盖了垃圾回收、引用计数和内存池机制。通过具体示例和专业解释,帮助读者理解Python如何高效地管理和释放内存资源。 ... [详细]
  • C#设计模式学习笔记:观察者模式解析
    本文将探讨观察者模式的基本概念、应用场景及其在C#中的实现方法。通过借鉴《Head First Design Patterns》和维基百科等资源,详细介绍该模式的工作原理,并提供具体代码示例。 ... [详细]
  • 目录一、salt-job管理#job存放数据目录#缓存时间设置#Others二、returns模块配置job数据入库#配置returns返回值信息#mysql安全设置#创建模块相关 ... [详细]
  • 本文详细介绍了优化DB2数据库性能的多种方法,涵盖统计信息更新、缓冲池调整、日志缓冲区配置、应用程序堆大小设置、排序堆参数调整、代理程序管理、锁机制优化、活动应用程序限制、页清除程序配置、I/O服务器数量设定以及编入组提交数调整等方面。通过这些技术手段,可以显著提升数据库的运行效率和响应速度。 ... [详细]
author-avatar
诗人不失神
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有