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

cumi發表於2018-10-07

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

相關文章