條件渲染
有時候我們要根據數據的情況,決定標簽是否進行顯示或者有其他動作。最常見的就是,表格渲染的時候,如果表格沒有數據,就顯示無數據。如果有數據就顯示表格數據。 Vue 幫我們提供了一個v-if
的指令,幫助我們完成判斷的模板處理。
<div id="app"> <h1 v-if="ok">Yes</h1> <h1 v-else>No</h1> </div> <!-- 當ok為true的時候,輸出: Yes, 否則輸出: No --> <script> var app = new Vue({ el: '#app', data: { ok: true // true,返回:Yes, false=> No } }); </script>
v-if
指令可以根據數據綁定的情況進行插入標簽或者移除標簽。 當然,如果熟悉 js 的都清楚,有 if,肯定會有 else。 Vue 提供的是 v-else
指令。
v-if
在 Vue 中,我們使用 v-if
指令實現同樣的功能:
<h1 v-if="ok">Yes</h1>
也可以用 v-else
添加一個“else 塊”:
<h1 v-if="ok">Yes</h1> <h1 v-else>No</h1>
在 <template>
元素上使用 v-if
條件渲染分組
因為 v-if
是一個指令,所以必須將它添加到一個元素上。但是如果想切換多個元素呢?此時可以把一個 <template>
元素當做不可見的包裹元素,並在上面使用 v-if
。最終的渲染結果將不包含 <template>
元素。
<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
2.1.0 新增
v-else-if
,顧名思義,充當 v-if
的“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>
類似於 v-else
,v-else-if
也必須緊跟在帶 v-if
或者 v-else-if
的元素之后。
用 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
。
自己動手試一試,在輸入框中輸入一些文本,然后按下切換按鈕:
<div id="no-key-example" class="demo"> <div> <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> </div> <button @click="toggleLoginType">Toggle login type</button> </div> <script> new Vue({ el: '#no-key-example', data: { loginType: 'username' }, methods: { toggleLoginType: function () { return this.loginType = this.loginType === 'username' ? 'email' : 'username' } } }) </script>
這樣也不總是符合實際需求,所以 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>
現在,每次切換時,輸入框都將被重新渲染。請看:
<div id="key-example" class="demo"> <div> <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> </div> <button @click="toggleLoginType">Toggle login type</button> </div> <script> new Vue({ el: '#key-example', data: { loginType: 'username' }, methods: { toggleLoginType: function () { return this.loginType = this.loginType === 'username' ? 'email' : 'username' } } }) </script>
注意,<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-for 循環渲染標簽
模板引擎都會提供循環的支持。Vue 也不例外,Vue 是提供了一個v-for
指令。基本的用法類似於 foreach 的用法。還是看例子最直接,上代碼:
<div id="app"> <table> <thead> <tr> <th>姓名</th> <th>年齡</th> <th>地址</th> </tr> </thead> <tbody> <!-- 每次for循環,都會創建一個tr標簽。item是遍歷的元素。 --> <tr v-for="item in UserList" > <td>{{ item.name }}</td> <td>{{ item.age }}</td> <td>{{ item.address }}</td> </tr> </tbody> </table> </div> <script> var app = new Vue({ el: '#app', data: { UserList: [ {'name': 'malun', 'age': 18, 'address': '北京黑地下室'}, {'name': 'flydragon', 'age': 22, 'address': '廈門的很多熱的地方'}, {'name': 'temp', 'age': 25, 'address': '東北松花江上'} ] } }); </script>
用 v-for
把一個數組對應為一組元素
我們用 v-for
指令根據一組數組的選項列表進行渲染。v-for
指令需要使用 item in items
形式的特殊語法,items
是源數據數組並且 item
是數組元素迭代的別名。
<ul id="example-1" class="demo"> <li v-for="item in items"> {{item.message}} </li> </ul> <script> var example1 = new Vue({ el: '#example-1', data: { items: [ { message: 'Foo' }, { message: 'Bar' } ] }, watch: { items: function () { smoothScroll.animateScroll(document.querySelector('#example-1')) } } }) </script>
在 v-for
塊中,我們擁有對父作用域屬性的完全訪問權限。v-for
還支持一個可選的第二個參數為當前項的索引。
<ul id="example-2" class="demo"> <li v-for="(item, index) in items"> {{ parentMessage }} - {{ index }} - {{ item.message }} </li> </ul> <script> var example2 = new Vue({ el: '#example-2', data: { parentMessage: 'Parent', items: [ { message: 'Foo' }, { message: 'Bar' } ] }, watch: { items: function () { smoothScroll.animateScroll(document.querySelector('#example-2')) } } }) </script>
你也可以用 of
替代 in
作為分隔符,因為它是最接近 JavaScript 迭代器的語法:
<div v-for="item of items"></div>
一個對象的 v-for
你也可以用 v-for
通過一個對象的屬性來迭代。
<ul id="v-for-object" class="demo"> <li v-for="value in object"> {{ value }} </li> </ul> <script> new Vue({ el: '#v-for-object', data: { object: { firstName: 'John', lastName: 'Doe', age: 30 } } }) </script>
你也可以提供第二個的參數為鍵名:
<div id="v-for-object-value-key" class="demo"> <div v-for="(value, key) in object"> {{ key }}: {{ value }} </div> </div> <script> new Vue({ el: '#v-for-object-value-key', data: { object: { firstName: 'John', lastName: 'Doe', age: 30 } } }) </script>
第三個參數為索引:
<div id="v-for-object-value-key-index" class="demo"> <div v-for="(value, key, index) in object"> {{ index }}. {{ key }}: {{ value }} </div> </div> <script> new Vue({ el: '#v-for-object-value-key-index', data: { object: { firstName: 'John', lastName: 'Doe', age: 30 } } }) </script>
在遍歷對象時,是按 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"> <!-- 內容 --> </div>
建議盡可能在使用 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) {
return item.message.match(/Foo/)
})
你可能認為這將導致 Vue 丟棄現有 DOM 並重新渲染整個列表。幸運的是,事實並非如此。Vue 為了使得 DOM 元素得到最大范圍的重用而實現了一些智能的、啟發式的方法,所以用一個含有相同元素的數組去替換原來的數組是非常高效的操作。
注意事項
由於 JavaScript 的限制,Vue 不能檢測以下變動的數組:
- 當你利用索引直接設置一個項時,例如:
vm.items[indexOfItem] = newValue
- 當你修改數組的長度時,例如:
vm.items.length = newLength
舉個例子:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是響應性的
vm.items.length = 2 // 不是響應性的
為了解決第一類問題,以下兩種方式都可以實現和 vm.items[indexOfItem] = newValue
相同的效果,同時也將觸發狀態更新:
// Vue.set Vue.set(vm.items, indexOfItem, newValue) // Array.prototype.splice vm.items.splice(indexOfItem, 1, newValue)
你也可以使用 vm.$set
實例方法,該方法是全局方法 Vue.set
的一個別名:
vm.$set(vm.items, indexOfItem, newValue)
為了解決第二類問題,你可以使用 splice
:
vm.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)
方法向嵌套對象添加響應式屬性。例如,對於:
var vm = new Vue({ data: { userProfile: { name: 'Anika' } } })
你可以添加一個新的 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, { age: 27, favoriteColor: 'Vue Green' })
你應該這樣做:
vm.userProfile = Object.assign({}, vm.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
v-for
也可以取整數。在這種情況下,它將重復多次模板。
<div id="range" class="demo"> <span v-for="n in 10">{{ n }} </span> </div> <script> new Vue({ el: '#range' }) </script>
v-for
on a <template>
類似於 v-if
,你也可以利用帶有 v-for
的 <template>
渲染多個元素。比如:
<ul> <template v-for="item in items"> <li>{{ item.msg }}</li> <li class="divider" role="presentation"></li> </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"> {{ 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>
表格顯示的綜合案例
下面是一個綜合的案例,每秒鍾往表格中添加一條數據。 本案例綜合使用了 v-if 和 v-for 循環綜合案例。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入門之動態顯示表格</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> <table> <thead> <tr> <th>姓名</th> <th>年齡</th> <th>地址</th> </tr> </thead> <!-- 如果列表有數據,直接輸出表格數據,沒有數據提示用戶沒有數據 --> <tbody v-if="UserList.length > 0"> <tr v-for="item in UserList" > <td>{{ item.name }}</td> <td>{{ item.age }}</td> <td>{{ item.address }}</td> </tr> </tbody> <tbody v-else> <tr><td colspan="3">沒有數據奧!</td></tr> </tbody> </table> </div> <script> var app = new Vue({ el: '#app', data: { UserList: [] } }); // 每秒鍾插入一條數據。 setInterval(function () { app.UserList.push({'name': 'malun', 'age': 18, 'address': '北京黑地下室'}); }, 1000); </script> </body> </html>
總結列表和條件綁定
列表的使用其實本質還是 js 的衍生使用,對於有 js 開發基礎的沒有什么難度。關鍵是多寫幾個案例就會詳細通了。