這一頁假設你已經閱讀了組件基礎。如果你對組件還不熟悉,請先閱讀那部分內容。 Props屬性是 Vue 組件中一個非常重要的概念,用來接收從父組件傳遞過來的數據。使用 props
可以讓組件更加靈活和可重用。慢慢對他有點熟悉了吧!
Vue 組件需要明確的 prop 定義,這樣 Vue 才能知道應該將傳遞給組件的哪些外部 props 視為透傳屬性 fallthrough attributes(這部分內容將在專門的部分討論)。
在使用 <script setup>
的單文件組件(SFC)中,可以使用 defineProps()
巨集來定義 props:
<script setup>
const props = defineProps(['foo'])
console.log(props.foo)
</script>
在非 <script setup>
的組件中,props 是使用 props
選項來定義的:
export default {
props: ['foo'],
setup(props) {
// setup() 接收的第一個參數就是 props。
console.log(props.foo)
}
}
注意傳遞給 defineProps()
的參數與提供給 props
選項的值相同:這兩種定義方式共享相同的 props 選項 API。
除了使用字符串數組定義 props 外,我們還可以使用物件語法:
// 在 <script setup> 中
defineProps({
title: String,
likes: Number
})
// 在非 <script setup> 中
export default {
props: {
title: String,
likes: Number
}
}
在物件定義語法中,每個屬性的鍵是 prop 的名字,而值應該是預期類型的建構函數。
這不僅能記錄你的組件,還能在其他開發者在瀏覽器控制台中傳遞錯誤類型時給出警告。關於 prop 驗證的更多細節,我們將在本頁面後面討論。
如果你在 <script setup>
中使用 TypeScript,也可以使用純類型註釋來定義 props:
<script setup lang="ts">
defineProps<{
title? : string
likes?: number
}>()
</script>
更多細節: Typing Component Props
Vue 的響應系統根據屬性存取來追蹤狀態使用。例如,當你在計算屬性或監聽器中存取 props.foo
時,foo
prop 會被追蹤為一個依賴。
例如,給定以下程式碼:
const { foo } = defineProps(['foo'])
watchEffect(() => {
// 在 3.5 之前只執行一次
// 在 3.5 之後當 "foo" prop 變更時重新執行
console.log(foo)
})
在 3.4 及更低版本中,foo
是一個實際的常量,永遠不會變更。在 3.5 及更高版本中,Vue 的編譯器會在同一 <script setup>
區塊中存取從 defineProps
解構的變數時,自動添加 props.
前綴。因此,以上程式碼相當於以下程式碼:
const props = defineProps(['foo'])
watchEffect(() => {
// `foo` 被編譯器轉換為 `props.foo`
console.log(props.foo)
})
此外,你可以使用 JavaScript 的原生預設值語法為 props 宣告預設值。這在使用基於類型的 props 定義時特別有用:
const { foo = 'hello' } = defineProps<{ foo?: string }>()
如果你希望在 IDE 中更明顯地區分解構的 props 和普通變數,Vue 的 VSCode 擴充提供了一個設定,可以啟用解構 props 的內嵌提示。
當我們將解構的 prop 傳入函數時,例如:
const { foo } = defineProps(['foo'])
watch(foo, /* ... */)
這將無法如預期運作,因為它相當於 watch(props.foo, ...)
,我們傳遞的是一個值而不是一個響應式資料源給 watch
。事實上,Vue 的編譯器會捕捉這種情況並拋出警告。
類似於我們可以使用 watch(() => props.foo, ...)
來監聽普通的 prop,我們也可以通過將解構的 prop 包裝在 getter 中來監聽:
watch(() => foo, /* ... */)
此外,當我們需要將解構的 prop 傳入外部函數並保留響應性時,這是推薦的方法:
useComposable(() => foo)
外部函數可以在需要追蹤提供的 prop 變化時調用 getter(或使用 toValue
進行標準化),例如在計算屬性或監聽器的 getter 中。
我們使用 camelCase 來宣告較長的 prop 名稱,因為這樣可以避免在使用它們作為屬性鍵時需要加上引號,並且允許我們在模板表達式中直接引用它們,因為它們是有效的 JavaScript 標識符:
defineProps({
greetingMessage: String
})
<span>{{ greetingMessage }}</span>
技術上來說,當傳遞 props 給子組件時(除非在 DOM 模板中),你也可以使用 camelCase。然而,慣例是在所有情況下都使用 kebab-case,以對齊 HTML 屬性:
<MyComponent greeting-message="hello" />
我們盡可能使用 PascalCase 來命名組件標籤,因為這樣可以通過區分 Vue 組件和原生元素來提高模板的可讀性。然而,在傳遞 props 時使用 camelCase 並沒有太大的實際好處,所以我們選擇遵循每種語言的慣例。
到目前為止,你已經看到了作為靜態值傳遞的 props,例如:
<BlogPost title="My journey with Vue" />
你也看到了使用 v-bind
或其 :
簡寫來動態賦值的 props,例如:
<!-- 動態賦值變量的值 -->
<BlogPost :title="post.title" />
<!-- 動態賦值複雜表達式的值 -->
<BlogPost :title="post.title + ' by ' + post.author.name" />
在上面的兩個例子中,我們碰巧傳遞的是字符串值,但可以傳遞給 prop 的值類型不限於此。
數字
<!-- 即使 `42` 是靜態的,我們也需要 v-bind 來告訴 Vue 這是 JavaScript 表達式,而不是字符串。 -->
<BlogPost :likes="42" />
<!-- 動態賦值變量的值。 -->
<BlogPost :likes="post.likes" />
<!-- 包含沒有值的 prop 將默認為 `true`。 -->
<BlogPost is-published />
<!-- 即使 `false` 是靜態的,我們也需要 v-bind 來告訴 Vue 這是 JavaScript 表達式,而不是字符串。 -->
<BlogPost :is-published="false" />
<!-- 動態賦值變量的值。 -->
<BlogPost :is-published="post.isPublished" />
<!-- 即使陣列是靜態的,我們也需要 v-bind 來告訴 Vue 這是 JavaScript 表達式,而不是字符串。 -->
<BlogPost :comment-ids="[234, 266, 273]" />
<!-- 動態賦值變量的值。 -->
<BlogPost :comment-ids="post.commentIds" />
<!-- 即使物件是靜態的,我們也需要 v-bind 來告訴 Vue 這是 JavaScript 表達式,而不是字符串。 -->
<BlogPost
:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
/>
<!-- 動態賦值變量的值。 -->
<BlogPost :author="post.author" />
如果你想將物件的所有屬性作為 props 傳遞,你可以使用不帶參數的 v-bind
(即 v-bind
而不是 :prop-name
)。例如,給定一個 post
物件:
const post = {
id: 1,
title: 'My Journey with Vue'
}
以下模板:
<BlogPost v-bind="post" />
將等同於:
<BlogPost :id="post.id" :title="post.title" />
所有 props 形成從父屬性到子屬性的單向綁定:當父屬性更新時,它會向下傳遞到子屬性,但不會反過來。這樣可以防止子組件意外修改父屬性狀態,從而使應用程序的數據流更容易理解。
此外,每當父組件更新時,子組件中的所有 props 都將刷新為最新的值。這意味著你不應該嘗試在子組件中修改 prop。如果你這樣做,Vue 會在控制台中發出警告:
const props = defineProps(['foo'])
// ❌ 警告,props 是只讀的!
props.foo = 'bar'
通常有兩種情況會讓你想要修改 prop:
const props = defineProps(['initialCounter'])
// counter 只使用 props.initialCounter 作為初始值;
// 它與未來的 prop 更新斷開連接。
const counter = ref(props.initialCounter)
const props = defineProps(['size'])
// 當 prop 變更時自動更新的計算屬性
const normalizedSize = computed(() => props.size.trim().toLowerCase())
當物件和陣列作為 props 傳遞時,雖然子組件不能修改 prop 綁定,但它可以修改物件或陣列的嵌套屬性。這是因為在 JavaScript 中,物件和陣列是通過引用傳遞的,而 Vue 阻止這種修改代價過高。
這種修改的主要缺點是,它允許子組件以父組件不明顯的方式影響父狀態,可能使未來的數據流變得更難理解。作為最佳實踐,你應該避免這種修改,除非父子組件是設計上緊密耦合的。在大多數情況下,子組件應該發出一個事件,讓父組件進行修改。
組件可以為它們的 props 指定需求,例如你已經看到的類型。如果不符合需求,Vue 會在瀏覽器的 JavaScript 控制台中發出警告。這在開發要被其他人使用的組件時尤其有用。
要指定 prop 驗證,你可以向 defineProps()
巨集提供一個帶有驗證需求的物件,而不是一個字符串數組。例如:
defineProps({
// 基本類型檢查
// (`null` 和 `undefined` 值將允許任何類型)
propA: Number,
// 多種可能的類型
propB: [String, Number],
// 必需的字符串
propC: {
type: String,
required: true
},
// 必需但可為空的字符串
propD: {
type: [String, null],
required: true
},
// 有默認值的數字
propE: {
type: Number,
default: 100
},
// 有默認值的物件
propF: {
type: Object,
// 物件或數組的默認值必須從工廠函數返回。該函數接收組件接收到的原始 props 作為參數。
default(rawProps) {
return { message: 'hello' }
}
},
// 自定義驗證函數
// 在 3.4+ 中,完整的 props 作為第二個參數傳遞
propG: {
validator(value, props) {
// 值必須匹配這些字符串之一
return ['success', 'warning', 'danger'].includes(value)
}
},
// 有默認值的函數
propH: {
type: Function,
// 不像物件或數組默認值,這不是工廠函數——這是一個用作默認值的函數
default() {
return 'Default function'
}
}
})
提示
defineProps()
參數內的代碼無法訪問 <script setup>
中聲明的其他變數,因為整個表達式在編譯時被移動到外部函數作用域。
額外詳情:
required: true
。undefined
值。false
。你可以通過為其設置默認值來更改這一點——即:default: undefined
使其表現為非 Boolean prop。undefined
時將使用它——這包括 prop 缺失或傳遞了明確的 undefined
值。defineProps<{ msg: string }>
將編譯為 { msg: { type: String, required: true }}
。類型可以是以下本地構造函數之一:
此外,類型還可以是一個自定義類或構造函數,並且將使用 instanceof
檢查進行斷言。例如,給定以下類:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
}
你可以將其用作 prop 的類型:
defineProps({
author: Person
})
Vue 將使用 instanceof Person
來驗證 author
prop 的值是否確實是 Person
類的實例。
如果類型是必需但可為空的,你可以使用包含 null
的數組語法:
defineProps({
id: {
type: [String, null],
required: true
}
})
請注意,如果類型只是 null
而不使用數組語法,它將允許任何類型。
具有布林(Boolean)類型的 props 具有特殊的轉換規則,以模擬本地布林屬性的行為。假設有一個具有以下聲明的 <MyComponent>
:
defineProps({
disabled: Boolean
})
組件可以這樣使用:
<!-- 等同於傳遞 :disabled="true" -->
<MyComponent disabled />
<!-- 等同於傳遞 :disabled="false" -->
<MyComponent />
當 prop 被聲明為允許多種類型時,也會應用布林值的轉換規則。但是,當同時允許字符串(String)和布林值時,有一個邊界情況——只有當布林值出現在字符串之前時才會應用布林值的轉換規則:
// disabled 會被轉換為 true
defineProps({
disabled: [Boolean, Number]
})
// disabled 會被轉換為 true
defineProps({
disabled: [Boolean, String]
})
// disabled 會被轉換為 true
defineProps({
disabled: [Number, Boolean]
})
// disabled 會被解析為空字符串 (disabled="")
defineProps({
disabled: [String, Boolean]
})
老實說!這篇文章我是邊看邊睡覺~哈
理解力太差 www ~~ 小睡一會!再回來看看應該會比較懂?
懂原理,但是細節有點多的時候~真的很想睡www~