父组件使用 prop 传递数据给子组件。子组件通过自定义事件与父组件通信。
自定义事件从子组件向父组件传递数据的步骤可大致归纳为:
1. 子组件模板中的原生事件(如click)绑定事件函数
2. 回调函数实现子组件内部数据的更新,同时触发自定义事件:$this.emit(//customEvent)
3. 父组件模板中v-on监听自定义事件
$emit+v-on偏向于事件函数的监听和触发,主要指向methods中的函数。
自定义事件的另一方面主要是针对prop自定义特性的双向绑定,偏向于纯数据的更新。采用以下方式:
父组件:<comp :foo="bar" @update:foo="val => bar = val"></comp>
子组件:this.$emit('update:foo', newValue) //当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件
请看以下代码:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>update改变父组件数据</title>
<style type="text/css"></style>
<script src = "https://unpkg.com/vue"></script>
</head>
<body>
<div id="dr">
<!-- 注意! @update:my-message不能是驼峰和kebab-case模式-->
<h2>父组件{{name}}</h2>
<child :message="name" @update:message="val => name = val"></child>
</div>
<script>
//Prop双向绑定
Vue.component("child", {
props: ["message"],
template: '<div>\
<p>{{message}}</p>\
<button @click="refresh">emit</button>\
</div>',
methods: {
refresh: function () {
this.$emit('update:message','brightRanger');
}
}
});
var dr = new Vue({
el: "#dr",
data: {
name: "DarkRanger",
}
});
</script>
</body>
</html>
而对于非表单,通常在子组件使用<button>按钮触发子组件的事件,子组件函数中$emit触发父组件的自定义事件;对于表单,v-model是一个原生事件,默认实现表单控件元素上的双向数据绑定,无法改写加入自定义的语句,如果要加入$emit触发父组件的自定义事件,就要对v-model变通,把语法糖还原:
(1)v-bind:value="something"
(2)v-on:input="something = $event.target.value">
在第(2)条input监听事件中加入$emit,触发父组件的数据更新
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>v-model变通实现父、子组件表单的双向绑定</title>
<style type="text/css"></style>
<script src = "https://unpkg.com/vue"></script>
</head>
<body>
<h3>update改变父组件状态</h3>
<div id="dr05">
<div>
<div>HTML表单</div>
<div>{{name}}</div>
<input type='text' v-model="name" />
</div>
<br />
<div>
<div>component组件的表单(update):</div>
<!--:message="name"是父更新子,@update:message是子更新父-->
<dr05 :message="name" @update:message="val => name = val"></dr05>
</div>
</div>
<script>
//Prop类型绑定
Vue.component("dr05", {
props: ["message"],
// 当子组件需要更新子组件的值时,它需要显式地触发一个更新事件,父组件执行此事件更数据。对于非表单,通常在子组件使用<button>按钮触发子组件的事件,子组件函数中$emit触发父组件的自定义事件;对于表单,v-model是一个原生事件,默认实现表单控件元素上的双向数据绑定,无法改写加入自定义的语句,如果要加入$emit触发父组件的自定义事件,就要对v-model变通,把语法糖还原:
// (1)v-bind:value="something"
// (2)v-on:input="something = $event.target.value">
// 在第(2)条input监听事件中加入$emit,触发父组件的数据更新
template: '<div>\
<div>{{message}}</div>\
<input type="text" v-bind:value="message" v-on:input="refresh($event.target.value)" />\
</div>', // 注意事件函数中的形参,传入的参数是&event.target.value,
methods: {
refresh: function (inputVal) {
this.$emit('update:message',inputVal);
}
}
});
var dr05 = new Vue({
el: "#dr05",
data: {
name: "DarkRanger",
}
});
</script>
</body>
</html>
最后再强调程序调试中发现的两点:
1. @update:foo="val => bar = val"语句中,prop名foo既不能用驼峰命名,也不能用kebab-case模式命名;
2. 表单prop双向传递中,$emit触发的事件函数,传入的参数是$event.target.value而不是value