帶你深入瞭解vue2中的 v-model,看看如何讓元件支援該語法

2022-01-04 22:00:07
本篇文章帶大家瞭解一下vue2中的 v-model,看看v-model 是雙向繫結還是單向資料流,如何讓你開發的元件支援 v-model,希望對大家有所幫助。

閱讀本文

你將:

  • 弄明白: v-model 是什麼的語法糖? vue2 對原生元件究竟做了什麼特殊處理?
  • 弄明白: v-model 到底是單向資料流還是資料雙向繫結
  • 弄明白: v-model 在語法糖之外的『副作用』?
  • 學會如何讓你的元件也支援 v-model 語法。

一、v-model 的本質是語法糖。

v-model 本質上不過是語法糖。它負責監聽使用者的輸入事件以更新資料,並對一些極端場景進行一些特殊處理。』 -- 官方檔案。【相關推薦:】

什麼是語法糖?

語法糖,簡單來說就是『便捷寫法』。

在大部分情況下, v-model="foo" 等價於 :value="foo" 加上 @input="foo = $event"

<!-- 在大部分情況下,以下兩種寫法是等價的 -->
<el-input v-model="foo" />

<el-input :value="foo" @input="foo = $event" />

沒錯,在大部分情況下如此。

但也有例外:

  • vue2 給元件提供了 model 屬性,可以讓使用者自定義傳值的prop名更新值的事件名。這個暫且略過,第四節會細說。

  • 對於原生 html 原生元素,vue 幹了大量『髒活兒』,目的是為了能讓我們忽視 html 在api上的差異性。以下元素的左右兩種寫法是等價的:

  • textarea 元素:

1.png

  • select 下拉框:

2.png

  • input type='radio' 單選框:

3.png

  • input type='checkbox' 多選框:

4.png

在程式設計思想上,這種幫助使用者『隱藏細節』的方式叫封裝

二、v-model 僅僅是語法糖嗎?(冷知識)

v-model 不僅僅是語法糖,它還有副作用。

副作用如下:如果 v-model 繫結的是響應式物件上某個不存在的屬性,那麼 vue 會悄悄地增加這個屬性,並讓它響應式。

舉個例子,看下面的程式碼:

// template中:
<el-input v-model="user.tel"></el-input>
// script中:
export default {
  data() {
    return {
      user: {
        name: '公眾號: 前端要摸魚',
      }
    }
  }
}

響應式資料中沒有定義 user.tel 屬性,但是 template 裡卻用 v-model 繫結了 user.tel,猜一猜當你輸入時會發生什麼?

看效果:

5.gif

揭曉答案吧:user 上會新增 tel 屬性,並且 tel 這個屬性還是響應式的。

這就是『副作用』帶來的效果,你學會了嗎?

三、 v-model 是雙向繫結還是單向資料流?

2.1 v-model 是雙向繫結嗎?

是,官方說是。

『你可以用 v-model 指令在表單 <input><textarea><select> 元素上建立雙向資料繫結。』 —— vue2官方檔案

2.2 那 v-model 是單向資料流嗎?

是的,它甚至是單向資料流的典型正規化。

雖然官方沒有明確表示這點,但我們可以捋一捋兩者的關係。

  • 什麼是單項資料流?

子元件不能改變父元件傳遞給它的 prop 屬性,推薦的做法是它丟擲事件,通知父元件自行改變繫結的值。

  • v-model 的做法是怎樣的?

v-model 做法完全符合單項資料流。甚至於,它給出了一種在命名和事件定義上的規範。

眾所周知 .sync 修飾符是單向資料流的另一個典型正規化。

6.png

『單向資料流』總結起來其實也就8個字:『資料向下,事件向上』。

四、如何讓你開發的元件支援 v-model

雖然不想說,但這確實是高頻面試題。

在定義 vue 元件時,你可以提供一個 model 屬性,用來定義該元件以何種方式支援 v-model

model 屬性本身是有預設值的,如下:

// 預設的 model 屬性
export default {
  model: {
    prop: 'value',
    event: 'input'
  }
}

也就是說,如果你不定義 model 屬性,或者你按照當面方法定義屬性,當其他人使用你的自定義元件時,v-model="foo" 就完全等價於 :value="foo" 加上 @input="foo = $event"

如果把 model 屬性進行一些改裝,如下:

// 預設的 model 屬性
export default {
  model: {
    prop: 'ame',
    event: 'zard'
  }
}

那麼,v-model="foo" 就等價於 :ame="foo" 加上 @zard="foo = $event"

沒錯,就是這麼容易,讓我們看個例子。

先定義一個自定義元件:

<template>
<div>
  我們是TI{{ ame }}冠軍
  <el-button @click="playDota2(1)">加</el-button>
  <el-button @click="playDota2(-1)">減</el-button>
</div>
</template>
<script>
export default {
  props: {
    ame: {
      type: Number,
      default: 8
    }
  },
  model: { // 自定義v-model的格式
    prop: 'ame', // 代表 v-model 繫結的prop名
    event: 'zard' // 程式碼 v-model 通知父元件更新屬性的事件名
  },
  methods: {
    playDota2(step) {
      const newYear = this.ame + step
      this.$emit('zard', newYear)
    }
  }
}
</script>

然後我們在父元件中使用該元件:

// template中
<dota v-model="ti"></dota>
// script中
export default {
  data() {
    return {
      ti: 8
    }
  }
}

看看效果:

7.gif

讓你的元件支援 v-model 就這麼容易。

五、demo和原始碼

獲取原始碼請存取github

https://github.com/zhangshichun/blog-vue2-demos/tree/master/src/views/about-v-model

更多程式設計相關知識,請存取:!!

以上就是帶你深入瞭解vue2中的 v-model,看看如何讓元件支援該語法的詳細內容,更多請關注TW511.COM其它相關文章!