EP24 - 組件 v-model

閱讀時間約 1 分鐘
Component v-model 之前有看過一些內容!
但還是不太清楚,希望這篇看完可以多了解一些
也太多v-xxx 要記住了吧 www~~

基本用法

v-model 可以用於組件實現雙向綁定。

從 Vue 3.4 開始,推薦的方法是使用 defineModel() 巨集:

<!-- Child.vue -->
<script setup>
const model = defineModel()

function update() {
model.value++
}
</script>

<template>
<div>Parent bound v-model is: {{ model }}</div>
<button @click="update">Increment</button>
</template>

父組件可以使用 v-model 綁定一個值:

<!-- Parent.vue -->
<template>
<Child v-model="countModel" />
</template>

defineModel() 返回一個 ref。它可以像其他 ref 一樣訪問和修改,但它作為父組件值和本地值之間的雙向綁定:

  • .value 與父組件 v-model 綁定的值同步;
  • 當子組件修改它時,父組件綁定的值也會更新。

這意味著你也可以將這個 ref 綁定到原生輸入元素上,使用 v-model,這使得包裝原生輸入元素變得簡單:

<script setup>
const model = defineModel()
</script>

<template>
<input v-model="model" />
</template>

Try it in the playground

底層原理 - Under the Hood

defineModel 是一個方便的巨集。編譯器將其展開為:

  • 一個名為 modelValueprop,與本地 ref 的值同步;
  • 一個名為 update:modelValue 的事件,當本地 ref 的值被修改時觸發。

這是在 3.4 之前實現相同子組件的方法:

<!-- Child.vue -->
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>

<template>
<input
:value="props.modelValue"
@input="emit('update:modelValue', $event.target.value)"
/>
</template>

然後,父組件中的 v-model="foo" 會被編譯為:

<!-- Parent.vue -->
<template>
<Child
:modelValue="foo"
@update:modelValue="$event => (foo = $event)"
/>
</template>

可以看到,這種方式更加冗長。然而,了解其底層運作方式是有幫助的。

因為 defineModel 聲明了一個 prop,所以可以通過傳遞選項來聲明底層 prop 的選項:

// 使 v-model 成為必填項
const model = defineModel({ required: true })

// 提供默認值
const model = defineModel({ default: 0 })

警告

如果 defineModel prop 有默認值且父組件沒有提供任何值,這可能導致父子組件之間的不同步。在下面的例子中,父組件的 myRef 是未定義的,但子組件的 model 是 1:

// 子組件:
const model = defineModel({ default: 1 })

// 父組件:
const myRef = ref()
<Child v-model="myRef"></Child>

v-model 的引數 - v-model arguments

在組件上使用 v-model 也可以接受一個引數:

<!-- 父組件 -->
<MyComponent v-model:title="bookTitle" />

在子組件中,我們可以通過將字串作為 defineModel() 的第一個引數來支援相應的引數:

<!-- MyComponent.vue -->
<script setup>
const title = defineModel('title')
</script>

<template>
<input type="text" v-model="title" />
</template>

Try it in the playground

如果需要 prop 選項,應在模型名稱之後傳遞它們:

const title = defineModel('title', { required: true })

3.4 之前的用法

<!-- MyComponent.vue -->
<script setup>
defineProps({
title: {
required: true
}
})
defineEmits(['update:title'])
</script>

<template>
<input
type="text"
:value="title"
@input="$emit('update:title', $event.target.value)"
/>
</template>

Try it in the playground

多個 v-model 綁定 - Multiple v-model bindings​

透過使用 v-model 參數的能力,我們可以在單個組件實例上創建多個 v-model 綁定。

每個 v-model 會同步到不同的 prop,而不需要在組件中添加額外的選項:

<template>
<UserName
v-model:first-name="first"
v-model:last-name="last"
/>
</template>

<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>

<template>
<input type="text" v-model="firstName" />
<input type="text" v-model="lastName" />
</template>

Try it in the playground

3.4 之前的用法

<script setup>
defineProps({
firstName: String,
lastName: String
})

defineEmits(['update:firstName', 'update:lastName'])
</script>

<template>
<input
type="text"
:value="firstName"
@input="$emit('update:firstName', $event.target.value)"
/>
<input
type="text"
:value="lastName"
@input="$emit('update:lastName', $event.target.value)"
/>
</template>

Try it in the playground

處理 v-model 修飾符 - Handling v-model modifiers

在學習表單輸入綁定時,我們知道 v-model 具有內建修飾符,例如 .trim.number.lazy。有時,我們可能希望自定義輸入組件的 v-model 支援自定義修飾符。

我們來創建一個名為 capitalize 的自定義修飾符,它會將 v-model 綁定的字符串的第一個字母大寫:

<template>
<MyComponent v-model.capitalize="myText" />
</template>

在子組件中,我們可以通過解構 defineModel() 的返回值來訪問添加到組件 v-model 的修飾符:

<script setup>
const [model, modifiers] = defineModel()

console.log(modifiers) // { capitalize: true }
</script>

<template>
<input type="text" v-model="model" />
</template>

為了根據修飾符有條件地調整如何讀取/寫入值,我們可以將 getset 選項傳遞給 defineModel()。這兩個選項在獲取/設置模型 ref 值時接收該值,並應返回轉換後的值。以下是使用 set 選項實現 capitalize 修飾符的方法:

<script setup>
const [model, modifiers] = defineModel({
set(value) {
if (modifiers.capitalize) {
return value.charAt(0).toUpperCase() + value.slice(1)
}
return value
}
})
</script>

<template>
<input type="text" v-model="model" />
</template>

Try it in the playground

3.4 之前的用法

<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) }
})

const emit = defineEmits(['update:modelValue'])

function emitValue(e) {
let value = e.target.value
if (props.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
emit('update:modelValue', value)
}
</script>

<template>
<input type="text" :value="props.modelValue" @input="emitValue" />
</template>

Try it in the playground

帶有參數的 v-model 修飾符

這是使用具有不同參數的多個 v-model 修飾符的另一個示例:

<template>
<UserName
v-model:first-name.capitalize="first"
v-model:last-name.uppercase="last"
/>
</template>

<script setup>
const [firstName, firstNameModifiers] = defineModel('firstName')
const [lastName, lastNameModifiers] = defineModel('lastName')

console.log(firstNameModifiers) // { capitalize: true }
console.log(lastNameModifiers) // { uppercase: true }
</script>

3.4 之前的用法

<script setup>
const props = defineProps({
firstName: String,
lastName: String,
firstNameModifiers: { default: () => ({}) },
lastNameModifiers: { default: () => ({}) }
})

defineEmits(['update:firstName', 'update:lastName'])

console.log(props.firstNameModifiers) // { capitalize: true }
console.log(props.lastNameModifiers) // { uppercase: true }
</script>
v-model還是需要多練習,
看了3.4之前的用法,覺得3.4之後寫法很不錯啊!
頭暈暈地,到底差在哪?

3.4之前之後差異

1. 定義 v-model 的方式

在 3.4 之前,使用 v-model 需要手動定義 props 和 emits:

<!-- MyComponent.vue -->
<script setup>
defineProps({
modelValue: String
})
defineEmits(['update:modelValue'])
</script>

<template>
<input
type="text"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>

從 3.4 之後,使用 defineModel() 來自動化這個過程,簡化了代碼:

<!-- MyComponent.vue -->
<script setup>
const model = defineModel()
</script>

<template>
<input v-model="model" />
</template>

2. 支援多個 v-model 綁定

在 3.4 之前,無法直接在同一組件上使用多個 v-model。需要手動管理不同的 props 和 emits:

<script setup>
defineProps({
firstName: String,
lastName: String
})
defineEmits(['update:firstName', 'update:lastName'])
</script>

<template>
<input
type="text"
:value="firstName"
@input="$emit('update:firstName', $event.target.value)"
/>
<input
type="text"
:value="lastName"
@input="$emit('update:lastName', $event.target.value)"
/>
</template>

在 3.4 之後,可以使用 v-model 參數來支持多個綁定,讓語法更加清晰和簡潔:

<UserName
v-model:first-name="first"
v-model:last-name="last"
/>

3. 支援自定義修飾符

在3.4之前,無法直接在 v-model 上使用自定義修飾符,需手動處理:

const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) }
})

在3.4之後,可以直接在 v-model 上使用自定義修飾符,如 .capitalize,並且可以透過 defineModel() 獲取修飾符:

<MyComponent v-model.capitalize="myText" />
<script setup>
const [model, modifiers] = defineModel()

總結

  • 簡化代碼:3.4 之後的寫法更加簡潔,省去了手動定義 props 和 emits 的繁瑣。
  • 支持多個 v-model:允許在同一組件上使用多個 v-model,提供更大的靈活性。
  • 自定義修飾符:提供更方便的方式來處理輸入值的轉換。

這些變化使得 Vue 的組件更容易開發和維護。



avatar-img
2會員
71內容數
分享生活趣事~
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
卡關的人生 的其他內容
子組件可以使用 $emit 方法來發送自定義事件,父組件則使用 v-on 來監聽這些事件。事件參數可以通過 $emit 傳遞,並在父組件中使用內聯箭頭函數或方法接收。使用 defineEmits 可以顯式宣告事件並進行驗證,提升代碼可讀性和可維護性。
props的值可以是字符串數組或物件語法,每個屬性的鍵是prop的名字,值是預期類型的建構函數。當解構props時,Vue會自動添加props.前綴以保持響應性。Prop驗證確保了數據類型和要求的一致性,使開發更嚴謹。
接下來要深入探討組件註冊的內容!Vue 組件需要註冊,讓 Vue 知道在模板中遇到組件時在哪裡找到實現。註冊方式有全局和局部兩種。全局註冊適用於通用、頻繁使用的組件,例如按鈕、模態框等;而局部註冊適用於特定頁面或功能,能明確依賴關係,提升維護性。回顧之前的範例,我們都是使用局部註冊呢!
Ex6SimpleComponent.vue 使用 ref 來創建響應式變數 groceryList,並引入 TodoItem 子組件。模板部分使用 <ol> 標籤來創建有序列表,並透過 v-for 指令遍歷 groceryList 陣列,每個項目對應一個 TodoItem 組件。
在這個範例中,我們展示了如何使用 Vue 的 v-model 來實現各種表單元件的雙向綁定。範例包括文字輸入框、單選框、多選框、單選按鈕、下拉選單和多選下拉選單。使用 ref 創建響應式變數,並在 template 中使用 v-model 進行雙向綁定。這使得表單元件能夠即時反映和更新數據。
這個範例包含六個基礎範例,接下來的實用範例將會更有趣。第四個範例關於條件與迴圈,實作起來也不難。在介紹這個範例之前,注意到如果組件的 <style> 標籤中缺少 scoped 屬性,樣式會全局生效,影響到其他組件的按鈕樣式。因此,請確保每個組件的樣式都加上 scoped。
子組件可以使用 $emit 方法來發送自定義事件,父組件則使用 v-on 來監聽這些事件。事件參數可以通過 $emit 傳遞,並在父組件中使用內聯箭頭函數或方法接收。使用 defineEmits 可以顯式宣告事件並進行驗證,提升代碼可讀性和可維護性。
props的值可以是字符串數組或物件語法,每個屬性的鍵是prop的名字,值是預期類型的建構函數。當解構props時,Vue會自動添加props.前綴以保持響應性。Prop驗證確保了數據類型和要求的一致性,使開發更嚴謹。
接下來要深入探討組件註冊的內容!Vue 組件需要註冊,讓 Vue 知道在模板中遇到組件時在哪裡找到實現。註冊方式有全局和局部兩種。全局註冊適用於通用、頻繁使用的組件,例如按鈕、模態框等;而局部註冊適用於特定頁面或功能,能明確依賴關係,提升維護性。回顧之前的範例,我們都是使用局部註冊呢!
Ex6SimpleComponent.vue 使用 ref 來創建響應式變數 groceryList,並引入 TodoItem 子組件。模板部分使用 <ol> 標籤來創建有序列表,並透過 v-for 指令遍歷 groceryList 陣列,每個項目對應一個 TodoItem 組件。
在這個範例中,我們展示了如何使用 Vue 的 v-model 來實現各種表單元件的雙向綁定。範例包括文字輸入框、單選框、多選框、單選按鈕、下拉選單和多選下拉選單。使用 ref 創建響應式變數,並在 template 中使用 v-model 進行雙向綁定。這使得表單元件能夠即時反映和更新數據。
這個範例包含六個基礎範例,接下來的實用範例將會更有趣。第四個範例關於條件與迴圈,實作起來也不難。在介紹這個範例之前,注意到如果組件的 <style> 標籤中缺少 scoped 屬性,樣式會全局生效,影響到其他組件的按鈕樣式。因此,請確保每個組件的樣式都加上 scoped。
你可能也想看
Google News 追蹤
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
MVVMC(Model View ViewModel Coordinator),特點是Coordinator。 Model 負責儲存應用程式的資料。 View 負責顯示資料。 ViewModel 負責處理View和Model之間的狀態關係。 Coordinator 負
MVI(Model View Intent),特點是Intent。 Model 負責介面狀態 View 負責顯示資料。 Intent 負責將封裝後的操作告知Model。
MVVM(Model View ViewModel),特點是View跟ViewModel之間做資料綁定。 Model 負責儲存應用程式的資料。 View 負責顯示資料。 ViewModel 負責處理View和Model之間的狀態關係。
MVP(Model View Presenter)由MVC演變而來。MVC與MVP的差異是View跟Model之間的關係;MVC中是可以直接溝通的;MVP中是不可以直接溝通的,必須要透過 Presenter。 Model 負責資料存取。 View 負責顯示資料,並將使用者的操作傳給P
Thumbnail
因為最近想嘗試編碼風格,於是就選了一套比較"不嚴格"的輔助工具來摸索。 編輯器 VS CODE 框架 VUE3 打包工具 VITE 編碼風格 Standard 環境 version { "nodejs":"v18.18.0", "npm":"9.8.1" }
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找
Thumbnail
我們在實作中,難免會遇到在不同組件中,卻有需求相同的資料格式,因此 mixins 可以達到我們的需求,除了 data 以外也包含了 methods 可以共用,舉例來說,學生資料可能會在,班級跟社團內被使用,當我們要撰寫元件時,就可以省略多餘的 data 定義。
本節課程將介紹 MVVM 架構的概念和優點。MVVM 是 Model-View-ViewModel 的簡稱,是一種分離資料、介面和邏輯的設計模式。透過 MVVM 架構,您可以提高程式碼的可讀性、可測試性和可維護性。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
MVVMC(Model View ViewModel Coordinator),特點是Coordinator。 Model 負責儲存應用程式的資料。 View 負責顯示資料。 ViewModel 負責處理View和Model之間的狀態關係。 Coordinator 負
MVI(Model View Intent),特點是Intent。 Model 負責介面狀態 View 負責顯示資料。 Intent 負責將封裝後的操作告知Model。
MVVM(Model View ViewModel),特點是View跟ViewModel之間做資料綁定。 Model 負責儲存應用程式的資料。 View 負責顯示資料。 ViewModel 負責處理View和Model之間的狀態關係。
MVP(Model View Presenter)由MVC演變而來。MVC與MVP的差異是View跟Model之間的關係;MVC中是可以直接溝通的;MVP中是不可以直接溝通的,必須要透過 Presenter。 Model 負責資料存取。 View 負責顯示資料,並將使用者的操作傳給P
Thumbnail
因為最近想嘗試編碼風格,於是就選了一套比較"不嚴格"的輔助工具來摸索。 編輯器 VS CODE 框架 VUE3 打包工具 VITE 編碼風格 Standard 環境 version { "nodejs":"v18.18.0", "npm":"9.8.1" }
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找
Thumbnail
我們在實作中,難免會遇到在不同組件中,卻有需求相同的資料格式,因此 mixins 可以達到我們的需求,除了 data 以外也包含了 methods 可以共用,舉例來說,學生資料可能會在,班級跟社團內被使用,當我們要撰寫元件時,就可以省略多餘的 data 定義。
本節課程將介紹 MVVM 架構的概念和優點。MVVM 是 Model-View-ViewModel 的簡稱,是一種分離資料、介面和邏輯的設計模式。透過 MVVM 架構,您可以提高程式碼的可讀性、可測試性和可維護性。