此章(zhāng)節假設你已經看(kàn)過了(le)組件基礎。若你還不了(le)解組件是什麽,請(qǐng)先閱讀該章(zhāng)節。
Props 聲明(míng)
一(yī)個組件需要(yào)顯式聲明(míng)它所接受的 props,這(zhè)樣 Vue 才能(néng)知道(dào)外(wài)部傳入的哪些是 props,哪些是透傳 attribute (關于透傳 attribute,我們會在專門的章(zhāng)節中讨論)。
props 需要(yào)使用 props 選項來定義:
js
export default {
props: ['foo'],
created() {
// props 會暴露到 `this` 上(shàng)
console.log(this.foo)
}
}
除了(le)使用字符串數(shù)組來聲明(míng) prop 外(wài),還可(kě)以使用對象的形式:
js
export default {
props: {
title: String,
likes: Number
}
}
對于以對象形式聲明(míng)中的每個屬性,key 是 prop 的名稱,而值則是該 prop 預期類型的構造函數(shù)。比如(rú),如(rú)果要(yào)求一(yī)個 prop 的值是 number 類型,則可(kě)使用 Number 構造函數(shù)作(zuò)為(wèi)其聲明(míng)的值。
對象形式的 props 聲明(míng)不僅可(kě)以一(yī)定程度上(shàng)作(zuò)為(wèi)組件的文檔,而且如(rú)果其他(tā)開(kāi)發者在使用你的組件時(shí)傳遞了(le)錯誤的類型,也會在浏覽器控制台中抛出警告。我們将在本章(zhāng)節稍後進一(yī)步讨論有關 prop 校驗的更多細節。
TypeScript 用戶請(qǐng)參考:為(wèi)組件 Props 标注類型
傳遞 prop 的細節
Prop 名字格式
如(rú)果一(yī)個 prop 的名字很(hěn)長(cháng),應使用 camelCase 形式,因為(wèi)它們是合法的 JavaScript 标識符,可(kě)以直接在模闆的表達式中使用,也可(kě)以避免在作(zuò)為(wèi)屬性 key 名時(shí)必須加上(shàng)引号。
js
export default {
props: {
greetingMessage: String
}
}
template
<span>{{ greetingMessage }}</span>
雖然理論上(shàng)你也可(kě)以在向子組件傳遞 props 時(shí)使用 camelCase 形式 (使用 DOM 模闆時(shí)例外(wài)),但(dàn)實際上(shàng)為(wèi)了(le)和(hé) HTML attribute 對齊,我們通(tōng)常會将其寫為(wèi) kebab-case 形式:
template
<MyComponent greeting-message="hello" />
對于組件名我們推薦使用 PascalCase,因為(wèi)這(zhè)提高了(le)模闆的可(kě)讀性,能(néng)幫助我們區(qū)分 Vue 組件和(hé)原生 HTML 元素。然而對于傳遞 props 來說,使用 camelCase 并沒有太多優勢,因此我們推薦更貼近 HTML 的書(shū)寫風(fēng)格。
靜态 vs. 動态 Prop
至此,你已經見(jiàn)過了(le)很(hěn)多像這(zhè)樣的靜态值形式的 props:
template
<BlogPost title="My journey with Vue" />
相應地(dì),還有使用 v-bind 或縮寫 : 來進行(xíng)動态綁定的 props:
template
<!-- 根據一(yī)個變量的值動态傳入 -->
<BlogPost :title="post.title" />
<!-- 根據一(yī)個更複雜(zá)表達式的值動态傳入 -->
<BlogPost :title="post.title + ' by ' + post.author.name" />
傳遞不同的值類型
在上(shàng)述的兩個例子中,我們隻傳入了(le)字符串值,但(dàn)實際上(shàng)任何類型的值都(dōu)可(kě)以作(zuò)為(wèi) props 的值被傳遞。
Number
template
<!-- 雖然 `42` 是個常量,我們還是需要(yào)使用 v-bind -->
<!-- 因為(wèi)這(zhè)是一(yī)個 JavaScript 表達式而不是一(yī)個字符串 -->
<BlogPost :likes="42" />
<!-- 根據一(yī)個變量的值動态傳入 -->
<BlogPost :likes="post.likes" />
Boolean
template
<!-- 僅寫上(shàng) prop 但(dàn)不傳值,會隐式轉換為(wèi) `true` -->
<BlogPost is-published />
<!-- 雖然 `false` 是靜态的值,我們還是需要(yào)使用 v-bind -->
<!-- 因為(wèi)這(zhè)是一(yī)個 JavaScript 表達式而不是一(yī)個字符串 -->
<BlogPost :is-published="false" />
<!-- 根據一(yī)個變量的值動态傳入 -->
<BlogPost :is-published="post.isPublished" />
Array
template
<!-- 雖然這(zhè)個數(shù)組是個常量,我們還是需要(yào)使用 v-bind -->
<!-- 因為(wèi)這(zhè)是一(yī)個 JavaScript 表達式而不是一(yī)個字符串 -->
<BlogPost :comment-ids="[234, 266, 273]" />
<!-- 根據一(yī)個變量的值動态傳入 -->
<BlogPost :comment-ids="post.commentIds" />
Object
template
<!-- 雖然這(zhè)個對象字面量是個常量,我們還是需要(yào)使用 v-bind -->
<!-- 因為(wèi)這(zhè)是一(yī)個 JavaScript 表達式而不是一(yī)個字符串 -->
<BlogPost
:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
/>
<!-- 根據一(yī)個變量的值動态傳入 -->
<BlogPost :author="post.author" />
使用一(yī)個對象綁定多個 prop
如(rú)果你想要(yào)将一(yī)個對象的所有屬性都(dōu)當作(zuò) props 傳入,你可(kě)以使用沒有參數(shù)的 v-bind,即隻使用 v-bind 而非 :prop-name。例如(rú),這(zhè)裏有一(yī)個 post 對象:
js
export default {
data() {
return {
post: {
id: 1,
title: 'My Journey with Vue'
}
}
}
}
以及下(xià)面的模闆:
template
<BlogPost v-bind="post" />
而這(zhè)實際上(shàng)等價于:
template
<BlogPost :id="post.id" :title="post.title" />
單向數(shù)據流
所有的 props 都(dōu)遵循着單向綁定原則,props 因父組件的更新而變化,自(zì)然地(dì)将新的狀态向下(xià)流往子組件,而不會逆向傳遞。這(zhè)避免了(le)子組件意外(wài)修改父組件的狀态的情況,不然應用的數(shù)據流将很(hěn)容易變得混亂而難以理解。
另外(wài),每次父組件更新後,所有的子組件中的 props 都(dōu)會被更新到最新值,這(zhè)意味着你不應該在子組件中去更改一(yī)個 prop。若你這(zhè)麽做(zuò)了(le),Vue 會在控制台上(shàng)向你抛出警告:
js
export default {
props: ['foo'],
created() {
// ❌ 警告!prop 是隻讀的!
this.foo = 'bar'
}
}
導緻你想要(yào)更改一(yī)個 prop 的需求通(tōng)常來源于以下(xià)兩種場景:
prop 被用于傳入初始值;而子組件想在之後将其作(zuò)為(wèi)一(yī)個局部數(shù)據屬性。在這(zhè)種情況下(xià),最好是新定義一(yī)個局部數(shù)據屬性,從(cóng) props 上(shàng)獲取初始值即可(kě):
js
export default {
props: ['initialCounter'],
data() {
return {
// 計數(shù)器隻是将 this.initialCounter 作(zuò)為(wèi)初始值
// 像下(xià)面這(zhè)樣做(zuò)就使 prop 和(hé)後續更新無關了(le)
counter: this.initialCounter
}
}
}
需要(yào)對傳入的 prop 值做(zuò)進一(yī)步的轉換。在這(zhè)種情況中,最好是基于該 prop 值定義一(yī)個計算屬性:
js
export default {
props: ['size'],
computed: {
// 該 prop 變更時(shí)計算屬性也會自(zì)動更新
normalizedSize() {
return this.size.trim().toLowerCase()
}
}
}
更改對象 / 數(shù)組類型的 props
當對象或數(shù)組作(zuò)為(wèi) props 被傳入時(shí),雖然子組件無法更改 props 綁定,但(dàn)仍然可(kě)以更改對象或數(shù)組內(nèi)部的值。這(zhè)是因為(wèi) JavaScript 的對象和(hé)數(shù)組是按引用傳遞,而對 Vue 來說,禁止這(zhè)樣的改動,雖然可(kě)能(néng)生效,但(dàn)有很(hěn)大的性能(néng)損耗,比較得不償失。
這(zhè)種更改的主要(yào)缺陷是它允許了(le)子組件以某種不明(míng)顯的方式影響父組件的狀态,可(kě)能(néng)會使數(shù)據流在将來變得更難以理解。在最佳實踐中,你應該盡可(kě)能(néng)避免這(zhè)樣的更改,除非父子組件在設計上(shàng)本來就需要(yào)緊密耦合。在大多數(shù)場景下(xià),子組件應該抛出一(yī)個事件來通(tōng)知父組件做(zuò)出改變。
Prop 校驗
Vue 組件可(kě)以更細緻地(dì)聲明(míng)對傳入的 props 的校驗要(yào)求。比如(rú)我們上(shàng)面已經看(kàn)到過的類型聲明(míng),如(rú)果傳入的值不滿足類型要(yào)求,Vue 會在浏覽器控制台中抛出警告來提醒使用者。這(zhè)在開(kāi)發給其他(tā)開(kāi)發者使用的組件時(shí)非常有用。
要(yào)聲明(míng)對 props 的校驗,你可(kě)以向 props 選項提供一(yī)個帶有 props 校驗選項的對象,例如(rú):
js
export default {
props: {
// 基礎類型檢查
//(給出 `null` 和(hé) `undefined` 值則會跳(tiào)過任何類型檢查)
propA: Number,
// 多種可(kě)能(néng)的類型
propB: [String, Number],
// 必傳,且為(wèi) String 類型
propC: {
type: String,
required: true
},
// Number 類型的默認值
propD: {
type: Number,
default: 100
},
// 對象類型的默認值
propE: {
type: Object,
// 對象或者數(shù)組應當用工(gōng)廠函數(shù)返回。
// 工(gōng)廠函數(shù)會收到組件所接收的原始 props
// 作(zuò)為(wèi)參數(shù)
default(rawProps) {
return { message: 'hello' }
}
},
// 自(zì)定義類型校驗函數(shù)
propF: {
validator(value) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
},
// 函數(shù)類型的默認值
propG: {
type: Function,
// 不像對象或數(shù)組的默認,這(zhè)不是一(yī)個
// 工(gōng)廠函數(shù)。這(zhè)會是一(yī)個用來作(zuò)為(wèi)默認值的函數(shù)
default() {
return 'Default function'
}
}
}
}
一(yī)些補充細節:
所有 prop 默認都(dōu)是可(kě)選的,除非聲明(míng)了(le) required: true。
除 Boolean 外(wài)的未傳遞的可(kě)選 prop 将會有一(yī)個默認值 undefined。
Boolean 類型的未傳遞 prop 将被轉換為(wèi) false。這(zhè)可(kě)以通(tōng)過為(wèi)它設置 default 來更改——例如(rú):設置為(wèi) default: undefined 将與非布爾類型的 prop 的行(xíng)為(wèi)保持一(yī)緻。
如(rú)果聲明(míng)了(le) default 值,那(nà)麽在 prop 的值被解析為(wèi) undefined 時(shí),無論 prop 是未被傳遞還是顯式指明(míng)的 undefined,都(dōu)會改為(wèi) default 值。
當 prop 的校驗失敗後,Vue 會抛出一(yī)個控制台警告 (在開(kāi)發模式下(xià))。
注意
注意 prop 的校驗是在組件實例被創建之前,所以實例的屬性 (比如(rú) data、computed 等) 将在 default 或 validator 函數(shù)中不可(kě)用。
運行(xíng)時(shí)類型檢查
校驗選項中的 type 可(kě)以是下(xià)列這(zhè)些原生構造函數(shù):
String
Number
Boolean
Array
Object
Date
Function
Symbol
另外(wài),type 也可(kě)以是自(zì)定義的類或構造函數(shù),Vue 将會通(tōng)過 instanceof 來檢查類型是否匹配。例如(rú)下(xià)面這(zhè)個類:
js
class Person {
constructor(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
}
你可(kě)以将其作(zuò)為(wèi)一(yī)個 prop 的類型:
js
export default {
props: {
author: Person
}
}
Vue 會通(tōng)過 instanceof Person 來校驗 author prop 的值是否是 Person 類的一(yī)個實例。
Boolean 類型轉換
為(wèi)了(le)更貼近原生 boolean attributes 的行(xíng)為(wèi),聲明(míng)為(wèi) Boolean 類型的 props 有特别的類型轉換規則。以帶有如(rú)下(xià)聲明(míng)的 <MyComponent> 組件為(wèi)例:
js
export default {
props: {
disabled: Boolean
}
}
該組件可(kě)以被這(zhè)樣使用:
template
<!-- 等同于傳入 :disabled="true" -->
<MyComponent disabled />
<!-- 等同于傳入 :disabled="false" -->
<MyComponent />
當一(yī)個 prop 被聲明(míng)為(wèi)允許多種類型時(shí),例如(rú):
js
export default {
props: {
disabled: [Boolean, Number]
}
}
無論聲明(míng)類型的順序如(rú)何,Boolean 類型的特殊轉換規則都(dōu)會被應用。
網站建設開(kāi)發|APP設計開(kāi)發|小程序建設開(kāi)發