Prop
Prop的大小寫(camelCase vs kebab-case)
HTML 中的特性名是大小寫不敏感的,所以瀏覽器會把所有大寫字符解釋爲 小寫字符。這意味着當你使用 DOM 的模板時,camelCase(駝峯命名法)的 prop名需要使用其等價的 kebab-case(短橫線分隔命名)命名:
Vue.component('blog-post',{
// 在JavaScript 中是 camelCase的
props:['postTitle'],
template:'<h3> {{ postTitle }} </h3>'
})
<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-tittle="hello!"></blog-post>
重申一次,如果你使用字符串模板,那麼這個限制就不存在了。
Prop類型
到這裏,我們只看到了以字符串數組形式列出的 prop:
props:['tittle','likes','other']
但是,通常我們都希望每個 prop 都有一個指定的值類型。這時,我們可以以對象形式列出 prop,這些屬性的名稱和值分別是 prop 各自的名稱和類型:
prop:{
tittle:string,
likes:Number,
isPublished:Boolean,
commentIds:Array,
}
這不僅爲你的組件提供了文檔,還會在它們遇到錯誤的類型時從瀏覽器的 JavaScript 控制檯提示用戶。我們會在這個頁面接下來的部分看到 類型檢查和其他 prop 驗證。
傳遞靜態或動態 Prop
像如下這樣,我們已經知道這樣給 prop 傳入一個靜態的值:
<blog-post tittle="My journey with Vue"></blog-post>
我們都知道 prop 可以通過 v-bind 動態賦值,例如:
<!-- 動態賦予一個變量的值 -->
<blog-post v-bind:tittle="post.tittle"></blog-post>
<!-- 動態賦予一個複雜表達式的值 -->
<blog-post
v-bind:tittle="post.tittle + ' By ' post.author.name"></blog-post>
在上面的例子中,我們傳入的值都是字符串類型的,但實際上任何類型的值都可以傳給一個 Prop。
傳入一個數字
<!-- 即便 `42` 是靜態的,我們仍然需要 `v-bind` 來告訴 Vue -->
<!-- 這是一個 JavaScript 表達式而不是一個字符串 -->
<blog-post v-bind:likes="42"></blog-post>
<!-- 用一個變量賦值 -->
<blog-post v-bind:likes="post.likes"></blog-post>
傳入一個布爾值
<!-- 包含該 Prop 沒有值得情況在內,都意味着 `true` -->
<blog-post is-published></blog-post>
<!-- 即便 `false`是靜態的,我們仍然需要 `v-bind` 來告訴 Vue -->
<!-- 這是一個 JavaScript 表達式而不是一個字符串 -->
<blog-post v-bind:is-published="false"></blog-post>
<!-- 用一個變量進行動態賦值 -->
<blog-post v-bind:is-published="post.isPublished"></blog-post>
傳入一個數組
<!-- 即便數組是靜態的,我們仍然需要 `v-bind` 來告訴 Vue -->
<!-- 這是一個 JavaScript 表達式而不是一個字符串。 -->
<blog-post v-bind:comment-ids:"[123,234,456]"></blog-post>
<!-- 用一個變量進行動態賦值。 -->
<blog-post v-bind:comment-ids:"post.commentIds"></blog-post>
傳入一個對象
<!-- 即便對象是靜態的,我們仍然需要`v-bind` 來告訴 Vue -->
<!-- 這是一個 JavaScript 表達式 而不是一個字符串 -->
<blog-post
v-bind:author="{
name:'zzk',
company:'Xnew'
}"></blog-post>
<!-- 用一個變量進行動態賦值 -->
<blog-post v-bind:author="post.author"></blog-post>
傳入一個對象的所有屬性
如果你想要將一個對象的所有屬性都作爲 prop 傳入,我們可以使用不帶參數的 v-bind (取代 v-bind:prop-name),例如:對於一個給定的對象 post:
post:{
id:1,
tittle:"My Journey with Vue"
}
下面的模板:
<blog-post v-bind:"post"></blog-post>
等價於:
<blog-post
v-bind:id='post.id'
v-bind:tittle='post.tittle'></blog-post>
單向數據流
所有的 prop 都使得其父子 prop 之間形成了一個單向下行綁定:父級 prop 的更新會向下流動到子組件中,但是反過來則不行。這樣會防止從子組件意外改變父級組件的狀態,從而導致你的應用的數據流向難以理解。
當然了,額外的,每次父級組件發生更新時,所有子組件的 prop 都會被重新刷新到最新值。這意味着我們不應該在一個子組件內部改變 prop。如果我們這樣做了,Vue 會在瀏覽器的控制檯中發出警告。
這裏有2種常見的試圖改變一個 prop 的情形:
1. 這個 prop 用來傳遞一個初始值;這個子組件接下來希望將其作爲一個本地的 prop 數據來使用。在這種情況下,最好定義一個本地的 data 屬性並將這個 prop 用作其初始值:
props:['initialCounter'],
data: function () {
return {
counter:this.initialCounter
}
}
2.這個 prop 以一種原始的值傳入且需要進行轉換。 在這種情況下,最好使用這個 prop 的值來定義一個計算屬性:
props:['size'],
couputed:{
nomalizedSize:function (){
return this.size.trim().toLowerCase()
}
}
注意:在 JavaScript 中對象和數組是通過引用傳入的,所以對於一個數組或對象類型的 prop 來說,在子組件中改變這個對象或數組本身將會影響到父組件的狀態。
Prop驗證
我們可以爲組件的 prop 指定驗證要求,例如你知道的這些類型。如果有一個需求沒有被滿足,則 Vue 會在瀏覽器控制檯中警告你。這在開發一個會被別人用到的組件時尤其有幫助。
爲了定製 prop 的驗證方式,我們可以爲 props 中的一個值提供一個帶有驗證需求的對象,而不是一個字符串數組。例如:
Vue.component('my-component',{
props:{
//基礎的類型檢查('null' 和'underfined' 會通過任何類型驗證)
propA:Number,
//多個可能的類型
propB:[String, Number],
//必填的字符串
propC:{
type: String,
required:true
},
//帶有默認值的數字
propD:{
type:Number,
default:100
},
//帶有默認值的對象
propE:{
type:Object,
// 對象或數組默認值必須從一個工廠函數獲取
default: function () {
return { message: 'hello' }
}
}
},
//自定義驗證函數:
propF:{
validator:function () {
//這個值必須匹配下列字符串中的一個
return ['success','warning','danger'].indexOf(value) !== -1
}
}
})
當 prop 驗證失敗的時候,(開發環境構建版本的) Vue將會產生一個控制檯的警告。
注意:那些 prop 會在一個組件實例創建之前進行驗證,所以實例的屬性(如data、computed 等)在 default 或 validator 函數中是不可用的。
類型檢查
Type 可以是下列原生構造函數中的一個:
String
Number
Boolean
Array
Object
Date
Function
Symbol
額外的, Type 還可以是一個自定義的構造函數,並且通過 instanceof 來進行檢查確認。例如,給定下列現成的構造函數:
function Person (firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
我們可以使用以下的方式:
Vue.component('blog-post',{
props:{
author:Person
}
})
來驗證 author prop的值是否是通過 New Person 來創建的。
非 Prop 的特性
一個非 Prop 特性是指傳向一個組件,但是該組件並沒有相應 prop 定義的特性。
因爲顯示定義的 prop 適用於向一個子組件中傳入信息,然而組件庫的作者並不總能預見組件會被用於怎麼樣的場景。這也是爲什麼組件可以接受任意的特性,而這些特性會被添加到這個組件的根元素上。
舉個例子,假如我通過一個 Bootstrap 插件使用了一個第三方的 <bootstrap-date-input> 組件時,這個插件需要在其 <input> 上用到一個 data-date-picker 特性。我們可以將這個特性添加到你的組件實例上:
<bootstrap-date-input data-date-picker="activated"></bootstrap-date-input>
替換/合併已有的特性
想象一下, <bootstrap-date-input> 的模板時這樣的:
<input type="date" class="form-control">
爲了給我們的日期選擇器插件定製一個主題,我們可能需要像這樣添加一個特別的類名:
<bootstrap-date-input
data-date-picker:"activated"
class="date-picker-theme-dark"></bootstrap-date-input>
在這種情況下,我們定義了兩個不同的 class 的值:
fom-control, 這是在組件的模板內設置好的
date-picker-theme-dark,這是從組件的父級傳入的
對於絕大多數特性來說,從外部提供給組件的值會替換掉組件內容設置好的值。所以如果傳入 type="text" 就會替換到 type="date" 並把它破壞掉。不過慶幸的是, class 和 style 特性會稍微智能一些,即兩邊的值會被合併起來,從而得到最終的值:form-control date-pick-theme-dark 。
禁用特性繼承
如果我們不希望組件的根元素繼承特性,我們可以在組件的選項中 設置 inheritAttrs:false。例如:
Vue.component('my-component',{
inheritAttrs:false,
// 此處省略....
})
這尤其適合配合實例的 $attrs 屬性使用,該屬性包含了傳遞給一個組件的特性名和特性值,例如:
{
required:true,
placehoder:'Enter your username :'
}
有了 inheritAttrs:false 和 $attrs。我們就可以手動決定這些特性會被賦予哪個元素。在撰寫 基礎組件 的時候是常會被用到的:
Vue.component('base-input',{
inheriatAttrs:false,
props:['label','value'],
template:`
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
`
})
注意: inheritAttrs: false 選項不會影響 style 和 class 的綁定。
這個模式允許你在使用基礎組件的時候更像是使用原始的 HTML 元素,而不會擔心哪個元素是真正的根元素:
<base-input
v-model:"username"
required
placeholder="Enter your username">
</base-input>