雙向綁定把數據變動的操做隱藏在組件內部,調用者並不會直接感知,業務層無需關心內部實現邏輯,簡化大量與業務無關的代碼。html
1. 父組件只傳輸 prop
,不定義事件接收。 vue
2. 由子組件更新傳下來的值。git
1. v-model
便可以做用在普通表單元素上,又能夠做用在組件上。github
2. vuejs隱式添加 value
的 prop
,子組件經過 props.value
接收值。bash
3. 子組件經過 this.$emit('input')
,改變父組件 v-model
綁定的值。
ui
4. v-model
能夠實現雙向綁定,無需定義接收事件。this
v-model
實際上是一個語法糖,如下是通過 vue 轉換後的模板:
spa
<input v-model="message" />
// 轉換後:
<input
v-bind:value="message"
v-on:input="message=$event.target.value">複製代碼
1. 添加 v-bind:value
。雙向綁定
2. 添加 v-on:input
的自定義事件。code
動態綁定了 input
的 value
指向了 messgae
變量,而且在觸發 input
事件的時候去動態把 message
設置爲目標值,這樣實際上就完成了數據雙向綁定。
雙向綁定其實就是普通單向綁定和事件組合來完成的。
父級組件不用過多解釋,使用 v-model
傳參。
<template>
<div>
<!-- $attrs & observer -->
<BaseInputAttrs v-model="pModel"/> <br>
<!-- watch & data & emit('input') -->
<BaseInputWatch v-model="pModel"/> <br>
<!-- computed & emit('input') -->
<BaseInputComputed v-model="pModel"/>
</div>
</template>
<script>
export default {
data () {
return {
pModel: 'v-model雙向綁定',
}
},
}
</script>複製代碼
下面是三種<當前組件>的 v-model 實現方案
方案1:
<template>
<input type="text" v-bind="$attrs" v-on="$listeners">
</template>
複製代碼
$attr: 向 子組件
傳遞,當前組件
沒有接收的,父組件
傳遞下來的 prop 。
$listeners: 向父組件
傳遞,當前組件
沒有接收的,子組件
拋出的自定義事件。
這樣實現後,發現輸入後會變成一個 [object InputEvent]
的事件對象。
是由於 v-on="input"
是內置事件,vue 判斷 當前組件
的 tag
是 'input'
類的表單元素,纔會關聯 value
。
當前組件
通過封裝後,tag
已經改變,父組件實際處理的是 this.$emit('input', el)
的傳參,el
就是 [object InputEvent]
表單對象。
解決方案:攔截內置的 input
事件,改變引用,向 父組件
傳遞最終值,以下。
<template>
<input type="text" v-bind="$attrs" @input="observerInput">
</template>
<script>
export default {
methods: {
// 攔截內置事件
observerInput (el) {
this.$emit('input', el.target.value)
},
},
}
</script>複製代碼
方案2:watch監聽 和 過渡屬性
分別監聽 父組件
和子組件
,經過過渡屬性 model
存儲值
<template>
<input type="text" v-model="model">
</template>
<script>
export default {
props: {
value: {
type: String,
default: '',
},
},
data () {
return {
model: '',
}
},
watch: {
value: {
immediate: true,
handler (newVal) {
this.model = newVal
},
},
model (newVal) {
this.$emit('input', newVal)
},
},
}
</script>複製代碼
方案3:計算屬性 setter getter
尤雨溪的方案 setter
去攔截修改
<template>
<input type="text" v-model="model">
</template>
<script>
export default {
props: {
value: {
type: String,
default: '',
},
},
computed: {
model: {
get () {
return this.value
},
set (newVal) {
this.$emit('input', newVal)
},
},
},
}
</script>複製代碼
總結:三種方案都是經過 this.$emit('input')
修改最終的值,這是封裝組件的關鍵所在:統一數據源。
以後幾篇文章會介紹其餘的雙向綁定方案!