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
相当于给子组件一个名为 value
的 prop
,再接受子组件 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>
作者:a小磊_
链接:https://www.jianshu.com/p/3c86ad047a83