Vue自定义组件双向绑定实现原理及方法详解


Posted in Javascript onSeptember 03, 2020

前言

无论在任何的语言或框架中,我们都提倡代码的复用性。对于Vue来说也是如此,相同的代码逻辑会被封装成组件,除了复用之外,更重要的是统一管理提高开发效率。我真就接手过一个项目,多个页面都会用到的列表,没有去封装列表组件,只要有一点改动,每个页面都得加上。很肯定的说,没有用组件化开发的Vue项目是没有灵魂的。所以如何封装一个优雅且复用性高的组件成为我们必需的技能。

Tab自定义组件

首先来看一个Tab组件的实现,看看它存在什么问题,哪里可以改进?

效果

Vue自定义组件双向绑定实现原理及方法详解

组件

<template>
 <div class="tabs">
  <div 
   class="tab-item" 
   :class="{'tab--active':item===activeName}"
   v-for="(item,index) in tabs" 
   :key="index" 
   @click="tabChange(item)">
   {{item}}
  </div>
 </div>
</template>

<script>
export default {
 props:{
  tabs:{
   type: Array,
   default: ()=> []
  },
  activeName:{
   type: String,
   default: ''
  }
 },
 methods:{
  tabChange(item){
   this.$emit('tabChange',item)
  }
 },
}
</script>

使用

<template>
 <div>
  <Tabs :tabs="tabs" :activeName="activeName" @tabChange="tabChange" />
 </div>
</template>

<script>
import Tabs from '../components/Tabs'
export default {
 components:{
  Tabs
 },
 data(){
  return{
   tabs:['黄金体验','败者食尘','绯红之王','白金之星','波纹疾走'],
   activeName: '黄金体验'
  }
 },
 methods:{
  tabChange(item){
   this.activeName = item
  }
 },
}
</script>

这个组件最大的问题就是,activeName 需要使用者额外通过事件来手动更新,假如有另一个使用者接手,在不知道这种情况下使用,会出现tab没有切换的情况。然后要去看组件内部实现,再回来修改代码,很显然这样的组件是失败的。本着所有的脏活累活都由组件实现的原则,理想的状态应该是使用者不需要管理 activeName,而是由组件内部去更新。

如何改进修改prop?

可能有人会想到,既然要由内部管理,那在组件内部修改prop的值是不是就可以了?来看下这样的做法是否可行
修改组件tabChange方法,在点击时更新prop的值

tabChange(item){
 this.activeName = item
 this.$emit('tabChange',item)
}

使用时,控制台抛出警告

Vue自定义组件双向绑定实现原理及方法详解

由于prop是单向数据流,父级prop的更新会向下流动到子组件中,相反的在子组件内部直接更新状态,会导致数据的流向不明确。例如,在父组件中有多个子组件依赖同一个属性,其中一个子组件更新该属性,会引发其余子组件发生改变,发生问题时不容易被找到,因此Vue不推荐我们这样做。另外,在父组件发生更新时,子组件的prop会被刷新为最新的值。

单向数据流: https://cn.vuejs.org/v2/guide/components-props.html#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81

正解:model选项改进组件

组件model选项

允许一个自定义组件在使用 v-model 时定制 prop 和 event。默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。

model: https://cn.vuejs.org/v2/api/

在model选项里,我们可以绑定一个属性,并为其添加事件,只需在调用方法时传入值即可更新属性。

<script>
export default {
 model:{
  prop: 'activeName',
  event: 'update'
 },
 props:{
  tabs:{
   type: Array,
   default: ()=> []
  },
  activeName:{
   type: String,
   default: ''
  }
 },
 methods:{
  tabChange(item){
   this.$emit('update',item) // 这里更新activeName
   this.$emit('tabChange',item)
  }
 }
}
</script>

注意你仍然需要在组件的 props 选项里声明 prop。

使用

使用组件双向绑定后,属性在组件内部被更新时,父组件的 activeName 也会随之更新,这样使用者可以很明确的知道数据可能会被修改。

<Tabs :tabs="tabs" v-model="activeName" />

总结

使用组件的model选项实现自定义组件双向绑定,在组件内部通过事件更新属性值,这样的自定义组件使用起来更优雅。其实通过model选项的方式去修改父级属性,我认为有点违反了单向数据流的原则。本来单向数据流是不允许子级修改父级属性的,只是使用v-model的语法糖,看起来会让数据流向显得更加明确,恰好弥补这个缺点。

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

Javascript 相关文章推荐
两个SUBMIT按钮,如何区分处理
Aug 22 Javascript
Jquery 实现checkbox全选方法
Jan 28 Javascript
jQuery禁用键盘后退屏蔽F5刷新及禁用右键单击
Jan 22 Javascript
js Canvas实现圆形时钟教程
Sep 19 Javascript
HTML Table 空白单元格补全的简单实现
Oct 13 Javascript
JS判断是否为JSON对象及是否存在某字段的方法(推荐)
Nov 29 Javascript
angular forEach方法遍历源码解读
Jan 25 Javascript
ES6 中可以提升幸福度的小功能
Aug 06 Javascript
解决layui数据表格Date日期格式的回显Object的问题
Sep 19 Javascript
使用layui的layer组件做弹出层的例子
Sep 27 Javascript
原生JS实现记忆翻牌游戏
Jul 31 Javascript
JavaScript中layim之整合右键菜单的示例代码
Feb 06 Javascript
Vue js with语句原理及用法解析
Sep 03 #Javascript
Vue通过provide inject实现组件通信
Sep 03 #Javascript
Vue组件通信$attrs、$listeners实现原理解析
Sep 03 #Javascript
Vue父组件监听子组件生命周期
Sep 03 #Javascript
JavaScript 几种循环方式以及模块化的总结
Sep 03 #Javascript
Vuejs通过拖动改变元素宽度实现自适应
Sep 02 #Javascript
Vue Object.defineProperty及ProxyVue实现双向数据绑定
Sep 02 #Javascript
You might like
php 破解防盗链图片函数
2008/12/09 PHP
如何在symfony中导出为CSV文件中的数据
2011/10/06 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(四)
2014/06/23 PHP
PHP面向对象之事务脚本模式(详解)
2017/06/07 PHP
php日志函数error_log用法实例分析
2019/09/23 PHP
Prototype 学习 Prototype对象
2009/07/12 Javascript
高亮显示web页表格行的javascript代码
2010/11/19 Javascript
JQuery扩展插件Validate—6 radio、checkbox、select的验证
2011/09/05 Javascript
分享Javascript中最常用的55个经典小技巧
2013/11/29 Javascript
JavaScript中判断原生函数检查function是否是原生代码
2014/09/09 Javascript
JS+CSS实现自适应选项卡宽度的圆角滑动门效果
2015/09/15 Javascript
JavaScript的Number对象的toString()方法
2015/12/18 Javascript
nodejs读写json文件的简单方法(必看)
2017/03/09 NodeJs
微信小程序 setData使用方法及常用错误解决办法
2017/05/11 Javascript
AngularJS通过ng-Img-Crop实现头像截取的示例
2017/08/17 Javascript
用最少的JS代码写出贪吃蛇游戏
2018/01/12 Javascript
使用微信SDK自定义分享的方法
2019/07/03 Javascript
vuex实现数据状态持久化
2019/11/11 Javascript
js编写简易的计算器
2020/07/29 Javascript
用Python实现一个简单的多线程TCP服务器的教程
2015/05/05 Python
理解Python垃圾回收机制
2016/02/12 Python
Python3实现爬取简书首页文章标题和文章链接的方法【测试可用】
2018/12/11 Python
python之当你发现QTimer不能用时的解决方法
2019/06/21 Python
Python实现生成密码字典的方法示例
2019/09/02 Python
Python Sphinx使用实例及问题解决
2020/01/17 Python
pytorch 状态字典:state_dict使用详解
2020/01/17 Python
python 常用日期处理-- datetime 模块的使用
2020/09/02 Python
利用CSS3实现圆角的outline效果的教程
2015/06/05 HTML / CSS
linux面试题参考答案(3)
2012/09/13 面试题
火锅店的活动方案
2014/08/15 职场文书
夫妻双方自愿离婚协议书怎么写
2014/12/01 职场文书
自我评价优缺点范文
2015/03/11 职场文书
2015年计划生育责任书
2015/05/08 职场文书
婚宴祝酒词大全
2015/08/10 职场文书
创业开店,这样方式更合理
2019/08/26 职场文书
java中为什么说子类的构造方法默认访问的是父类的无参构造方法
2022/04/13 Java/Android