v-model 摸爬滾打原理詳解

v-model 是 Vue 框架提供的衆多指令中的一個,其主要做用是能夠實如今表單 <input><textarea><select> 標籤元素上建立雙向數據綁定。javascript

那雙向數據綁定又是什麼呢?在講解 v-model 指令以前,咱們就先來講說雙向數據綁定吧!html

雙向數據綁定

廢話很少說,上來就直接亮一段 v-model 示例的代碼吧!vue

// 定義 v-model 示例組件
Vue.component('bindData', {
  template:` <div> <p>this is bindData component!</p> <button @click="handleChange">change input value</button> <input type="text" v-model="inputValue" /> <p>{{inputValue}}</p> </div> `,
  data() {
    return {
      inputValue: 'hello'
    }
  },
  methods:{
    // 點擊按鈕改變 input 的值
    handleChange() {
      this.inputValue = `I'm changed`;
    }
  }
});

const app=new Vue({
  el: '#app',
  template: ` <div> <bindData /> </div> `
});
複製代碼

我相信上面的一段簡單的示例代碼對於參與過基於 Vue 技術棧項目開發的同窗來講都是常規操做了,若是你尚未參與過基於 Vue 技術棧項目的開發或者對 Vue 不怎麼熟悉,我介意你先去查看官網的文檔,嘗試着作一兩個項目,而後回過頭來看我們的這篇文章,你確定會對 v-model 有新的認識!java

上面這段示例代碼經過在 input 標籤上綁定 v-model 實現一個很神奇的功能:react

  • 首先在 input 標籤上經過 v-model 指令綁定 data_(若是你還不知道data 這個 Vue 示例的內置屬性,你能夠點擊_這裏_) _的 inputValue 屬性;api

  • 由於 $data 的 inputValue 屬性有一個 hello 的初始值,因此當組件 mounted 後,input 顯示的值就是 hello,同時 input 標籤下的 p 標籤裏面顯示的也是 helloapp

  • 當組件顯示後,向 input 中輸入值,發現 input 標籤下的 p 標籤裏面顯示的是_ _input 標籤當前展現的值框架


* 最後點擊 input 標籤上面的 button 標籤,發現 input 標籤的值和 p 標籤裏面顯示的內容同時變化了,並且_顯示內容一致,都顯示成了 I'm changed_。

v-model

上面詳細的列出了幾點操做和展現的內容,回頭看看這就是雙向數據綁定呀!此時此刻,若是你還不是很瞭解 v-model 的實現原理,能夠先靜下心來根據本身如今已經知道的知識來好好想一想或者假設一下 v-model 內部是怎樣操做的(不要怕想錯 💪)。ide

幾分鐘過去了。。。[斜眼笑]函數

接觸 v-model

如今咱們一塊兒來分析一下吧:

  • 一、從 inputValue 屬性入手吧,由於 input 標籤、v-model、button 標籤、p 標籤的惟一聯繫就是這個 inputValue 屬性了,不信你就看看示例代碼吧!

  • 二、由於 input 屬性是定義在 $data 上的,那就是響應式數據_(若是你還不知道響應式數據是啥,你能夠點擊_這裏,根據響應式數據的特性:**當數據值被修改時,會驅動視圖的變化,顯示變化後的數據,**那麼對應的綁定了 inputValue 屬性的 p 標籤顯示的內容就會變化

  • 三、當咱們往 input 輸入值時,p 標籤顯示的內容也相應的變化了,根據響應式數據的特性,這裏確定是觸發了 inputValue 值的變化,而且將 input 此時的值賦值給了 inputValue,才致使了 p 標籤內容的變化;

  • 四、當點擊 button 時,input 和 p 標籤顯示的值都變化了且顯示的值同樣,而在 button 綁定的 click 處理函數中,咱們直接將新值賦值給了 inputValue。p 標籤是顯示的展現了 inputValue 屬性的值,因此理所固然的變化了,那 input 呢?

  • 五、據咱們所知,input 上面是有一個叫 value 的屬性,咱們能夠根據 value 屬性給 input 設置顯示值。那點擊 button 後,咱們給 inputValue 設置了新的值,input 的顯示值也變化了,那確定是有個給 input 的 value 屬性設置值的操做;

  • 六、在第 3 點和第 5 點分別暴露出了在 input 上有兩個隱式的操做:給 inputValue 設置和給 input 的 value 屬性設置。可是在 input 標籤上沒有顯示的綁定操做,只有一個 v-model 指令。

❓這裏咱們就假設這兩個隱式的操做就是 v-model 封裝的。

懵逼改造 v-model

根據上面 6 點的分析或者根據咱們現有的知識,咱們稍微改寫一下代碼:

// 定義 v-model 示例組件改寫
Vue.component('bindData1', {
  template:` <div> <p>this is bindData1 component!</p> <button @click="handleChange">change input value</button> <input type="text" :value="inputValue" @change="handleInputChange" /> <p>input 中的值爲:{{inputValue}}</p> </div> `,
  data() {
    return {
      inputValue: 'hello'
    }
  },
  methods:{
    // 處理 input 輸入 change 事件
    handleInputChange(e) {
      this.inputValue = e.target.value;
    },
    
    // 點擊按鈕改變 input 的值
    handleChange() {
      this.inputValue = `I'm changed`;
    }
  }
});

const app=new Vue({
  el: '#app',
  template: ` <div> <bindData1 /> </div> `
});
複製代碼

改造後的代碼能夠實現和改造以前同樣的雙向數據綁定的效果,可是改造以前的示例示例代碼,改造以後的代碼主要修改了兩個地方:

  • 一、將 input 標籤上的 v-model 指令去掉了,換成了用 v-bind:value(縮寫 :value) 指令來綁定 inputValue 屬性,而且加上了一個 v-on:change(縮寫 ) 事件;

  • 二、添加了一個 input change 處理函數,函數邏輯是將當前 input 標籤的值賦值給 $data 的 inputValue 屬性

深刻 v-model

看到這裏,你是否是在犯嘀咕:這兩中不一樣的代碼均可以實現一樣的雙向數據綁定的效果,確定不是巧合,這兩種處理方式確定存在某種聯繫。

不錯,上面兩個示例基本是等效的實現。改造後的示例是改造前的複雜實現方式,也就是說 v-model 只是一種封裝或者語法糖,負責監聽用戶的輸入事件以更新數據,並對一些極端場景進行特殊處理。

v-model 會忽略全部表單元素的 value、checked、selected 特性的初始值而老是將 Vue 實例的數據做爲數據來源。你應該經過 JavaScript 在組件的 data 選項中聲明初始值。

v-model 在不一樣的 HTML 標籤上使用會監控不一樣的屬性和拋出不一樣的事件:

  • text 和 textarea 元素使用 value 屬性和 input 事件;

  • checkbox 和 radio 使用 checked 屬性和 change 事件;

  • select 字段將 value 做爲 prop 並將 change 做爲事件。

看到這裏是否是恍然大悟了!下面是一個 select 的示例,關於更多的示例能夠上 Vue 官網查看

Feb-26-2019 12-57-55.gif

自定義組件 v-model

前文的內容只是講解了 v-model 在原生表單標籤上面的使用,在平時的開發中使用第三方 UI 庫提供的組件時使用 v-model 的頻率也很好,那自定義組件上面的 v-model 又是怎麼實現的呢?

在自定義的組件上 v-model 默認會利用名爲 value 的 prop 和名爲 input 的事件實現,可是對於不一樣的表單元素 value 屬性會用於不一樣的目的(正如咱們上面提到的),好比單選框、複選框表現爲 checked。爲了區別這些不一樣的表現特性 Vue 給組件提供了 model 配置屬性。model 是一個對象:提供 prop 屬性指定組件 value 特性,event 指定值變化時觸發的事件。

說了這麼多,來一段示例代碼吧!

// 自定義組件 checkbox
Vue.component('checkbox', {
  template: ` <input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change', $event.target.checked)" > `,
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean
  },
});

// 使用自定義組件 
Vue.component('useCheckbox', {
  template: ` <checkbox v-model="checkStatus" @change="handleChange"></checkbox> `,
  data() {
  	return {
    	checkStatus: false
    }
  },
  methods: {
  	handleChange(checked) {
    	// do something
    }
  }
});

複製代碼

示例代碼中的 checkStatus 的值將會傳入這個名爲 checked 的 prop。同時當 checkbox 觸發一個 change 事件並附帶一個新的值的時候,這個 checkStatus 的屬性將會被更新。

⚠️注意:注意你仍然須要在組件的 props 選項裏聲明 checked 這個 prop

v-model 修飾符

在原生的表單元素和自定義的組件中使用 v-model 咱們都講解完了,同時咱們也講到了 v-model 的實現原理,如今你們應該對 v-model 很瞭解了吧!

接下來,咱們就說說那些使用在 v-model 上的修飾符吧。

.lazy

在上面的內容中,咱們說起到 v-model 實現了雙向數據綁定,雙向數據綁定的特性是:當 input 標籤顯示的值實時變化時,也會實時的觸發 input 標籤上的 input 事件,在每次 input 事件觸發後將輸入框的值與數據實時進行同步。 在一些特殊的需求和場景下,你可能但願數據同步不是實時同步而是在觸發 change 事件的時候進行數據同步,那麼你能夠添加 lazy 修飾符進行處理,使用的示例以下:

<!-- 在「change」時而非「input」時更新 -->
<input v-model.lazy="msg" >
複製代碼

.number

在表單中處理輸入驗證必須是數字時,好比跟錢💰相關的操做,你這是可能就但願能夠將用戶輸入的值自動轉換成數字類型,那你能夠在 v-model 上加上 number 修飾符進行處理。

<!-- 將輸入的值自動轉換成數字類型 -->
<input v-model.number="age" type="number">
複製代碼

⚠️ number 修飾符的處理原則是:使用 parseFloat() 函數對輸入的值進行處理,若是輸入的值是 parseFloat() 函數不能解析的,如以非數字開頭的字符串,就會返回原始值。

.trim

對於用戶在表單標籤中輸入的字符串,最後咱們都想去除首尾的空白字符,那麼這個 trim 修飾符是很是有用的。

<!-- 去除輸入值的首尾空白字符 -->
<input v-model.trim="msg">
複製代碼

總結

v-model 是一個很是實用的指令,它的使用能夠省去咱們不少的業務邏輯代碼,使代碼更加清晰、更好維護。固然 v-model 的實現原理仍是很是容易理解和消化的,再者尤大爲了讓咱們的開發更加方便,在 v-model 提供了很實用的修飾符簡化咱們的操做,修飾符的使用也很簡單。

v-model 的實現原理咱們算是搞清楚了,那下節咱們就說一下 Vue 源碼究竟是怎麼實現 v-model 的,下次再見吧👋!

相關文章
相關標籤/搜索