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

用了幾個月Vue一直很糾結自定義組件的v-model實現,最近開始學習React時,React中受控組件與狀態提高的理念與v-model不謀而合。html

 轉載請註明地址: http://www.javashuo.com/article/p-drdofdfi-dm.html學習

在Vue與React中其實都存在單向數據流的概念,只不過Vue中經過各類語法糖被弱化了,好比React與Vue中的props都是單向傳輸數據的。在React中若是想實現相似於v-model的功能,須要這樣實現:this

父組件:spa

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>
    );
  }
}

子組件:code

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的理解。component

轉載請註明地址: http://www.javashuo.com/article/p-drdofdfi-dm.htmlhtm

在Vue官方文檔中,對原生組件v-model的解釋是這樣的:blog

<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的理解也就一目瞭然了。

轉載請註明地址: http://www.javashuo.com/article/p-drdofdfi-dm.html

相關文章
相關標籤/搜索