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
留言分享你的想法!
avatar-img
卡關的人生
2會員
73內容數
分享生活趣事~
卡關的人生的其他內容
2024/11/10
Vue 提供了多種動畫技術來提升應用程式的互動性,包括基於 CSS 類別的動畫、基於狀態的動畫,以及使用監視器來動畫化數值。基於類別的動畫可通過動態添加 CSS 類別來觸發,像是觸發按鈕搖動效果。基於狀態的動畫則是透過樣式綁定,根據互動動態調整元素的外觀,例如根據滑鼠位置改變背景顏色。
Thumbnail
2024/11/10
Vue 提供了多種動畫技術來提升應用程式的互動性,包括基於 CSS 類別的動畫、基於狀態的動畫,以及使用監視器來動畫化數值。基於類別的動畫可通過動態添加 CSS 類別來觸發,像是觸發按鈕搖動效果。基於狀態的動畫則是透過樣式綁定,根據互動動態調整元素的外觀,例如根據滑鼠位置改變背景顏色。
Thumbnail
2024/11/09
Web Components 是一組網頁原生 API,允許開發者創建可重複使用的自訂元素。Vue 與 Web Components 是互補的技術,Vue 支援整合和創建自訂元素。
Thumbnail
2024/11/09
Web Components 是一組網頁原生 API,允許開發者創建可重複使用的自訂元素。Vue 與 Web Components 是互補的技術,Vue 支援整合和創建自訂元素。
Thumbnail
2024/11/08
Vue 建議使用模板構建應用程式,但在需要 JavaScript 的全程式化功能時,渲染函數可派上用場。渲染函數通過 h() 函數創建 vnode,h 是 hyperscript 的簡寫,能生成 HTML 的 JavaScript。
Thumbnail
2024/11/08
Vue 建議使用模板構建應用程式,但在需要 JavaScript 的全程式化功能時,渲染函數可派上用場。渲染函數通過 h() 函數創建 vnode,h 是 hyperscript 的簡寫,能生成 HTML 的 JavaScript。
Thumbnail
看更多
你可能也想看
Thumbnail
孩子寫功課時瞇眼?小心近視!這款喜光全光譜TIONE⁺光健康智慧檯燈,獲眼科院長推薦,網路好評不斷!全光譜LED、180cm大照明範圍、5段亮度及色溫調整、350度萬向旋轉,讓孩子學習更舒適、保護眼睛!
Thumbnail
孩子寫功課時瞇眼?小心近視!這款喜光全光譜TIONE⁺光健康智慧檯燈,獲眼科院長推薦,網路好評不斷!全光譜LED、180cm大照明範圍、5段亮度及色溫調整、350度萬向旋轉,讓孩子學習更舒適、保護眼睛!
Thumbnail
創作者營運專員/經理(Operations Specialist/Manager)將負責對平台成長及收入至關重要的 Partnership 夥伴創作者開發及營運。你將發揮對知識與內容變現、影響力變現的精準判斷力,找到你心中的潛力新星或有聲量的中大型創作者加入 vocus。
Thumbnail
創作者營運專員/經理(Operations Specialist/Manager)將負責對平台成長及收入至關重要的 Partnership 夥伴創作者開發及營運。你將發揮對知識與內容變現、影響力變現的精準判斷力,找到你心中的潛力新星或有聲量的中大型創作者加入 vocus。
Thumbnail
先前提到 Quasar 的 Dialog Plugin 很好用,再讓我補充一個用法。
Thumbnail
先前提到 Quasar 的 Dialog Plugin 很好用,再讓我補充一個用法。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
因為最近想嘗試編碼風格,於是就選了一套比較"不嚴格"的輔助工具來摸索。 編輯器 VS CODE 框架 VUE3 打包工具 VITE 編碼風格 Standard 環境 version { "nodejs":"v18.18.0", "npm":"9.8.1" }
Thumbnail
因為最近想嘗試編碼風格,於是就選了一套比較"不嚴格"的輔助工具來摸索。 編輯器 VS CODE 框架 VUE3 打包工具 VITE 編碼風格 Standard 環境 version { "nodejs":"v18.18.0", "npm":"9.8.1" }
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找
Thumbnail
我們在實作中,難免會遇到在不同組件中,卻有需求相同的資料格式,因此 mixins 可以達到我們的需求,除了 data 以外也包含了 methods 可以共用,舉例來說,學生資料可能會在,班級跟社團內被使用,當我們要撰寫元件時,就可以省略多餘的 data 定義。
Thumbnail
我們在實作中,難免會遇到在不同組件中,卻有需求相同的資料格式,因此 mixins 可以達到我們的需求,除了 data 以外也包含了 methods 可以共用,舉例來說,學生資料可能會在,班級跟社團內被使用,當我們要撰寫元件時,就可以省略多餘的 data 定義。
Thumbnail
自訂元件生成位置顧名思義就是可以指定部分HTML區塊渲染在特定的畫面上,即使在不同組件也能把A組件內的部分畫面,展現在B組件上,以下方程式舉例。
Thumbnail
自訂元件生成位置顧名思義就是可以指定部分HTML區塊渲染在特定的畫面上,即使在不同組件也能把A組件內的部分畫面,展現在B組件上,以下方程式舉例。
Thumbnail
樣板模式的定義極為簡單,卻是大型系統程式、WEB/APP應用框架的設計核心,完美展現設計模式的價值: 簡單、高效、強大。
Thumbnail
樣板模式的定義極為簡單,卻是大型系統程式、WEB/APP應用框架的設計核心,完美展現設計模式的價值: 簡單、高效、強大。
Thumbnail
2023 Vue直播班筆記 - 動態路由Props,接續之前的一般動態路由。分為 "寫死" 及 "彈性" 兩種。
Thumbnail
2023 Vue直播班筆記 - 動態路由Props,接續之前的一般動態路由。分為 "寫死" 及 "彈性" 兩種。
Thumbnail
這系列是我在 2023 六角學院 Vue作品實戰班的筆記,筆記以本人理解的方式記錄。此篇主題為 Slot Props 進階應用 ,其中包含單筆資料、多筆資料。
Thumbnail
這系列是我在 2023 六角學院 Vue作品實戰班的筆記,筆記以本人理解的方式記錄。此篇主題為 Slot Props 進階應用 ,其中包含單筆資料、多筆資料。
Thumbnail
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
Thumbnail
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News