Vue 实现父子组件双向绑定最正确的做法
最近在学习 vue
,遇到了父子组件数据绑定的问题。Vue
官方文档有这样一段话:
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
但这并不是说父页面就不能跟子组件双向通讯,不过就是麻烦了一点。在 Google
了诸多之后,尝试了网络上一些破碎零星的代码(什么计算属性,侦听器等)之后,终于找到了最佳的做法:prop.sync
。
完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Prop 双向绑定的实现</title>
<script src="dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>父组件数据</h1>
<table>
<tr>
<th>姓名</th>
<th>年龄</th>
</tr>
<tr>
<td>{{ name }} <input type="text" v-model="name" /></td>
<td>{{ age }} <input type="number" v-model.number="age" /></td>
</tr>
</table>
<!--
v-bind:my-name
v-on:update:my-name="my-name = $event"
参考文档
https://cn.vuejs.org/v2/guide/components-custom-events.html
-->
<user-table
v-bind:my-name.sync="name"
v-bind:my-age="age"
v-on:change-age="age = $event"
></user-table>
</div>
<template id="userTable">
<div>
<h2>子组件数据</h2>
<table>
<tr>
<th>姓名</th>
<th>年龄</th>
</tr>
<tr>
<td>{{ myName }} <input type="text" :value="myName" @input="updateName" /></td>
<td>{{ myAge }} <input type="number" :value="myAge" @input="updateAge" /></td>
</tr>
</table>
</div>
</template>
<style>
table, td, th {
border-collapse: collapse;
border-spacing: 0
}
table {
margin: 20px;
}
td, th {
border: 1px solid #bcbcbc;
padding: 5px 10px
}
th {
background: #42b983;
font-weight: 400;
color: #fff;
cursor: pointer
}
</style>
<script>
var UserTable = {
props: {
myName: String,
myAge: Number
},
template: '#userTable',
watch: {
myName: function (val) {
console.log('child-component watch fater-component name:' + val)
},
myAge: function (val) {
console.log('child-component watch father-component age:' + val)
}
},
methods: {
updateName (evt) {
console.log(evt)
console.log('_name value:' + this.myName)
console.log('evt.target.value:' + evt.target.value)
this.$emit('update:myName', evt.target.value)
console.log('child-component myName:' + this.myName)
},
updateAge (evt) {
console.log(evt)
console.log('_name value:' + this.myAge)
console.log('evt.target.value:' + evt.target.value)
// 自定义 change-age 事件
this.$emit('change-age', Number(evt.target.value))
console.log('child-component myAge:' + this.myAge)
}
}
}
new Vue({
el: '#app',
data: {
name: '张三',
age: 20
},
components: {
'user-table': UserTable
},
mounted() {
const vm = this
setInterval(() => { console.log('name', this.name, 'age', this.age); }, 3000);
}
})
</script>
</body>
</html>
使用 Event-Emit
方式实现的示例(请随意在输入框中输入数据,观察数据文本变化):