前端 | 自定義元件 v-model:Vue 如何實現雙向繫結

Skuld_yi發表於2022-01-19

v-model 是 Vue 中一個常用的指令,常用於表單中的資料繫結。如下基本用法想必大家都很熟悉,data 中的 checked 屬性的值就會隨著多選框的狀態實時變化。

<el-checkbox v-model="checked" />

但你或許聽說過,Vue 元件之間是“單向資料流”,即通過 props 從父元件向子元件單向傳遞資料。那麼,v-model 的“雙向繫結”效果是如何實現的呢?

自定義元件實現 v-model

首先來看一個實際應用的例子,需求如下:在建立試卷頁面 paperCreate 中,通過選擇題目元件 questionSelect 查詢並選擇題目組成試卷,效果如下圖所示。

選擇試題

為了方便使用,希望能在選擇題目元件上使用 v-model 指令進行雙向繫結:

<!-- 選擇題目元件 -->
<question-select v-model="formData.questions" />

<!-- 顯示已選擇題目 -->
<div v-for="item in formData.questions" :key="item.id" >
    <question :question="item" />
</div>

value 屬性和 input 事件

自定義支援 v-model 的元件只需要滿足兩個條件:

  • value 屬性作為傳入資料
  • 資料改變時,將新資料作為 payload,向上 emit 一個 input 事件

就是這麼簡單,相關程式碼為:

export default {
  props: {
    value: Array,	// 型別根據實際需要
  },
  methods: {
    handelChange() {
      this.$emit("input", newValue);
    },
  },
};

如果不想使用預設的 value 屬性和 input 事件,也可以通過 model 物件自定義相應屬性和事件:

model: {
  prop: 'checked',
  event: 'change',
},

單向資料流

需要注意的是,prop 傳值是單向的,即父元件中的資料改變會反映到子元件中,但在子元件內部不能主動改變 prop 的值。具體來說就是,子元件中不能使用類似於 v-model="value" 之類試圖改變 value 值的操作。

因此表格中的 checkbox,也只單向傳入 value 來控制是否選中的狀態,然後在選中狀態改變時手動處理改變後的結果。

  • 對於表格中的一行(即一道題目),如果傳入的 value 陣列(即已選中的題目列表)中包含它的 id,則顯示為選中狀態
  • 不能直接改變原陣列 value,直接構造新列表傳出:
    • 如果新增了一個選項,傳原列表 + 所選物件
    • 如果取消了一個選項,傳原列表 - 所選物件

(PS:元件庫中提供的 el-checkbox 實際上也是另一個自定義元件,可以同理分析)

<el-table :data="questionList">
  <el-table-column>
    <template slot-scope="scope">
      <el-checkbox
        :value="selectedIds.includes(scope.row.id)"
        @change="(value) => handelChange(scope.row, value)"
      />
    </template>
  </el-table-column>
  <!-- 其他屬性 -->
</el-table>
props: {
  value: Array,
},
computed: {
  selectedIds() {
    return this.value.map((item) => item.id);
  },
},
methods: {
  handelChange(item, checked) {
    if (checked) {
      this.$emit("input", [...this.value, item]);
    } else {
      this.$emit(
        "input",
        this.value.filter((element) => element.id != item.id)
      );
    }
  },
},

具體寫法與元件庫的具體實現有關,簡單說明一下此處的 element 語法:

  • scope.row 為 el-table 表格當前行對應的物件
  • el-checkbox 的 change 事件的負載為核取方塊點選後的新值

然後就結束了,父元件使用選擇題目元件時就能正常使用 v-model 了。本例中繫結的資料是完整的題目列表,原因是需要在頁面中顯示已選擇題目的具體資訊;如果只需要 id 資料(例如 select 那樣的元件),則 emit 時只傳 id 列表即可,寫法完全一致。

雙向繫結

通過上面的例子想必你已經看出來了,v-model 指令的“雙向繫結”實際上是一個語法糖:它將父元件的資料通過 prop 傳入子元件,再監聽子元件的輸入事件來更新父元件中的資料,從而實現雙向繫結的效果。

v-model

結語&參考資料

以上是個人對 Vue 中 v-model 指令的一些理解與思考,希望能給你提供幫助。如果有問題或疏漏之處,歡迎在評論中討論與指正。

我將繼續在個人部落格中更新自己的學習筆記,以前端技術(Vue框架)為主,感興趣的話歡迎關注!

參考資料:Vue 官方文件

相關文章