用了幾個月Vue一直很糾結自定義組件的v-model實現,最近開始學習React時,React中受控組件與狀態提升的理念與v-model不謀而合。 轉載請註明地址: https://www.cnblogs.com/sonoda-umi/p/9750188.html 在Vue與React中其實都存在單 ...
用了幾個月Vue一直很糾結自定義組件的v-model實現,最近開始學習React時,React中受控組件與狀態提升的理念與v-model不謀而合。
轉載請註明地址: https://www.cnblogs.com/sonoda-umi/p/9750188.html
在Vue與React中其實都存在單向數據流的概念,只不過Vue中通過各種語法糖被弱化了,比如React與Vue中的props都是單向傳輸數據的。在React中如果想實現類似於v-model的功能,需要這樣實現:
父組件:
class Calculator extends React.Component { constructor(props) { super(props); this.handleCelsiusChange = this.handleCelsiusChange.bind(this); this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this); this.state = {temperature: '', scale: 'c'}; } handleCelsiusChange(temperature) { this.setState({scale: 'c', temperature}); } handleFahrenheitChange(temperature) { this.setState({scale: 'f', temperature}); } render() { const scale = this.state.scale; const temperature = this.state.temperature; const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature; const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature; return ( <div> <TemperatureInput scale="c" temperature={celsius} onTemperatureChange={this.handleCelsiusChange} /> <TemperatureInput scale="f" temperature={fahrenheit} onTemperatureChange={this.handleFahrenheitChange} /> <BoilingVerdict celsius={parseFloat(celsius)} /> </div> ); } }
子組件:
class TemperatureInput extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); } handleChange(e) { this.props.onTemperatureChange(e.target.value); } render() { const temperature = this.props.temperature; const scale = this.props.scale; return ( <fieldset> <legend>Enter temperature in {scaleNames[scale]}:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } }
這是一個輸入水溫從而監控水是否沸騰的一個小組件。子組件是一個溫度輸入的input控制項,父組件是由兩個溫度輸入(華氏與攝氏)與一個現實水是否沸騰的指示器組成。父組件存在一個state作為唯一數據源用於存放溫度等值於狀態,溫度通過子組件的prop傳入子組件內部通過input的value屬性顯示在基礎輸入框中,當在基礎輸入框中觸發輸入時間時,onChange事件觸發由prop傳入的onTempreture事件並附帶變化後的值,再由父組件的handleCelsiusChange/handleFahrenheitChange事件處理方法將基礎輸入框傳來的值寫入state中,再由state通過prop將溫度傳入子組件完成一次數據的更新。這其中其實已經完成了對Vue中基礎組件v-model的理解與自定義組件v-model的理解。
轉載請註明地址: https://www.cnblogs.com/sonoda-umi/p/9750188.html
在Vue官方文檔中,對原生組件v-model的解釋是這樣的:
<input v-bind:value="searchText" v-on:input="searchText = $event.target.value" >
v-model其實是上面寫法的語法糖。其實就是將this.searchText的值通過名為value的prop傳入input組件內,而後當input事件觸發時將事件帶來的input的新值寫入this.searchText中,然後根據this.searchText中值的變化通過value的prop傳入input控制項完成input控制項上值的變化,如果去掉v-on...後,這個控制項將變為一個只讀控制項。
對於自定義組件,文檔中有這樣的解釋:
一個組件上的 v-model
預設會利用名為 value
的 prop 和名為 input
的事件,但是像單選框、覆選框等類型的輸入控制項可能會將 value
特性用於不同的目的。model
選項可以用來避免這樣的衝突:
Vue.component('base-checkbox', { model: { prop: 'checked', event: 'change' }, props: { checked: Boolean }, template: ` <input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change', $event.target.checked)" > ` })
現在在這個組件上使用 v-model 的時候:
<base-checkbox v-model="lovingVue"></base-checkbox>
這裡的 lovingVue 的值將會傳入這個名為 checked 的 prop。同時當 <base-checkbox> 觸發一個 change 事件並附帶一個新的值的時候,這個 lovingVue 的屬性將會被更新。
其實就是將原來v-model預設使用的名為value的prop與名為input的event自定義一個名字使用,在上面自定義組件中存在
props: {
checked: Boolean
}
說明checked本質上還是一個prop,然後在子組件的model屬性中將自定義的prop與event註冊,而觸發model中event時也就是通過觸發子組件的事件在父組件中修改綁定自定義prop的變數的值的過程,這樣這個過程就很明顯了:
1.父組件創建一個名為tmp變數綁定名為checked的prop的值(已被修飾為v-model)並根據父組件中tmp值的變化將變化後的值傳入子組件中,引起子組件checkbox狀態變化;
2.子組件中checkbox被勾選,觸發checkbox的change事件,通過this.$emit方法觸發子組件的change事件並將change事件產生的新值傳入;
3.因為在model屬性中已將v-model語法糖中event註冊為change(換成其他名字也都可以),v-model會自動將子組件傳來的值傳入tmp變數中;
4.Vue監聽到tmp值的變化,執行第一步,更新子組件中checkbox的狀態;
其實上面的子組件可以換個寫法更容易理解:
Vue.component('base-checkbox', { model: { prop: 'checked', event: 'test' }, props: { checked: Boolean }, template: ` <input type="checkbox" v-bind:checked="checked" v-on:change="$emit('test', $event.target.checked)" > ` })
父組件中調用時可以這樣寫:
<base-checkbox :checked="something" @test="something='子組件中的$event.target.value'"></base-checkbox>
這樣對v-model的理解也就一目瞭然了。
轉載請註明地址: https://www.cnblogs.com/sonoda-umi/p/9750188.html