Vue自定义组件数据的双向绑定方法
最近在学习饿了么的Vue项目制作的课件,课件内容给了我很大的启发。在开发过程中,数据的单一性可以让功能更加的安全。之前对Vue的学习,更多的停留在父子单向数据绑定,当子组件数据变化时,无法直接影响到父组件。经过这段时间的学习,慢慢找到了一些数据双向绑定的方法,稍微做了一下总结。
v-model
v-model本质上就是语法糖,具体使用方法如下:
父组件:
<!-- 父组件调用子组件 -->
<q-code v-model="isShowCode" :code-img="codeImg"></q-code>
export default {
name: 'father',
components: {
QCode
},
data () {
return {
isShowCode: false
};
}
};
子组件:
<!-- 子组件 -->
<template>
<div class="qCode-container" v-show="show">
<h1 class="popup-header" @click="closePopup">微信咨询</h1>
</div>
</template>
自定义model名的写法
export default {
name:'qCode',
model: {
prop: 'isShow', // props对应的名字
event: 'toggle' // 触发改变的事件名称
},
props: ['isShow'],
data () {
return {
show: false // 用data来控制传递过来的数据,可以防止数据改变时第一次响应失效
};
},
watch: {
isShow: function(newVal, oldVal) {
if (newVal !== oldVal) {
this.show = newVal
}
}
},
methods: {
closePopup () {
this.show = false;
this.$emit('toggle', false); // 触发父组件数据的改变
}
}
};
使用默认的value的写法
export default {
name:'qCode',
props: {
value: {
type: Boolean,
default: false
}
},
data () {
return {
show: this.value // 用data来控制传递过来的数据,可以防止数据改变时第一次响应失效
};
},
watch: {
isShow: function(newVal, oldVal) {
if (newVal !== oldVal) {
this.show = newVal
}
}
},
methods: {
closePopup () {
this.show = false;
this.$emit('input', false); // 触发父组件数据的改变
}
}
};
.sync
.sync的官方文档:sync-修饰符
父组件:
<!-- 父组件调用子组件 -->
<rating-select :select-type.sync="selectType" :only-content.sync="onlyContent"></rating-select>
export default {
name: 'father',
components: {RatingSelect},
data () {
return {
selectType: this.$store.state.RATING_CODE.ALL,
onlyContent: true
};
},
watch: {
selectType (value) {
console.log(value);
},
onlyContent (value) {
console.log(value);
}
}
};
子组件:
<!-- 子组件 -->
<template>
<div class="rating-select">
<div class="rating-type border-1px">
<span class="block positive" :class="{'active': intSelectType === $store.state.RATING_CODE.ALL}" @click="select($store.state.RATING_CODE.ALL)">{{desc.all}}<span class="count">{{ratings.length}}</span></span>
<span class="block positive" :class="{'active': intSelectType === $store.state.RATING_CODE.POSITIVE}" @click="select($store.state.RATING_CODE.POSITIVE)">{{desc.positive}}<span class="count">{{positives.length}}</span></span>
<span class="block negative" :class="{'active': intSelectType === $store.state.RATING_CODE.NEGATIVE}" @click="select($store.state.RATING_CODE.NEGATIVE)">{{desc.negative}}<span class="count">{{negatives.length}}</span></span>
</div>
<div class="switch border-1px">
<i class="iconfont" :class="{'on': bOnlyContent}" @click="toggleContent"></i>
<span class="text">只看有内容的评价</span>
</div>
</div>
</template>
export default {
name: 'ratingSelect',
props: {
ratings: {
type: Array,
default () {
return [];
}
},
selectType: {
type: Number,
default () {
return this.$store.state.RATING_CODE.ALL;
}
},
onlyContent: {
type: Boolean,
default: false
},
desc: {
type: Object,
default () {
return {
all: '全部',
positive: '满意',
negative: '不满意'
};
}
}
},
data () {
return {
// 用data来控制传递过来的数据,可以防止数据改变时第一次响应失效
intSelectType: this.selectType,
bOnlyContent: this.onlyContent
};
},
watch: {
selectType (value) {
this.intSelectType = value;
},
onlyContent (value) {
this.bOnlyContent = value;
}
},
computed: {
positives () {
return this.ratings.filter((rating) => {
return rating.rateType === this.$store.state.RATING_CODE.POSITIVE;
});
},
negatives () {
return this.ratings.filter((rating) => {
return rating.rateType === this.$store.state.RATING_CODE.NEGATIVE;
});
}
},
methods: {
select (type) {
this.intSelectType = type;
this.$emit('update:select-type', type);
},
toggleContent () {
this.bOnlyContent = !this.onlyContent;
this.$emit('update:only-content', this.bOnlyContent);
}
}
};
Object
当父组件传入的数据是个对象的时候,子组件对该对象的修改会直接影响到父组件。
父组件:
<!-- 父组件调用子组件 -->
<cart-control :food="food" @cart-add="drop"></cart-control>
export default {
name: 'father',
components: {CartControl},
data () {
return {
food: {}
};
}
};
子组件:
<!-- 子组件 -->
<template>
<div class="cart-control">
<transition name="move">
<div class="cart-decrease" v-show="food.count" @click.stop.prevent="minusCart"><i class="iconfont"></i></div>
</transition>
<div class="cart-count" v-show="food.count">{{food.count}}</div>
<div class="cart-increase" @click.stop.prevent="addCart"><i class="iconfont"></i></div>
</div>
</template>
export default {
name:'cartControl',
props: {
food: {
type: Object,
default () {
return {};
}
}
},
methods: {
addCart (event) {
if (!this.food.count) {
Vue.set(this.food, 'count', 1);
} else {
this.food.count++;
}
this.$emit('cart-add', event.target);
},
minusCart () {
this.food.count--;
}
}
};