自定義組件v-model的實質性理解


用了幾個月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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM