Vue2.x 中实现父子组件间的双向绑定

Vue2.x 中父子组件中 props 的属性不能在子组件中改变其值然后传出至父组件了,但是有的时候也会存在一些麻烦,比如我们想要实现一些自定义组件,总是会有值的传入传出改变的,比如我希望做一个 popup,那么肯定需要根据父节点的状态来确定子节点。

尽管官方推荐双向数据流,但还是有一些方法避免报错,实现或 hack 双向数据流:

Event Bus / Vuex

最简单的当然是通过状态管理来管理我们的变量,但对于数据流不复杂的父子节点交互而言,未免太过浪费,因此通常我们不会特别的去考虑它,尤其是 popup / checkbox 这种基础组件的时候。

使用 Object

Object 的值是其内存地址,只改变 Object 内部的值不改变地址将不会出现报错。

使用 Event-Emit 处理

尽管不可以直接修改值,但是通过 event emit,我们在子组件改变其值时(可以通过 watch,事件监听等方法监听,在子组件中用this.$emit('eventName', value) 传入,在父组件中绑定 @event-name,在函数中修改值。但是这样如果是input基础组件,写函数就得写到疯——不靠谱。

v-model

所幸我们还有一个叫做 v-model 的语法糖:v-model 相当于给子组件一个名为 valueprop,再接受子组件 input 事件并赋值给父组件的对应变量。

  • 所以我们需要做以下几件事情:

    1.子组件定义名为 value 的 prop 值
    2.在需要改变父组件值时发送 input 事件。

但是在子组件内我们要先变动一个作用域为子组件的值,才能使用监听,所以我们绑定一个 _value,之后在父组件就能使用 v-model 了。:

computed: {
 _value: {
   get() {
     return this.value;
   },
   set(val) {
     this.$emit('input', val);
     this.$emit('change', val);
   }
 }

一个完整的基础组件代码:

<template>
  <label class="radio">    
    <input type="radio" :name="name" v-model="_value" :value="label" :disabled="disabled">
    <div>{{ text }}</div>
  </label>
</template>
<style lang="scss" scoped>
  .radio {
    display: block;
    height: 50px;
    margin-bottom: 10px;
    input[type=radio] {
      display: none;
    }
    div {
      position: relative;
      line-height: 30px;
      top: -10px;
      right: 20px;
    }
    div:after {
      position: relative;
      right: -20px;
      top: 10px;
      transition: .1s;
    }
    input[type=radio] + div:after {
      content: '';
      display: inline-block;
      border: 1px solid #b3bbc2;
      height: 30px;
      width: 30px;
      border-radius: 50%;
    }
    input[type=radio]:checked + div:after {
      border: 10px solid #7ebcff;
      height: 30px;
      width: 30px;
    }
  }
</style>
<script>
  export default {
    name: 'CustomRadio',
    props: {
      value: {
        type: [ Boolean, String, Number ]
      },
      text: {
        type: String,
        required: false
      },
      name: {
        type: String,
        default: ''
      },
      disabled: {
        type: Boolean,
        required: false,
        default: false
      },
      label: {
        type: [ Boolean, String, Number ],
        required: false
      }
    },
    computed: {
      _value: {
        get() {
          return this.value;
        },
        set(val) {
          this.$emit('input', val);
          this.$emit('change', val);
        }
      }
    }
  };
</script>
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 前言 您将在本文当中了解到,往网页中添加数据,从传统的dom操作过渡到数据层操作,实现同一个目标,两种不同的方式....
    itclanCoder阅读 26,212评论 1 12
  • 组件(Component)是Vue.js最核心的功能,也是整个架构设计最精彩的地方,当然也是最难掌握的。...
    六个周阅读 5,746评论 0 32
  • 这篇笔记主要包含 Vue 2 不同于 Vue 1 或者特有的内容,还有我对于 Vue 1.0 印象不深的内容。关于...
    云之外阅读 5,159评论 0 29
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,368评论 19 139
  • 清晨五点 不知情的闹钟叫着 灵魂酣睡的身体 门外湿润的空气,小雨 留着气息延伸睡意 …… 操作间咆哮的压面机 像是...
    攀伟阅读 386评论 2 2

友情链接更多精彩内容