Vue.js 使用了基於 HTML 的模板語法,容許開發者聲明式地將 DOM 綁定至底層 Vue 實例的數據。全部 Vue.js 的模板都是合法的 HTML ,因此能被遵循規範的瀏覽器和 HTML 解析器解析。javascript
在底層的實現上,Vue 將模板編譯成虛擬 DOM 渲染函數。結合響應系統,Vue 可以智能地計算出最少須要從新渲染多少組件,並把 DOM 操做次數減到最少。php
若是你熟悉虛擬 DOM 而且偏心 JavaScript 的原始力量,你也能夠不用模板,直接寫渲染 (render) 函數,使用可選的 JSX 語法。css
數據綁定最多見的形式就是使用「Mustache」語法 (雙大括號) 的文本插值:html
<span>Message: {{ msg }}</span> |
Mustache 標籤將會被替代爲對應數據對象上 msg
屬性的值。不管什麼時候,綁定的數據對象上 msg
屬性發生了改變,插值處的內容都會更新。vue
經過使用 v-once 指令,你也能執行一次性地插值,當數據改變時,插值處的內容不會更新。但請留心這會影響到該節點上的其它數據綁定:java
<span v-once>這個將不會改變: {{ msg }}</span> |
雙大括號會將數據解釋爲普通文本,而非 HTML 代碼。爲了輸出真正的 HTML,你須要使用 v-html
指令:webpack
<p>Using mustaches: {{ rawHtml }}</p> |
Using mustaches: <span style="color: red">This should be red.</span>ios
Using v-html directive: This should be red.git
這個 span
的內容將會被替換成爲屬性值 rawHtml
,直接做爲 HTML——會忽略解析屬性值中的數據綁定。注意,你不能使用 v-html
來複合局部模板,由於 Vue 不是基於字符串的模板引擎。反之,對於用戶界面 (UI),組件更適合做爲可重用和可組合的基本單位。github
你的站點上動態渲染的任意 HTML 可能會很是危險,由於它很容易致使 XSS 攻擊。請只對可信內容使用 HTML 插值,毫不要對用戶提供的內容使用插值。
Mustache 語法不能做用在 HTML 特性上,遇到這種狀況應該使用 v-bind 指令:
<div v-bind:id="dynamicId"></div> |
在布爾特性的狀況下,它們的存在即暗示爲 true
,v-bind
工做起來略有不一樣,在這個例子中:
<button v-bind:disabled="isButtonDisabled">Button</button> |
若是 isButtonDisabled
的值是 null
、undefined
或 false
,則 disabled
特性甚至不會被包含在渲染出來的 <button>
元素中。
迄今爲止,在咱們的模板中,咱們一直都只綁定簡單的屬性鍵值。但實際上,對於全部的數據綁定,Vue.js 都提供了徹底的 JavaScript 表達式支持。
{{ number + 1 }} |
這些表達式會在所屬 Vue 實例的數據做用域下做爲 JavaScript 被解析。有個限制就是,每一個綁定都只能包含單個表達式,因此下面的例子都不會生效。
<!-- 這是語句,不是表達式 --> |
模板表達式都被放在沙盒中,只能訪問全局變量的一個白名單,如 Math
和 Date
。你不該該在模板表達式中試圖訪問用戶定義的全局變量。
指令 (Directives) 是帶有 v-
前綴的特殊特性。指令特性的值預期是單個 JavaScript 表達式 (v-for
是例外狀況,稍後咱們再討論)。指令的職責是,當表達式的值改變時,將其產生的連帶影響,響應式地做用於 DOM。回顧咱們在介紹中看到的例子:
<p v-if="seen">如今你看到我了</p> |
這裏,v-if
指令將根據表達式 seen
的值的真假來插入/移除 <p>
元素。
一些指令可以接收一個「參數」,在指令名稱以後以冒號表示。例如,v-bind
指令能夠用於響應式地更新 HTML 特性:
<a v-bind:href="url">...</a> |
在這裏 href
是參數,告知 v-bind
指令將該元素的 href
特性與表達式 url
的值綁定。
另外一個例子是 v-on
指令,它用於監聽 DOM 事件:
<a v-on:click="doSomething">...</a> |
在這裏參數是監聽的事件名。咱們也會更詳細地討論事件處理。
修飾符 (Modifiers) 是以半角句號 .
指明的特殊後綴,用於指出一個指令應該以特殊方式綁定。例如,.prevent
修飾符告訴 v-on
指令對於觸發的事件調用 event.preventDefault()
:
<form v-on:submit.prevent="onSubmit">...</form> |
在接下來對 v-on
和 v-for
等功能的探索中,你會看到修飾符的其它例子。
v-
前綴做爲一種視覺提示,用來識別模板中 Vue 特定的特性。當你在使用 Vue.js 爲現有標籤添加動態行爲 (dynamic behavior) 時,v-
前綴頗有幫助,然而,對於一些頻繁用到的指令來講,就會感到使用繁瑣。同時,在構建由 Vue.js 管理全部模板的單頁面應用程序 (SPA - single page application) 時,v-
前綴也變得沒那麼重要了。所以,Vue.js 爲 v-bind
和 v-on
這兩個最經常使用的指令,提供了特定簡寫:
v-bind
縮寫 <!-- 完整語法 --> |
v-on
縮寫 <!-- 完整語法 --> |
它們看起來可能與普通的 HTML 略有不一樣,但 :
與 @
對於特性名來講都是合法字符,在全部支持 Vue.js 的瀏覽器都能被正確地解析。並且,它們不會出如今最終渲染的標記中。縮寫語法是徹底可選的,但隨着你更深刻地瞭解它們的做用,你會慶幸擁有它們。
模板內的表達式很是便利,可是設計它們的初衷是用於簡單運算的。在模板中放入太多的邏輯會讓模板太重且難以維護。例如:
<div id="example"> |
在這個地方,模板再也不是簡單的聲明式邏輯。你必須看一段時間才能意識到,這裏是想要顯示變量 message
的翻轉字符串。當你想要在模板中屢次引用此處的翻轉字符串時,就會更加難以處理。
因此,對於任何複雜邏輯,你都應當使用計算屬性。
<div id="example"> |
var vm = new Vue({ |
結果:
Original message: "Hello"
Computed reversed message: "olleH"
這裏咱們聲明瞭一個計算屬性 reversedMessage
。咱們提供的函數將用做屬性 vm.reversedMessage
的 getter 函數:
console.log(vm.reversedMessage) // => 'olleH' |
你能夠打開瀏覽器的控制檯,自行修改例子中的 vm。vm.reversedMessage
的值始終取決於 vm.message
的值。
你能夠像綁定普通屬性同樣在模板中綁定計算屬性。Vue 知道 vm.reversedMessage
依賴於 vm.message
,所以當 vm.message
發生改變時,全部依賴 vm.reversedMessage
的綁定也會更新。並且最妙的是咱們已經以聲明的方式建立了這種依賴關係:計算屬性的 getter 函數是沒有反作用 (side effect) 的,這使它更易於測試和理解。
你可能已經注意到咱們能夠經過在表達式中調用方法來達到一樣的效果:
<p>Reversed message: "{{ reversedMessage() }}"</p> |
// 在組件中 |
咱們能夠將同一函數定義爲一個方法而不是一個計算屬性。兩種方式的最終結果確實是徹底相同的。然而,不一樣的是計算屬性是基於它們的依賴進行緩存的。只在相關依賴發生改變時它們纔會從新求值。這就意味着只要 message
尚未發生改變,屢次訪問 reversedMessage
計算屬性會當即返回以前的計算結果,而沒必要再次執行函數。
這也一樣意味着下面的計算屬性將再也不更新,由於 Date.now()
不是響應式依賴:
computed: { |
相比之下,每當觸發從新渲染時,調用方法將總會再次執行函數。
咱們爲何須要緩存?假設咱們有一個性能開銷比較大的計算屬性 A,它須要遍歷一個巨大的數組並作大量的計算。而後咱們可能有其餘的計算屬性依賴於 A 。若是沒有緩存,咱們將不可避免的屢次執行 A 的 getter!若是你不但願有緩存,請用方法來替代。
Vue 提供了一種更通用的方式來觀察和響應 Vue 實例上的數據變更:偵聽屬性。當你有一些數據須要隨着其它數據變更而變更時,你很容易濫用 watch
——特別是若是你以前使用過 AngularJS。然而,一般更好的作法是使用計算屬性而不是命令式的 watch
回調。細想一下這個例子:
<div id="demo">{{ fullName }}</div> |
var vm = new Vue({ |
上面代碼是命令式且重複的。將它與計算屬性的版本進行比較:
var vm = new Vue({ |
好得多了,不是嗎?
計算屬性默認只有 getter ,不過在須要時你也能夠提供一個 setter :
// ... |
如今再運行 vm.fullName = 'John Doe'
時,setter 會被調用,vm.firstName
和 vm.lastName
也會相應地被更新。
雖然計算屬性在大多數狀況下更合適,但有時也須要一個自定義的偵聽器。這就是爲何 Vue 經過 watch
選項提供了一個更通用的方法,來響應數據的變化。當須要在數據變化時執行異步或開銷較大的操做時,這個方式是最有用的。
例如:
<div id="watch-example"> |
<!-- 由於 AJAX 庫和通用工具的生態已經至關豐富,Vue 核心代碼沒有重複 --> |
結果:
Ask a yes/no question:
I cannot give you an answer until you ask a question!
在這個示例中,使用 watch
選項容許咱們執行異步操做 (訪問一個 API),限制咱們執行該操做的頻率,並在咱們獲得最終結果前,設置中間狀態。這些都是計算屬性沒法作到的。
除了 watch
選項以外,您還可使用命令式的 vm.$watch API。
操做元素的 class 列表和內聯樣式是數據綁定的一個常見需求。由於它們都是屬性,因此咱們能夠用 v-bind
處理它們:只須要經過表達式計算出字符串結果便可。不過,字符串拼接麻煩且易錯。所以,在將 v-bind
用於 class
和 style
時,Vue.js 作了專門的加強。表達式結果的類型除了字符串以外,還能夠是對象或數組。
咱們能夠傳給 v-bind:class
一個對象,以動態地切換 class:
<div v-bind:class="{ active: isActive }"></div> |
上面的語法表示 active
這個 class 存在與否將取決於數據屬性 isActive
的 truthiness。
你能夠在對象中傳入更多屬性來動態切換多個 class。此外,v-bind:class
指令也能夠與普通的 class 屬性共存。當有以下模板:
<div class="static" |
和以下 data:
data: { |
結果渲染爲:
<div class="static active"></div> |
當 isActive
或者 hasError
變化時,class 列表將相應地更新。例如,若是 hasError
的值爲 true
,class 列表將變爲 "static active text-danger"
。
綁定的數據對象沒必要內聯定義在模板裏:
<div v-bind:class="classObject"></div> |
data: { |
渲染的結果和上面同樣。咱們也能夠在這裏綁定一個返回對象的計算屬性。這是一個經常使用且強大的模式:
<div v-bind:class="classObject"></div> |
data: { |
咱們能夠把一個數組傳給 v-bind:class
,以應用一個 class 列表:
<div v-bind:class="[activeClass, errorClass]"></div> |
data: { |
渲染爲:
<div class="active text-danger"></div> |
若是你也想根據條件切換列表中的 class,能夠用三元表達式:
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div> |
這樣寫將始終添加 errorClass
,可是隻有在 isActive
是 truthy[1] 時才添加 activeClass
。
不過,當有多個條件 class 時這樣寫有些繁瑣。因此在數組語法中也可使用對象語法:
<div v-bind:class="[{ active: isActive }, errorClass]"></div> |
這個章節假設你已經對 Vue 組件有必定的瞭解。固然你也能夠先跳過這裏,稍後再回過頭來看。
當在一個自定義組件上使用 class
屬性時,這些類將被添加到該組件的根元素上面。這個元素上已經存在的類不會被覆蓋。
例如,若是你聲明瞭這個組件:
Vue.component('my-component', { |
而後在使用它的時候添加一些 class:
<my-component class="baz boo"></my-component> |
HTML 將被渲染爲:
<p class="foo bar baz boo">Hi</p> |
對於帶數據綁定 class 也一樣適用:
<my-component v-bind:class="{ active: isActive }"></my-component> |
當 isActive
爲 truthy[1] 時,HTML 將被渲染成爲:
<p class="foo bar active">Hi</p> |
v-bind:style
的對象語法十分直觀——看着很是像 CSS,但實際上是一個 JavaScript 對象。CSS 屬性名能夠用駝峯式 (camelCase) 或短橫線分隔 (kebab-case,記得用單引號括起來) 來命名:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> |
data: { |
直接綁定到一個樣式對象一般更好,這會讓模板更清晰:
<div v-bind:style="styleObject"></div> |
data: { |
一樣的,對象語法經常結合返回對象的計算屬性使用。
v-bind:style
的數組語法能夠將多個樣式對象應用到同一個元素上:
<div v-bind:style="[baseStyles, overridingStyles]"></div> |
當 v-bind:style
使用須要添加瀏覽器引擎前綴的 CSS 屬性時,如 transform
,Vue.js 會自動偵測並添加相應的前綴。
2.3.0+
從 2.3.0 起你能夠爲 style
綁定中的屬性提供一個包含多個值的數組,經常使用於提供多個帶前綴的值,例如:
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div> |
這樣寫只會渲染數組中最後一個被瀏覽器支持的值。在本例中,若是瀏覽器支持不帶瀏覽器前綴的 flexbox,那麼就只會渲染 display: flex
。
譯者注
[1] truthy 不是 true
,詳見 MDN 的解釋。
v-if
在字符串模板中,好比 Handlebars,咱們得像這樣寫一個條件塊:
<!-- Handlebars 模板 --> |
在 Vue 中,咱們使用 v-if
指令實現一樣的功能:
<h1 v-if="ok">Yes</h1> |
也能夠用 v-else
添加一個「else 塊」:
<h1 v-if="ok">Yes</h1> |
<template>
元素上使用 v-if
條件渲染分組由於 v-if
是一個指令,因此必須將它添加到一個元素上。可是若是想切換多個元素呢?此時能夠把一個 <template>
元素當作不可見的包裹元素,並在上面使用 v-if
。最終的渲染結果將不包含 <template>
元素。
<template v-if="ok"> |
v-else
你可使用 v-else
指令來表示 v-if
的「else 塊」:
<div v-if="Math.random() > 0.5"> |
v-else
元素必須緊跟在帶 v-if
或者 v-else-if
的元素的後面,不然它將不會被識別。
v-else-if
2.1.0 新增
v-else-if
,顧名思義,充當 v-if
的「else-if 塊」,能夠連續使用:
<div v-if="type === 'A'"> |
相似於 v-else
,v-else-if
也必須緊跟在帶 v-if
或者 v-else-if
的元素以後。
key
管理可複用的元素Vue 會盡量高效地渲染元素,一般會複用已有元素而不是從頭開始渲染。這麼作除了使 Vue 變得很是快以外,還有其它一些好處。例如,若是你容許用戶在不一樣的登陸方式之間切換:
<template v-if="loginType === 'username'"> |
那麼在上面的代碼中切換 loginType
將不會清除用戶已經輸入的內容。由於兩個模板使用了相同的元素,<input>
不會被替換掉——僅僅是替換了它的 placeholder
。
本身動手試一試,在輸入框中輸入一些文本,而後按下切換按鈕:
這樣也不老是符合實際需求,因此 Vue 爲你提供了一種方式來表達「這兩個元素是徹底獨立的,不要複用它們」。只需添加一個具備惟一值的 key
屬性便可:
<template v-if="loginType === 'username'"> |
如今,每次切換時,輸入框都將被從新渲染。請看:
注意,<label>
元素仍然會被高效地複用,由於它們沒有添加 key
屬性。
v-show
另外一個用於根據條件展現元素的選項是 v-show
指令。用法大體同樣:
<h1 v-show="ok">Hello!</h1> |
不一樣的是帶有 v-show
的元素始終會被渲染並保留在 DOM 中。v-show
只是簡單地切換元素的 CSS 屬性 display
。
注意,v-show
不支持 <template>
元素,也不支持 v-else
。
v-if
vs v-show
v-if
是「真正」的條件渲染,由於它會確保在切換過程當中條件塊內的事件監聽器和子組件適當地被銷燬和重建。
v-if
也是惰性的:若是在初始渲染時條件爲假,則什麼也不作——直到條件第一次變爲真時,纔會開始渲染條件塊。
相比之下,v-show
就簡單得多——無論初始條件是什麼,元素老是會被渲染,而且只是簡單地基於 CSS 進行切換。
通常來講,v-if
有更高的切換開銷,而 v-show
有更高的初始渲染開銷。所以,若是須要很是頻繁地切換,則使用 v-show
較好;若是在運行時條件不多改變,則使用 v-if
較好。
v-if
與 v-for
一塊兒使用不推薦同時使用 v-if
和 v-for
。請查閱風格指南以獲取更多信息。
當 v-if
與 v-for
一塊兒使用時,v-for
具備比 v-if
更高的優先級。請查閱列表渲染指南 以獲取詳細信息。
v-for
把一個數組對應爲一組元素咱們用 v-for
指令根據一組數組的選項列表進行渲染。v-for
指令須要使用 item in items
形式的特殊語法,items
是源數據數組而且 item
是數組元素迭代的別名。
<ul id="example-1"> |
var example1 = new Vue({ |
結果:
在 v-for
塊中,咱們擁有對父做用域屬性的徹底訪問權限。v-for
還支持一個可選的第二個參數爲當前項的索引。
<ul id="example-2"> |
var example2 = new Vue({ |
結果:
你也能夠用 of
替代 in
做爲分隔符,由於它是最接近 JavaScript 迭代器的語法:
<div v-for="item of items"></div> |
v-for
你也能夠用 v-for
經過一個對象的屬性來迭代。
<ul id="v-for-object" class="demo"> |
new Vue({ |
結果:
你也能夠提供第二個的參數爲鍵名:
<div v-for="(value, key) in object"> |
第三個參數爲索引:
<div v-for="(value, key, index) in object"> |
在遍歷對象時,是按 Object.keys()
的結果遍歷,可是不能保證它的結果在不一樣的 JavaScript 引擎下是一致的。
key
當 Vue.js 用 v-for
正在更新已渲染過的元素列表時,它默認用「就地複用」策略。若是數據項的順序被改變,Vue 將不會移動 DOM 元素來匹配數據項的順序, 而是簡單複用此處每一個元素,而且確保它在特定索引下顯示已被渲染過的每一個元素。這個相似 Vue 1.x 的 track-by="$index"
。
這個默認的模式是高效的,可是隻適用於不依賴子組件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出。
爲了給 Vue 一個提示,以便它能跟蹤每一個節點的身份,從而重用和從新排序現有元素,你須要爲每項提供一個惟一 key
屬性。理想的 key
值是每項都有的惟一 id。這個特殊的屬性至關於 Vue 1.x 的 track-by
,但它的工做方式相似於一個屬性,因此你須要用 v-bind
來綁定動態值 (在這裏使用簡寫):
<div v-for="item in items" :key="item.id"> |
建議儘量在使用 v-for
時提供 key
,除非遍歷輸出的 DOM 內容很是簡單,或者是刻意依賴默認行爲以獲取性能上的提高。
由於它是 Vue 識別節點的一個通用機制,key
並不與 v-for
特別關聯,key 還具備其餘用途,咱們將在後面的指南中看到其餘用途。
Vue 包含一組觀察數組的變異方法,因此它們也將會觸發視圖更新。這些方法以下:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
你打開控制檯,而後用前面例子的 items
數組調用變異方法:example1.items.push({ message: 'Baz' })
。
變異方法 (mutation method),顧名思義,會改變被這些方法調用的原始數組。相比之下,也有非變異 (non-mutating method) 方法,例如:filter()
, concat()
和 slice()
。這些不會改變原始數組,但老是返回一個新數組。當使用非變異方法時,能夠用新數組替換舊數組:
example1.items = example1.items.filter(function (item) { |
你可能認爲這將致使 Vue 丟棄現有 DOM 並從新渲染整個列表。幸運的是,事實並不是如此。Vue 爲了使得 DOM 元素獲得最大範圍的重用而實現了一些智能的、啓發式的方法,因此用一個含有相同元素的數組去替換原來的數組是很是高效的操做。
因爲 JavaScript 的限制,Vue 不能檢測如下變更的數組:
vm.items[indexOfItem] = newValue
vm.items.length = newLength
舉個例子:
var vm = new Vue({ |
爲了解決第一類問題,如下兩種方式均可以實現和 vm.items[indexOfItem] = newValue
相同的效果,同時也將觸發狀態更新:
// Vue.set |
// Array.prototype.splice |
你也可使用 vm.$set
實例方法,該方法是全局方法 Vue.set
的一個別名:
vm.$set(vm.items, indexOfItem, newValue) |
爲了解決第二類問題,你可使用 splice
:
vm.items.splice(newLength) |
仍是因爲 JavaScript 的限制,Vue 不能檢測對象屬性的添加或刪除:
var vm = new Vue({ |
對於已經建立的實例,Vue 不能動態添加根級別的響應式屬性。可是,可使用 Vue.set(object, key, value)
方法向嵌套對象添加響應式屬性。例如,對於:
var vm = new Vue({ |
你能夠添加一個新的 age
屬性到嵌套的 userProfile
對象:
Vue.set(vm.userProfile, 'age', 27) |
你還可使用 vm.$set
實例方法,它只是全局 Vue.set
的別名:
vm.$set(vm.userProfile, 'age', 27) |
有時你可能須要爲已有對象賦予多個新屬性,好比使用 Object.assign()
或 _.extend()
。在這種狀況下,你應該用兩個對象的屬性建立一個新的對象。因此,若是你想添加新的響應式屬性,不要像這樣:
Object.assign(vm.userProfile, { |
你應該這樣作:
vm.userProfile = Object.assign({}, vm.userProfile, { |
有時,咱們想要顯示一個數組的過濾或排序副本,而不實際改變或重置原始數據。在這種狀況下,能夠建立返回過濾或排序數組的計算屬性。
例如:
<li v-for="n in evenNumbers">{{ n }}</li> |
data: { |
在計算屬性不適用的狀況下 (例如,在嵌套 v-for
循環中) 你可使用一個 method 方法:
<li v-for="n in even(numbers)">{{ n }}</li> |
data: { |
v-for
v-for
也能夠取整數。在這種狀況下,它將重複屢次模板。
<div> |
結果:
v-for
on a <template>
相似於 v-if
,你也能夠利用帶有 v-for
的 <template>
渲染多個元素。好比:
<ul> |
v-for
with v-if
當它們處於同一節點,v-for
的優先級比 v-if
更高,這意味着 v-if
將分別重複運行於每一個 v-for
循環中。當你想爲僅有的一些項渲染節點時,這種優先級的機制會十分有用,以下:
<li v-for="todo in todos" v-if="!todo.isComplete"> |
上面的代碼只傳遞了未完成的 todos。
而若是你的目的是有條件地跳過循環的執行,那麼能夠將 v-if
置於外層元素 (或 <template>
)上。如:
<ul v-if="todos.length"> |
v-for
瞭解組件相關知識,查看 組件。徹底能夠先跳過它,之後再回來查看。
在自定義組件裏,你能夠像任何普通元素同樣用 v-for
。
<my-component v-for="item in items" :key="item.id"></my-component> |
2.2.0+ 的版本里,當在組件中使用
v-for
時,key
如今是必須的。
然而,任何數據都不會被自動傳遞到組件裏,由於組件有本身獨立的做用域。爲了把迭代數據傳遞到組件裏,咱們要用 props
:
<my-component |
不自動將 item
注入到組件裏的緣由是,這會使得組件與 v-for
的運做緊密耦合。明確組件數據的來源可以使組件在其餘場合重複使用。
下面是一個簡單的 todo list 的完整例子:
<div id="todo-list-example"> |
注意這裏的 is="todo-item"
屬性。這種作法在使用 DOM 模板時是十分必要的,由於在 <ul>
元素內只有 <li>
元素會被看做有效內容。這樣作實現的效果與 <todo-item>
相同,可是能夠避開一些潛在的瀏覽器解析錯誤。查看 DOM 模板解析說明 來了解更多信息。
Vue.component('todo-item', { |
能夠用 v-on
指令監聽 DOM 事件,並在觸發時運行一些 JavaScript 代碼。
示例:
<div id="example-1"> |
var example1 = new Vue({ |
結果:
The button above has been clicked 0 times.
然而許多事件處理邏輯會更爲複雜,因此直接把 JavaScript 代碼寫在 v-on
指令中是不可行的。所以 v-on
還能夠接收一個須要調用的方法名稱。
示例:
<div id="example-2"> |
var example2 = new Vue({ |
結果:
除了直接綁定到一個方法,也能夠在內聯 JavaScript 語句中調用方法:
<div id="example-3"> |
new Vue({ |
結果:
有時也須要在內聯語句處理器中訪問原始的 DOM 事件。能夠用特殊變量 $event
把它傳入方法:
<button v-on:click="warn('Form cannot be submitted yet.', $event)"> |
// ... |
在事件處理程序中調用 event.preventDefault()
或 event.stopPropagation()
是很是常見的需求。儘管咱們能夠在方法中輕鬆實現這點,但更好的方式是:方法只有純粹的數據邏輯,而不是去處理 DOM 事件細節。
爲了解決這個問題,Vue.js 爲 v-on
提供了事件修飾符。以前提過,修飾符是由點開頭的指令後綴來表示的。
.stop
.prevent
.capture
.self
.once
.passive
<!-- 阻止單擊事件繼續傳播 --> |
使用修飾符時,順序很重要;相應的代碼會以一樣的順序產生。所以,用 v-on:click.prevent.self
會阻止全部的點擊,而 v-on:click.self.prevent
只會阻止對元素自身的點擊。
2.1.4 新增
<!-- 點擊事件將只會觸發一次 --> |
不像其它只能對原生的 DOM 事件起做用的修飾符,.once
修飾符還能被用到自定義的組件事件上。若是你尚未閱讀關於組件的文檔,如今大可沒必要擔憂。
2.3.0 新增
Vue 還對應 addEventListener
中的 passive
選項提供了 .passive
修飾符。
<!-- 滾動事件的默認行爲 (即滾動行爲) 將會當即觸發 --> |
這個 .passive
修飾符尤爲可以提高移動端的性能。
不要把 .passive
和 .prevent
一塊兒使用,由於 .prevent
將會被忽略,同時瀏覽器可能會向你展現一個警告。請記住,.passive
會告訴瀏覽器你不想阻止事件的默認行爲。
在監聽鍵盤事件時,咱們常常須要檢查常見的鍵值。Vue 容許爲 v-on
在監聽鍵盤事件時添加按鍵修飾符:
<!-- 只有在 `keyCode` 是 13 時調用 `vm.submit()` --> |
記住全部的 keyCode
比較困難,因此 Vue 爲最經常使用的按鍵提供了別名:
<!-- 同上 --> |
所有的按鍵別名:
.enter
.tab
.delete
(捕獲「刪除」和「退格」鍵).esc
.space
.up
.down
.left
.right
能夠經過全局 config.keyCodes
對象自定義按鍵修飾符別名:
// 可使用 `v-on:keyup.f1` |
2.5.0 新增
你也可直接將 KeyboardEvent.key
暴露的任意有效按鍵名轉換爲 kebab-case 來做爲修飾符:
<input @keyup.page-down="onPageDown"> |
在上面的例子中,處理函數僅在 $event.key === 'PageDown'
時被調用。
有一些按鍵 (.esc
以及全部的方向鍵) 在 IE9 中有不一樣的 key
值, 若是你想支持 IE9,它們的內置別名應該是首選。
2.1.0 新增
能夠用以下修飾符來實現僅在按下相應按鍵時才觸發鼠標或鍵盤事件的監聽器。
.ctrl
.alt
.shift
.meta
注意:在 Mac 系統鍵盤上,meta 對應 command 鍵 (⌘)。在 Windows 系統鍵盤 meta 對應 Windows 徽標鍵 (⊞)。在 Sun 操做系統鍵盤上,meta 對應實心寶石鍵 (◆)。在其餘特定鍵盤上,尤爲在 MIT 和 Lisp 機器的鍵盤、以及其後繼產品,好比 Knight 鍵盤、space-cadet 鍵盤,meta 被標記爲「META」。在 Symbolics 鍵盤上,meta 被標記爲「META」或者「Meta」。
例如:
<!-- Alt + C --> |
請注意修飾鍵與常規按鍵不一樣,在和 keyup
事件一塊兒用時,事件觸發時修飾鍵必須處於按下狀態。換句話說,只有在按住 ctrl
的狀況下釋放其它按鍵,才能觸發 keyup.ctrl
。而單單釋放 ctrl
也不會觸發事件。若是你想要這樣的行爲,請爲 ctrl
換用 keyCode
:keyup.17
。
.exact
修飾符2.5.0 新增
.exact
修飾符容許你控制由精確的系統修飾符組合觸發的事件。
<!-- 即便 Alt 或 Shift 被一同按下時也會觸發 --> |
2.2.0 新增
.left
.right
.middle
這些修飾符會限制處理函數僅響應特定的鼠標按鈕。
你可能注意到這種事件監聽的方式違背了關注點分離 (separation of concern) 這個長期以來的優良傳統。但沒必要擔憂,由於全部的 Vue.js 事件處理方法和表達式都嚴格綁定在當前視圖的 ViewModel 上,它不會致使任何維護上的困難。實際上,使用 v-on
有幾個好處:
掃一眼 HTML 模板便能輕鬆定位在 JavaScript 代碼裏對應的方法。
由於你無須在 JavaScript 裏手動綁定事件,你的 ViewModel 代碼能夠是很是純粹的邏輯,和 DOM 徹底解耦,更易於測試。
當一個 ViewModel 被銷燬時,全部的事件處理器都會自動被刪除。你無須擔憂如何清理它們。
你能夠用 v-model
指令在表單 <input>
、<textarea>
及 <select>
元素上建立雙向數據綁定。它會根據控件類型自動選取正確的方法來更新元素。儘管有些神奇,但 v-model
本質上不過是語法糖。它負責監聽用戶的輸入事件以更新數據,並對一些極端場景進行一些特殊處理。
v-model
會忽略全部表單元素的 value
、checked
、selected
特性的初始值而老是將 Vue 實例的數據做爲數據來源。你應該經過 JavaScript 在組件的 data
選項中聲明初始值。
對於須要使用輸入法 (如中文、日文、韓文等) 的語言,你會發現 v-model
不會在輸入法組合文字過程當中獲得更新。若是你也想處理這個過程,請使用 input
事件。
<input v-model="message" placeholder="edit me"> |
Message is:
<span>Multiline message is:</span> |
在文本區域插值 (<textarea></textarea>
) 並不會生效,應用 v-model
來代替。
單個複選框,綁定到布爾值:
<input type="checkbox" id="checkbox" v-model="checked"> |
多個複選框,綁定到同一個數組:
<div id='example-3'> |
new Vue({ |
<div id="example-4"> |
new Vue({ |
單選時:
<div id="example-5"> |
new Vue({ |
若是 v-model
表達式的初始值未能匹配任何選項,<select>
元素將被渲染爲「未選中」狀態。在 iOS 中,這會使用戶沒法選擇第一個選項。由於這樣的狀況下,iOS 不會觸發 change 事件。所以,更推薦像上面這樣提供一個值爲空的禁用選項。
多選時 (綁定到一個數組):
<div id="example-6"> |
new Vue({ |
用 v-for
渲染的動態選項:
<select v-model="selected"> |
new Vue({ |
對於單選按鈕,複選框及選擇框的選項,v-model
綁定的值一般是靜態字符串 (對於複選框也能夠是布爾值):
<!-- 當選中時,`picked` 爲字符串 "a" --> |
可是有時咱們可能想把值綁定到 Vue 實例的一個動態屬性上,這時能夠用 v-bind
實現,而且這個屬性的值能夠不是字符串。
<input |
// 當選中時 |
這裏的 true-value
和 false-value
特性並不會影響輸入控件的 value
特性,由於瀏覽器在提交表單時並不會包含未被選中的複選框。若是要確保表單中這兩個值中的一個可以被提交,(好比「yes」或「no」),請換用單選按鈕。
<input type="radio" v-model="pick" v-bind:value="a"> |
// 當選中時 |
<select v-model="selected"> |
// 當選中時 |
.lazy
在默認狀況下,v-model
在每次 input
事件觸發後將輸入框的值與數據進行同步 (除了上述輸入法組合文字時)。你能夠添加 lazy
修飾符,從而轉變爲使用 change
事件進行同步:
<!-- 在「change」時而非「input」時更新 --> |
.number
若是想自動將用戶的輸入值轉爲數值類型,能夠給 v-model
添加 number
修飾符:
<input v-model.number="age" type="number"> |
這一般頗有用,由於即便在 type="number"
時,HTML 輸入元素的值也總會返回字符串。若是這個值沒法被 parseFloat()
解析,則會返回原始的值。
.trim
若是要自動過濾用戶輸入的首尾空白字符,能夠給 v-model
添加 trim
修飾符:
<input v-model.trim="msg"> |
v-model
若是你還不熟悉 Vue 的組件,能夠暫且跳過這裏。
HTML 原生的輸入元素類型並不總能知足需求。幸虧,Vue 的組件系統容許你建立具備徹底自定義行爲且可複用的輸入組件。這些輸入組件甚至能夠和 v-model
一塊兒使用!要了解更多,請參閱組件指南中的自定義輸入組件。
這裏有一個 Vue 組件的示例:
// 定義一個名爲 button-counter 的新組件 |
組件是可複用的 Vue 實例,且帶有一個名字:在這個例子中是 <button-counter>
。咱們能夠在一個經過 new Vue
建立的 Vue 根實例中,把這個組件做爲自定義元素來使用:
<div id="components-demo"> |
new Vue({ el: '#components-demo' }) |
由於組件是可複用的 Vue 實例,因此它們與 new Vue
接收相同的選項,例如 data
、computed
、watch
、methods
以及生命週期鉤子等。僅有的例外是像 el
這樣根實例特有的選項。
你能夠將組件進行任意次數的複用:
<div id="components-demo"> |
注意當點擊按鈕時,每一個組件都會各自獨立維護它的 count
。由於你每用一次組件,就會有一個它的新實例被建立。
data
必須是一個函數當咱們定義這個 <button-counter>
組件時,你可能會發現它的 data
並非像這樣直接提供一個對象:
data: { |
取而代之的是,一個組件的 data
選項必須是一個函數,所以每一個實例能夠維護一份被返回對象的獨立的拷貝:
data: function () { |
若是 Vue 沒有這條規則,點擊一個按鈕就可能會像以下代碼同樣影響到其它全部實例:
一般一個應用會以一棵嵌套的組件樹的形式來組織:
例如,你可能會有頁頭、側邊欄、內容區等組件,每一個組件又包含了其它的像導航連接、博文之類的組件。
爲了能在模板中使用,這些組件必須先註冊以便 Vue 可以識別。這裏有兩種組件的註冊類型:全局註冊和局部註冊。至此,咱們的組件都只是經過 Vue.component
全局註冊的:
Vue.component('my-component-name', { |
全局註冊的組件能夠用在其被註冊以後的任何 (經過 new Vue
) 新建立的 Vue 根實例,也包括其組件樹中的全部子組件的模板中。
到目前爲止,關於組件註冊你須要瞭解的就這些了,若是你閱讀完本頁內容並掌握了它的內容,咱們會推薦你再回來把組件註冊讀完。
早些時候,咱們提到了建立一個博文組件的事情。問題是若是你不能向這個組件傳遞某一篇博文的標題或內容之類的咱們想展現的數據的話,它是沒有辦法使用的。這也正是 prop 的由來。
Prop 是你能夠在組件上註冊的一些自定義特性。當一個值傳遞給一個 prop 特性的時候,它就變成了那個組件實例的一個屬性。爲了給博文組件傳遞一個標題,咱們能夠用一個 props
選項將其包含在該組件可接受的 prop 列表中:
Vue.component('blog-post', { |
一個組件默承認以擁有任意數量的 prop,任何值均可以傳遞給任何 prop。在上述模板中,你會發現咱們可以在組件實例中訪問這個值,就像訪問 data
中的值同樣。
一個 prop 被註冊以後,你就能夠像這樣把數據做爲一個自定義特性傳遞進來:
<blog-post title="My journey with Vue"></blog-post> |
然而在一個典型的應用中,你可能在 data
裏有一個博文的數組:
new Vue({ |
並想要爲每篇博文渲染一個組件:
<blog-post |
如上所示,你會發現咱們可使用 v-bind
來動態傳遞 prop。這在你一開始不清楚要渲染的具體內容,好比從一個 API 獲取博文列表的時候,是很是有用的。
到目前爲止,關於 prop 你須要瞭解的大概就這些了,若是你閱讀完本頁內容並掌握了它的內容,咱們會推薦你再回來把 prop 讀完。
當構建一個 <blog-post>
組件時,你的模板最終會包含的東西遠不止一個標題:
<h3>{{ title }}</h3> |
最最起碼,你會包含這篇博文的正文:
<h3>{{ title }}</h3> |
然而若是你在模板中嘗試這樣寫,Vue 會顯示一個錯誤,並解釋道 every component must have a single root element (每一個組件必須只有一個根元素)。你能夠將模板的內容包裹在一個父元素內,來修復這個問題,例如:
<div class="blog-post"> |
看起來當組件變得愈來愈複雜的時候,咱們的博文不僅須要標題和內容,還須要發佈日期、評論等等。爲每一個相關的信息定義一個 prop 會變得很麻煩:
<blog-post |
因此是時候重構一下這個 <blog-post>
組件了,讓它變成接受一個單獨的 post
prop:
<blog-post |
Vue.component('blog-post', { |
上述的這個和一些接下來的示例使用了 JavaScript 的模板字符串來讓多行的模板更易讀。它們在 IE 下並無被支持,因此若是你須要在不 (通過 Babel 或 TypeScript 之類的工具) 編譯的狀況下支持 IE,請使用折行轉義字符取而代之。
如今,不論什麼時候爲 post
對象添加一個新的屬性,它都會自動地在 <blog-post>
內可用。
在咱們開發 <blog-post>
組件時,它的一些功能可能要求咱們和父級組件進行溝通。例如咱們可能會引入一個可訪問性的功能來放大博文的字號,同時讓頁面的其它部分保持默認的字號。
在其父組件中,咱們能夠經過添加一個 postFontSize
數據屬性來支持這個功能:
new Vue({ |
它能夠在模板中用來控制全部博文的字號:
<div id="blog-posts-events-demo"> |
如今咱們在每篇博文正文以前添加一個按鈕來放大字號:
Vue.component('blog-post', { |
問題是這個按鈕不會作任何事:
<button> |
當點擊這個按鈕時,咱們須要告訴父級組件放大全部博文的文本。幸虧 Vue 實例提供了一個自定義事件的系統來解決這個問題。咱們能夠調用內建的 $emit
方法並傳入事件的名字,來向父級組件觸發一個事件:
<button v-on:click="$emit('enlarge-text')"> |
而後咱們能夠用 v-on
在博文組件上監聽這個事件,就像監聽一個原生 DOM 事件同樣:
<blog-post |
有的時候用一個事件來拋出一個特定的值是很是有用的。例如咱們可能想讓 <blog-post>
組件決定它的文本要放大多少。這時可使用 $emit
的第二個參數來提供這個值:
<button v-on:click="$emit('enlarge-text', 0.1)"> |
而後當在父級組件監聽這個事件的時候,咱們能夠經過 $event
訪問到被拋出的這個值:
<blog-post |
或者,若是這個事件處理函數是一個方法:
<blog-post |
那麼這個值將會做爲第一個參數傳入這個方法:
methods: { |
v-model
自定義事件也能夠用於建立支持 v-model
的自定義輸入組件。記住:
<input v-model="searchText"> |
等價於:
<input |
當用在組件上時,v-model
則會這樣:
<custom-input |
爲了讓它正常工做,這個組件內的 <input>
必須:
value
特性綁定到一個名叫 value
的 prop 上input
事件被觸發時,將新的值經過自定義的 input
事件拋出寫成代碼以後是這樣的:
Vue.component('custom-input', { |
如今 v-model
就應該能夠在這個組件上完美地工做起來了:
<custom-input v-model="searchText"></custom-input> |
到目前爲止,關於組件自定義事件你須要瞭解的大概就這些了,若是你閱讀完本頁內容並掌握了它的內容,咱們會推薦你再回來把自定義事件讀完。
和 HTML 元素同樣,咱們常常須要向一個組件傳遞內容,像這樣:
<alert-box> |
可能會渲染出這樣的東西:
幸虧,Vue 自定義的 <slot>
元素讓這變得很是簡單:
Vue.component('alert-box', { |
如你所見,咱們只要在須要的地方加入插槽就好了——就這麼簡單!
到目前爲止,關於插槽你須要瞭解的大概就這些了,若是你閱讀完本頁內容並掌握了它的內容,咱們會推薦你再回來把插槽讀完。
有的時候,在不一樣組件之間進行動態切換是很是有用的,好比在一個多標籤的界面裏:
上述內容能夠經過 Vue 的 <component>
元素加一個特殊的 is
特性來實現:
<!-- 組件會在 `currentTabComponent` 改變時改變 --> |
在上述示例中,currentTabComponent
能夠包括
你能夠在這裏查閱並體驗完整的代碼,或在這個版本瞭解綁定組件選項對象,而不是已註冊組件名的示例。
到目前爲止,關於動態組件你須要瞭解的大概就這些了,若是你閱讀完本頁內容並掌握了它的內容,咱們會推薦你再回來把動態和異步組件讀完。
有些 HTML 元素,諸如 <ul>
、<ol>
、<table>
和 <select>
,對於哪些元素能夠出如今其內部是有嚴格限制的。而有些元素,諸如 <li>
、<tr>
和 <option>
,只能出如今其它某些特定的元素內部。
這會致使咱們使用這些有約束條件的元素時遇到一些問題。例如:
<table> |
這個自定義組件 <blog-post-row>
會被做爲無效的內容提高到外部,並致使最終渲染結果出錯。幸虧這個特殊的 is
特性給了咱們一個變通的辦法:
<table> |
須要注意的是若是咱們從如下來源使用模板的話,這條限制是不存在的:
template: '...'
).vue
)<script type="text/x-template">
到這裏,你須要瞭解的解析 DOM 模板時的注意事項——實際上也是 Vue 的所有必要內容,大概就是這些了。恭喜你!接下來還有不少東西要去學習,不過首先,咱們推薦你先休息一下,試用一下 Vue,本身隨意作些好玩的東西。
若是你感受已經掌握了這些知識,咱們推薦你再回來把完整的組件指南,包括側邊欄中組件深刻章節的全部頁面讀完。
該頁面假設你已經閱讀過了組件基礎。若是你還對組件不太瞭解,推薦你先閱讀它。
在註冊一個組件的時候,咱們始終須要給它一個名字。好比在全局註冊的時候咱們已經看到了:
Vue.component('my-component-name', { /* ... */ }) |
該組件名就是 Vue.component
的第一個參數。
你給予組件的名字可能依賴於你打算拿它來作什麼。當直接在 DOM 中使用一個組件 (而不是在字符串模板或單文件組件) 的時候,咱們強烈推薦遵循 W3C 規範中的自定義組件名 (字母全小寫且必須包含一個連字符)。這會幫助你避免和當前以及將來的 HTML 元素相沖突。
你能夠在風格指南中查閱到關於組件名的其它建議。
定義組件名的方式有兩種:
Vue.component('my-component-name', { /* ... */ }) |
當使用 kebab-case (短橫線分隔命名) 定義一個組件時,你也必須在引用這個自定義元素時使用 kebab-case,例如 <my-component-name>
。
Vue.component('MyComponentName', { /* ... */ }) |
當使用 PascalCase (首字母大寫命名) 定義一個組件時,你在引用這個自定義元素時兩種命名法均可以使用。也就是說 <my-component-name>
和 <MyComponentName>
都是可接受的。注意,儘管如此,直接在 DOM (即非字符串的模板) 中使用時只有 kebab-case 是有效的。
到目前爲止,咱們只用過 Vue.component
來建立組件:
Vue.component('my-component-name', { |
這些組件是全局註冊的。也就是說它們在註冊以後能夠用在任何新建立的 Vue 根實例 (new Vue
) 的模板中。好比:
Vue.component('component-a', { /* ... */ }) |
<div id="app"> |
在全部子組件中也是如此,也就是說這三個組件在各自內部也均可以相互使用。
全局註冊每每是不夠理想的。好比,若是你使用一個像 webpack 這樣的構建系統,全局註冊全部的組件意味着即使你已經再也不使用一個組件了,它仍然會被包含在你最終的構建結果中。這形成了用戶下載的 JavaScript 的無謂的增長。
在這些狀況下,你能夠經過一個普通的 JavaScript 對象來定義組件:
var ComponentA = { /* ... */ } |
而後在 components
選項中定義你想要使用的組件:
new Vue({ |
對於 components
對象中的每一個屬性來講,其屬性名就是自定義元素的名字,其屬性值就是這個組件的選項對象。
注意局部註冊的組件在其子組件中不可用。例如,若是你但願 ComponentA
在 ComponentB
中可用,則你須要這樣寫:
var ComponentA = { /* ... */ } |
或者若是你經過 Babel 和 webpack 使用 ES2015 模塊,那麼代碼看起來更像:
import ComponentA from './ComponentA.vue' |
注意在 ES2015+ 中,在對象中放一個相似 ComponentA
的變量名實際上是 ComponentA: ComponentA
的縮寫,即這個變量名同時是:
若是你沒有經過 import
/require
使用一個模塊系統,也許能夠暫且跳過這個章節。若是你使用了,那麼咱們會爲你提供一些特殊的使用說明和注意事項。
若是你還在閱讀,說明你使用了諸如 Babel 和 webpack 的模塊系統。在這些狀況下,咱們推薦建立一個 components
目錄,並將每一個組件放置在其各自的文件中。
而後你須要在局部註冊以前導入每一個你想使用的組件。例如,在一個假設的 ComponentB.js
或 ComponentB.vue
文件中:
import ComponentA from './ComponentA' |
如今 ComponentA
和 ComponentC
均可以在 ComponentB
的模板中使用了。
可能你的許多組件只是包裹了一個輸入框或按鈕之類的元素,是相對通用的。咱們有時候會把它們稱爲基礎組件,它們會在各個組件中被頻繁的用到。
因此會致使不少組件裏都會有一個包含基礎組件的長列表:
import BaseButton from './BaseButton.vue' |
而只是用於模板中的一小部分:
<BaseInput |
幸虧若是你使用了 webpack (或在內部使用了 webpack 的 Vue CLI 3+),那麼就可使用 require.context
只全局註冊這些很是通用的基礎組件。這裏有一份可讓你在應用入口文件 (好比 src/main.js
) 中全局導入基礎組件的示例代碼:
import Vue from 'vue' |
記住全局註冊的行爲必須在根 Vue 實例 (經過 new Vue
) 建立以前發生。這裏有一個真實項目情景下的示例。
該頁面假設你已經閱讀過了組件基礎。若是你還對組件不太瞭解,推薦你先閱讀它。
HTML 中的特性名是大小寫不敏感的,因此瀏覽器會把全部大寫字符解釋爲小寫字符。這意味着當你使用 DOM 中的模板時,camelCase (駝峯命名法) 的 prop 名須要使用其等價的 kebab-case (短橫線分隔命名) 命名:
Vue.component('blog-post', { |
<!-- 在 HTML 中是 kebab-case 的 --> |
重申一次,若是你使用字符串模板,那麼這個限制就不存在了。
到這裏,咱們只看到了以字符串數組形式列出的 prop:
props: ['title', 'likes', 'isPublished', 'commentIds', 'author'] |
可是,一般你但願每一個 prop 都有指定的值類型。這時,你能夠以對象形式列出 prop,這些屬性的名稱和值分別是 prop 各自的名稱和類型:
props: { |
這不只爲你的組件提供了文檔,還會在它們遇到錯誤的類型時從瀏覽器的 JavaScript 控制檯提示用戶。你會在這個頁面接下來的部分看到類型檢查和其它 prop 驗證。
像這樣,你已經知道了能夠像這樣給 prop 傳入一個靜態的值:
<blog-post title="My journey with Vue"></blog-post> |
你也知道 prop 能夠經過 v-bind
動態賦值,例如:
<!-- 動態賦予一個變量的值 --> |
在上述兩個示例中,咱們傳入的值都是字符串類型的,但實際上任何類型的值均可以傳給一個 prop。
<!-- 即使 `42` 是靜態的,咱們仍然須要 `v-bind` 來告訴 Vue --> |
<!-- 包含該 prop 沒有值的狀況在內,都意味着 `true`。--> |
<!-- 即使數組是靜態的,咱們仍然須要 `v-bind` 來告訴 Vue --> |
<!-- 即使對象是靜態的,咱們仍然須要 `v-bind` 來告訴 Vue --> |
若是你想要將一個對象的全部屬性都做爲 prop 傳入,你可使用不帶參數的 v-bind
(取代 v-bind:prop-name
)。例如,對於一個給定的對象 post
:
post: { |
下面的模板:
<blog-post v-bind="post"></blog-post> |
等價於:
<blog-post |
全部的 prop 都使得其父子 prop 之間造成了一個單向下行綁定:父級 prop 的更新會向下流動到子組件中,可是反過來則不行。這樣會防止從子組件意外改變父級組件的狀態,從而致使你的應用的數據流向難以理解。
額外的,每次父級組件發生更新時,子組件中全部的 prop 都將會刷新爲最新的值。這意味着你不該該在一個子組件內部改變 prop。若是你這樣作了,Vue 會在瀏覽器的控制檯中發出警告。
這裏有兩種常見的試圖改變一個 prop 的情形:
這個 prop 用來傳遞一個初始值;這個子組件接下來但願將其做爲一個本地的 prop 數據來使用。在這種狀況下,最好定義一個本地的 data 屬性並將這個 prop 用做其初始值:
props: ['initialCounter'], |
這個 prop 以一種原始的值傳入且須要進行轉換。在這種狀況下,最好使用這個 prop 的值來定義一個計算屬性:
props: ['size'], |
注意在 JavaScript 中對象和數組是經過引用傳入的,因此對於一個數組或對象類型的 prop 來講,在子組件中改變這個對象或數組自己將會影響到父組件的狀態。
咱們能夠爲組件的 prop 指定驗證要求,例如你知道的這些類型。若是有一個需求沒有被知足,則 Vue 會在瀏覽器控制檯中警告你。這在開發一個會被別人用到的組件時尤爲有幫助。
爲了定製 prop 的驗證方式,你能夠爲 props
中的值提供一個帶有驗證需求的對象,而不是一個字符串數組。例如:
Vue.component('my-component', { |
當 prop 驗證失敗的時候,(開發環境構建版本的) Vue 將會產生一個控制檯的警告。
注意那些 prop 會在一個組件實例建立以前進行驗證,因此實例的屬性 (如 data
、computed
等) 在 default
或 validator
函數中是不可用的。
type
能夠是下列原生構造函數中的一個:
String
Number
Boolean
Array
Object
Date
Function
Symbol
額外的,type
還能夠是一個自定義的構造函數,而且經過 instanceof
來進行檢查確認。例如,給定下列現成的構造函數:
function Person (firstName, lastName) { |
你可使用:
Vue.component('blog-post', { |
來驗證 author
prop 的值是不是經過 new Person
建立的。
一個非 prop 特性是指傳向一個組件,可是該組件並無相應 prop 定義的特性。
由於顯式定義的 prop 適用於向一個子組件傳入信息,然而組件庫的做者並不總能預見組件會被用於怎樣的場景。這也是爲何組件能夠接受任意的特性,而這些特性會被添加到這個組件的根元素上。
例如,想象一下你經過一個 Bootstrap 插件使用了一個第三方的 <bootstrap-date-input>
組件,這個插件須要在其 <input>
上用到一個 data-date-picker
特性。咱們能夠將這個特性添加到你的組件實例上:
<bootstrap-date-input data-date-picker="activated"></bootstrap-date-input> |
而後這個 data-date-picker="activated"
特性就會自動添加到 <bootstrap-date-input>
的根元素上。
想象一下 <bootstrap-date-input>
的模板是這樣的:
<input type="date" class="form-control"> |
爲了給咱們的日期選擇器插件定製一個主題,咱們可能須要像這樣添加一個特別的類名:
<bootstrap-date-input |
在這種狀況下,咱們定義了兩個不一樣的 class
的值:
form-control
,這是在組件的模板內設置好的date-picker-theme-dark
,這是從組件的父級傳入的對於絕大多數特性來講,從外部提供給組件的值會替換掉組件內部設置好的值。因此若是傳入 type="text"
就會替換掉 type="date"
並把它破壞!慶幸的是,class
和 style
特性會稍微智能一些,即兩邊的值會被合併起來,從而獲得最終的值:form-control date-picker-theme-dark
。
若是你不但願組件的根元素繼承特性,你能夠在組件的選項中設置 inheritAttrs: false
。例如:
Vue.component('my-component', { |
這尤爲適合配合實例的 $attrs
屬性使用,該屬性包含了傳遞給一個組件的特性名和特性值,例如:
{ |
有了 inheritAttrs: false
和 $attrs
,你就能夠手動決定這些特性會被賦予哪一個元素。在撰寫基礎組件的時候是常會用到的:
Vue.component('base-input', { |
這個模式容許你在使用基礎組件的時候更像是使用原始的 HTML 元素,而不會擔憂哪一個元素是真正的根元素:
<base-input |