defineModel()
為 Vue 3.4 的新 API,針對 Two-way Binding 的 v-model
有更簡單的寫法。
Version
Vue 3.4
defineProps()
+
寫在 component 內部- Component 外的 external state 與 component 內的 internal state 連動
App.vue
<template>
<div>External state: {{ count }}</div>
<MyCounter v-model="count"></MyCounter>
</template>
<script setup>
import { ref } from 'vue'
import MyCounter from '@/components/MyCounter.vue'
let count = ref(0)
</script>
Line 2
<div>External state: {{ count }}</div>
- 顯示 component 外部的 state
Line 3
<MyCounter v-model="count"></MyCounter>
<MyCounter>
:將 counter 抽成MyComponent
componentv-model
:傳入雙向的 property,count
state 將傳入MyCount
component,且MyCount
也會將內部 state 傳出至count
state 傳出
Line 8
import MyCounter from '@/components/MyCounter.vue'
import
:引用MyCounter
component
Line 10
let count = ref(0)
count
state:定義count
state 的初始值
MyCounter.vue
<template>
<div class="box">
<button @click="onClick">inner +</button>
<span>Internal state: {{ count }}</span>
</div>
</template>
<script setup>
import { computed } from 'vue'
let props = defineProps({ modelValue: Number })
let emit = defineEmits(['update:modelValue'])
let count = computed({
get() {
return props.modelValue
},
set(newValue) {
emit('update:modelValue', newValue)
}
})
let onClick = () => count.value++
</script>
<style scoped>
.box {
border-style: solid;
border-width: 2px;
border-color: red;
width: fit-content;
}
</style>
- 建立
MyCounter
component
Line 2
<div class="box">
<button @click="onClick">inner +</button>
<span>Internal state: {{ count }}</span>
</div>
- 將 HTML 部分搬進
MyCounter
component box
:CSS 只是為了顯示紅框方便識別
Line 11
let props = defineProps({ modelValue: Number })
defineProps()
:定義 component 的 prop,因為v-model
為特殊 prop,其 prop 名稱固定為modelValue
,並宣告其型別為Number
defineProps()
: 回傳為props
Object,將以props
讀取 prop
defineProps()
為 compiler macro,不需 import,Vue compiler 會自動展開
Line 12
let emit = defineEmits(['update:modelValue'])
defineEmits()
:定義 component 的 event,傳入為 Array,因為v-model
為特殊的 prop,其 event 固定為update:modelValue
defineEmits()
回傳為 function,將以emit()
發出 event
defineEmits()
也是 compiler macro
Line 14
let count = computed({
get() {
return props.modelValue
},
set(newValue) {
emit('update:modelValue', newValue)
}
})
- 建立可讀可寫的
count
computed,如此才能對count
computed 遞增 - Getter:讀取
count
computed 時,直接讀取modelValue
prop - Setter:對
count
computed 修改時,直接將寫入值
newValue
發出update:modelValue
event 傳出
Line 23
let onClick = () => count.value++
- 因為
count
computed 可讀可寫,因此可當成一般 state讀取
與寫入
defineModel()
App.vue
<template>
<div>External state: {{ count }}</div>
<MyCounter v-model="count"></MyCounter>
</template>
<script setup>
import { ref } from 'vue'
import MyCounter from '@/components/MyCounter.vue'
let count = ref(0)
</script>
App.vue
部分完全不變
MyCounter.vue
<template>
<div class="box">
<button @click="onClick">inner +</button>
<span>Internal state: {{ count }}</span>
</div>
</template>
<script setup>
let count = defineModel()
let onClick = () => count.value++
</script>
<style scoped>
.box {
border-style: solid;
border-width: 2px;
border-color: red;
width: fit-content;
}
</style>
Line 9
let count = defineModel()
- Vue 3.4 提供了新的
defineModel()
,Vue compiler 會自動幫我們展開defineProps()
、defineEmits()
與computed()
,使用上只需將原本的ref()
改成defineModel()
即可,非常方便
defineModel()
也是 compiler macro
Conclusion
- Vue 不允許我們直接修改 prop,只能包成可讀可寫的 computed 後才能修改
- 當
computed()
傳入 function 時只能唯讀
,當傳入有 getter 與 setter 的 Object 才是可讀可寫的 computed - 跟 component 有關的
define
系列都是 compiler macro,只能用在 Script Setup 內,且目前只有 Vue 能提供 compiler macro,我們無法自行定義 compiler macro - 將原本代碼抽成使用
v-model
的 component 變得非常簡單,只需將ref()
改成defineModel()
即可