一.插值 v-once 通過使用 v-once 指令,你也能執行一次性地插值,當數據改變時,插值處的內容不會更新。但請留心這會影響到該節點上所有的數據綁定: v-html 雙大括弧會將數據解釋為普通文本,而非 HTML 代碼。為了輸出真正的 HTML,你需要使用 v-html 指令: 這個 span ...
一.插值
v-once
通過使用 v-once
指令,你也能執行一次性地插值,當數據改變時,插值處的內容不會更新。但請留心這會影響到該節點上所有的數據綁定:
span v-once>這個將不會改變: {{ msg }}</span>
v-html
雙大括弧會將數據解釋為普通文本,而非 HTML 代碼。為了輸出真正的 HTML,你需要使用 v-html 指令:
<p>Using mustaches: {{ rawHtml }}</p> <p>Using v-html directive: <span v-html="rawHtml"></span></p>
這個 span 的內容將會被替換成為屬性值 rawHtml,直接作為 HTML——會忽略解析屬性值中的數據綁定。
你的站點上動態渲染的任意 HTML 可能會非常危險,因為它很容易導致
XSS
攻擊。請只對可信內容使用 HTML 插值,絕不要對用戶提供的內容使用插值。
使用 JavaScript 表達式
Vue.js 都提供了完全的 JavaScript 表達式支持。
{{ number + 1 }} {{ ok ? 'YES' : 'NO' }} {{ message.split('').reverse().join('') }} <div v-bind:id="'list-' + id"></div>
這些表達式會在所屬 Vue 實例的數據作用域下作為 JavaScript 被解析。有個限制就是,每個綁定都只能包含單個表達式
,所以下麵的例子都不會生效
。
<!-- 這是語句,不是表達式 --> {{ var a = 1 }} <!-- 流控制也不會生效,請使用三元表達式 --> {{ if (ok) { return message } }}
二.v-bind與v-on的縮寫
v-bind 縮寫
<!-- 完整語法 --> <a v-bind:href="url"></a> <!-- 縮寫 --> <a :href="url"></a> <!-- 完整語法 --> <button v-bind:disabled="isButtonDisabled">Button</button> <!-- 縮寫 --> <button :disabled="isButtonDisabled">Button</button>
說明下:如果
isButtonDisabled
的值是null
、undefined
或false
,則disabled
特性甚至不會被包含在渲染出來的<button>
元素中。
v-on 縮寫
<!-- 完整語法 --> <a v-on:click="doSomething">...</a> <!-- 縮寫 --> <a @click="doSomething">...</a>
三.條件渲染
v-if
<h1 v-if="ok">Yes</h1>
在 <template> 元素上使用 v-if
<template v-if="ok"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </template>
v-else
你可以使用 v-else 指令來表示 v-if 的“else 塊”:
<div v-if="Math.random() > 0.5"> Now you see me </div> <div v-else> Now you don't </div>
v-else 元素必須緊跟在帶 v-if 或者 v-else-if 的元素的後面,否則它將不會被識別。
v-else-if
<div v-if="type === 'A'"> A </div> <div v-else-if="type === 'B'"> B </div> <div v-else-if="type === 'C'"> C </div> <div v-else> Not A/B/C </div>
用 key 管理可復用的元素
Vue 會儘可能高效地渲染元素,通常會復用已有元素而不是從頭開始渲染。這麼做除了使 Vue 變得非常快之外,還有其它一些好處。例如,如果你允許用戶在不同的登錄方式之間切換:
<template v-if="loginType === 'username'"> <label>Username</label> <input placeholder="Enter your username"> </template> <template v-else> <label>Email</label> <input placeholder="Enter your email address"> </template>
那麼在上面的代碼中切換 loginType
將不會清除用戶已經輸入的內容。因為兩個模板使用了相同的元素,<input>
不會被替換掉——僅僅是替換了它的 placeholder
。
這樣也不總是符合實際需求,所以 Vue 為你提供了一種方式來表達“這兩個元素是完全獨立的,不要復用它們
”。只需添加一個具有唯一值的 key
屬性即可:
<template v-if="loginType === 'username'"> <label>Username</label> <input placeholder="Enter your username" key="username-input"> </template> <template v-else> <label>Email</label> <input placeholder="Enter your email address" key="email-input"> </template>
現在,每次切換時,輸入框都將被重新渲染
。註意,<label>
元素仍然會被高效地復用,因為它們沒有添加 key
屬性。
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-show
有更高的初始渲染開銷。因此,如果需要非常頻繁地切換,則使用 v-show
較好;如果在運行時條件很少改變,則使用 v-if
較好。
v-if 與 v-for 一起使用
當 v-if
與 v-for
一起使用時,v-for 具有比 v-if 更高的優先順序
。
四.列表渲染v-for
一個數組的v-for
<ul id="example-1"> <li v-for="item in items"> {{ item.message }} </li> </ul> var example1 = new Vue({ el: '#example-1', data: { items: [ { message: 'Foo' }, { message: 'Bar' } ] } })
在 v-for 塊中,我們擁有對父作用域屬性的完全訪問許可權。v-for
還支持一個可選的第二個參數為當前項的索引。
<ul id="example-2"> <li v-for="(item, index) in items"> {{ parentMessage }} - {{ index }} - {{ item.message }} </li> </ul> var example2 = new Vue({ el: '#example-2', data: { parentMessage: 'Parent', items: [ { message: 'Foo' }, { message: 'Bar' } ] } })
結果:
Parent - 0 - Foo
Parent - 1 - Bar
你也可以用 of 替代 in 作為分隔符,因為它是最接近 JavaScript 迭代器的語法:
<div v-for="item of items"></div>
一個對象的 v-for
<ul id="v-for-object" class="demo"> <li v-for="value in object"> {{ value }} </li> </ul> new Vue({ el: '#v-for-object', data: { object: { firstName: 'John', lastName: 'Doe', age: 30 } } })
結果:
John
Doe
30
你也可以提供第二個的參數為鍵名:
<div v-for="(value, key) in object"> {{ key }}: {{ value }} </div>
結果:
firstName: John
lastName: Doe
age: 30
第三個參數為索引:
<div v-for="(value, key, index) in object"> {{ index }}. {{ key }}: {{ value }} </div>
在遍歷對象時,是按 Object.keys() 的結果遍歷,但是不能保證它的結果在不同的 JavaScript 引擎下是一致的。
key
為了給 Vue 一個提示,以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素,你需要為每項提供一個唯一 key 屬性。理想的 key 值是每項都有的且唯一的 id。
<div v-for="item in items" :key="item.id"> <!-- 內容 --> </div>
建議儘可能在使用 v-for 時提供 key,除非遍歷輸出的 DOM 內容非常簡單,或者是刻意依賴預設行為以獲取性能上的提升。
數組更新檢測
變異方法與替換數組
Vue.js 包裝了被觀察數組的變異方法,故它們能觸發視圖更新。被包裝的方法有: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) { return item.message.match(/Foo/) })
註意事項
由於 JavaScript 的限制,Vue 不能檢測以下變動的數組:
- 當你利用索引直接設置一個項時,例如:
vm.items[indexOfItem] = newValue
- 當你修改數組的長度時,例如:
vm.items.length = newLength
為瞭解決第一類問題,以下兩種方式都可以實現和 vm.items[indexOfItem] = newValue
相同的效果,同時也將觸髮狀態更新:
// Vue.set Vue.set(example1.items, indexOfItem, newValue)
// Array.prototype.splice example1.items.splice(indexOfItem, 1, newValue)
為瞭解決第二類問題,你可以使用 splice:
example1.items.splice(newLength)
對象更改檢測註意事項
還是由於 JavaScript 的限制,Vue 不能檢測對象屬性的添加或刪除:
var vm = new Vue({ data: { a: 1 } }) // `vm.a` 現在是響應式的 vm.b = 2 // `vm.b` 不是響應式的
對於已經創建的實例,Vue 不能動態添加根級別的響應式屬性。但是,可以使用 Vue.set(object, key, value)
方法向嵌套對象添加響應式屬性。例如,對於:
ar vm = new Vue({ data: { userProfile: { name: 'Anika' } } })
你可以添加一個新的 age
屬性到嵌套的 userProfile
對象:
Vue.set(vm.userProfile, 'age', 27)
你還可以使用 vm.$set
實例方法,它只是全局 Vue.set
的別名:
vm.$set(this.userProfile, 'age', 27)
有時你可能需要為已有對象賦予多個新屬性,比如使用 Object.assign()
或 _.extend()
。在這種情況下,你應該用兩個對象的屬性創建一個新的對象。所以,如果你想添加新的響應式屬性,不要像這樣:
Object.assign(this.userProfile, { age: 27, favoriteColor: 'Vue Green' })
你應該這樣做:
this.userProfile = Object.assign({}, this.userProfile, { age: 27, favoriteColor: 'Vue Green' })
顯示過濾/排序結果
有時,我們想要顯示一個數組的過濾或排序副本,而不實際改變或重置原始數據。在這種情況下,可以創建返回過濾或排序數組的計算屬性。
<li v-for="n in evenNumbers">{{ n }}</li> data: { numbers: [ 1, 2, 3, 4, 5 ] }, computed: { evenNumbers: function () { return this.numbers.filter(function (number) { return number % 2 === 0 }) } }
在計算屬性不適用的情況下 (例如,在嵌套 v-for 迴圈中) 你可以使用一個 method 方法:
<li v-for="n in even(numbers)">{{ n }}</li> data: { numbers: [ 1, 2, 3, 4, 5 ] }, methods: { even: function (numbers) { return numbers.filter(function (number) { return number % 2 === 0 }) } }
值域 v-for
<div> <span v-for="n in 10">{{ n }} </span> </div>
template-v-for
<ul> <template v-for="item in items"> <li>{{ item.msg }}</li> <li class="divider"></li> </template> </ul>
一個組件的 v-for
<my-component v-for="item in items" :key="item.id"></my-component>
2.2.0+ 的版本里,當在組件中使用
v-for
時,key
現在是必須的。
然而,任何數據都不會被自動傳遞到組件里,因為組件有自己獨立的作用域。為了把迭代數據傳遞到組件里,我們要用 props
:
<my-component v-for="(item, index) in items" v-bind:item="item" v-bind:index="index" v-bind:key="item.id" ></my-component>
不自動將 item 註入到組件里的原因是,這會使得組件與 v-for 的運作緊密耦合。明確組件數據的來源能夠使組件在其他場合重覆使用
<div id="todo-list-example"> <input v-model="newTodoText" v-on:keyup.enter="addNewTodo" placeholder="Add a todo" > <ul> <li is="todo-item" v-for="(todo, index) in todos" v-bind:key="todo.id" v-bind:title="todo.title" v-on:remove="todos.splice(index, 1)" ></li> </ul> </div>
註意這裡的
is="todo-item"
屬性。這種做法在使用 DOM 模板時是十分必要的,因為在 <ul> 元素內只有 <li> 元素會被看作有效內容。這樣做實現的效果與<todo-item>
相同,但是可以避開一些潛在的瀏覽器解析錯誤。
Vue.component('todo-item', { template: '\ <li>\ {{ title }}\ <button v-on:click="$emit(\'remove\')">X</button>\ </li>\ ', props: ['title'] }) new Vue({ el: '#todo-list-example', data: { newTodoText: '', todos: [ { id: 1, title: 'Do the dishes', }, { id: 2, title: 'Take out the trash', }, { id: 3, title: 'Mow the lawn' } ], nextTodoId: 4 }, methods: { addNewTodo: function () { this.todos.push({ id: this.nextTodoId++, title: this.newTodoText }) this.newTodoText = '' } } })
v-for 與 v-if
當它們處於同一節點,v-for 的優先順序比 v-if 更高,這意味著 v-if 將分別重覆運行於每個 v-for 迴圈中。當你想為僅有的一些項渲染節點時,這種優先順序的機制會十分有用,如下:
<li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo }} </li>
上面的代碼只傳遞了未完成的 todos。
而如果你的目的是有條件地跳過迴圈的執行,那麼可以將 v-if 置於外層元素 (或 <template>)上。如:
<ul v-if="todos.length"> <li v-for="todo in todos"> {{ todo }} </li> </ul> <p v-else>No todos left!</p>
五.事件處理
事件處理方法
<div id="example-2"> <!-- `greet` 是在下麵定義的方法名 --> <button v-on:click="greet">Greet</button> </div>
var example2 = new Vue({ el: '#example-2', data: { name: 'Vue.js' }, // 在 `methods` 對象中定義方法 methods: { greet: function (event) { // `this` 在方法里指向當前 Vue 實例 alert('Hello ' + this.name + '!') // `event` 是原生 DOM 事件 if (event) { alert(event.target.tagName) } } } }) // 也可以用 JavaScript 直接調用方法 example2.greet() // => 'Hello Vue.js!'
內聯處理器中的方法
<div id="example-3"> <button v-on:click="say('hi')">Say hi</button> <button v-on:click="say('what')">Say what</button> </div> new Vue({ el: '#example-3', methods: { say: function (message) { alert(message) } } })
有時也需要在內聯語句處理器中訪問原始的 DOM 事件。可以用特殊變數 $event
把它傳入方法:
<button v-on:click="warn('Form cannot be submitted yet.', $event)"> Submit </button> // ... methods: { warn: function (message, event) { // 現在我們可以訪問原生事件對象 if (event) event.preventDefault() alert(message) } }
事件修飾符
修飾符是由點開頭的指令尾碼來表示的。
- .stop
- .prevent
- .capture
- .self
- .once
<!-- 阻止單擊事件繼續傳播 --> <a v-on:click.stop="doThis"></a> <!-- 提交事件不再重載頁面 --> <form v-on:submit.prevent="onSubmit"></form> <!-- 修飾符可以串聯 --> <a v-on:click.stop.prevent="doThat"></a> <!-- 只有修飾符 --> <form v-on:submit.prevent></form> <!-- 添加事件監聽器時使用事件捕獲模式 --> <!-- 即元素自身觸發的事件先在此處處理,然後才交由內部元素進行處理 --> <div v-on:click.capture="doThis">...</div> <!-- 只當在 event.target 是當前元素自身時觸發處理函數 --> <!-- 即事件不是從內部元素觸發的 --> <div v-on:click.self="doThat">...</div>
使用修飾符時,順序很重要;相應的代碼會以同樣的順序產生。因此,用
@click.prevent.self
會阻止所有的點擊,而@click.self.prevent
只會阻止對元素自身的點擊。
<!-- 點擊事件將只會觸發一次 2.1.4 新增--> <a v-on:click.once="doThis"></a>
不像其它只能對原生的 DOM 事件起作用的修飾符,.once 修飾符還能被用到自定義的組件事件上。
<!-- the scroll event will not cancel the default scroll behavior 2.3.0 新增 --> <div v-on:scroll.passive="onScroll">...</div>
Vue 為這些修飾符額外提供了 .passive
修飾符來提升移動端的性能。舉個例子,在滾動的時候,瀏覽器會在整個事件處理完畢之後再觸發滾動,因為瀏覽器並不知道這個事件是否在其處理函數中被調用了 event.preventDefault()
。.passive
修飾符用來進一步告訴瀏覽器這個事件的預設行為不會被取消。
不要把 .passive 和 .prevent 一起使用。被動處理函數無法阻止預設的事件行為。
按鍵修飾符
<!-- 只有在 keyCode 是 13 時調用 vm.submit() --> <input v-on:keyup.13="submit"> <!-- 同上 --> <input v-on:keyup.enter="submit"> <!-- 縮寫語法 --> <input @keyup.enter="submit">
全部的按鍵別名:
- .enter
- .tab
- .delete (捕獲“刪除”和“退格”鍵)
- .esc
- .space
- .up
- .down
- .left
- .right
可以通過全局 config.keyCodes
對象自定義按鍵修飾符別名:
// 可以使用 `v-on:keyup.f1` Vue.config.keyCodes.f1 = 112
自動匹配按鍵修飾符(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 --> <input @keyup.alt.67="clear"> <!-- Ctrl + Click --> <div @click.ctrl="doSomething">Do something</div>
請註意修飾鍵與常規按鍵不同,在和
keyup
事件一起用時,事件觸發時修飾鍵必須處於按下狀態。換句話說,只有在按住ctrl
的情況下釋放其它按鍵,才能觸發keyup.ctrl
。而單單釋放ctrl
也不會觸發事件。
.exact 修飾符(2.5.0 新增)
.exact 修飾符允許你控制由精確的系統修飾符組合觸發的事件。
<!-- 即使 Alt 或 Shift 被一同按下時也會觸發 --> <button @click.ctrl="onClick">A</button> <!-- 有且只有 Ctrl 被按下的時候才觸發 --> <button @click.ctrl.exact="onCtrlClick">A</button> <!-- 沒有任何系統修飾符被按下的時候才觸發 --> <button @click.exact="onClick">A</button>
滑鼠按鈕修飾符(2.1.0 新增)
- .left
- .right
- .middle
這些修飾符會限制處理函數僅響應特定的滑鼠按鈕。
六.表單輸入綁定v-model
你可以用 v-model
指令在表單 <input>
及 <textarea>
元素上創建雙向數據綁定。
v-model
會忽略所有表單元素的 value
、checked
、selected
特性的初始值而總是將 Vue 實例的數據作為數據來源。你應該通過 JavaScript 在組件的 data
選項中聲明初始值。
對於需要使用
輸入法
(如中文、日文、韓文等) 的語言,你會發現v-model
不會在輸入法組合文字過程中得到更新。如果你也想處理這個過程,請使用input
事件。
文本
<input v-model="message" placeholder="edit me"> <p>Message is: {{ message }}</p>
多行文本
<span>Multiline message is:</span> <p style="white-space: pre-line;">{{ message }}</p> <br> <textarea v-model="message" placeholder="add multiple lines"></textarea>
在文本區域插值 (
<textarea></textarea>
) 並不會生效,應用v-model
來代替。
覆選框
單個覆選框,綁定到布爾值:
<input type="checkbox" id="checkbox" v-model="checked"> <label for="checkbox">{{ checked }}</label>
多個覆選框,綁定到同一個數組:
<div id='example-3'> <input type="checkbox" id="jack" value="Jack" v-model="checkedNames"> <label for="jack">Jack</label> <input type="checkbox" id="john" value="John" v-model="checkedNames"> <label for="john">John</label> <input type="checkbox" id="mike" value="Mike" v-model="checkedNames"> <label for="mike">Mike</label> <br> <span>Checked names: {{ checkedNames }}</span> </div>
new Vue({ el: '#example-3', data: { checkedNames: [] } })
看下麵一個例子:
<input type="checkbox" v-model="toggle" true-value="yes" false-value="no" >
// 當選中時 vm.toggle === 'yes' // 當沒有選中時 vm.toggle === 'no'
這裡的
true-value
和false-value
特性並不會影響輸入控制項的value
特性,因為瀏覽器在提交表單時並不會包含未被選中的覆選框。如果要確保表單中這兩個值中的一個能夠被提交,(比如“yes”或“no”),請換用單選按鈕。
單選按鈕
<div id="example-4"> <input type="radio" id="one" value="One" v-model="picked"> <label for="one"