vue 條件渲染與列表渲染
本文是對官方文檔的整理
因為 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-if
的“else 塊”:
v-else
元素必須緊跟在帶 v-if
或者 v-else-if
的元素的後面(同理v-else-if必須跟在v-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>
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>
現在,每次切換時,輸入框都將被重新渲染。
v-if
vs v-show
v-if
是“真正”的條件渲染,因為它會確保在切換過程中條件塊內的事件監聽器和子組件適當地被銷毀和重建。
v-if
也是惰性的:如果在初始渲染時條件為假,則什麽也不做——直到條件第一次變為真時,才會開始渲染條件塊。
相比之下,v-show
就簡單得多——不管初始條件是什麽,元素總是會被渲染,並且只是簡單地基於 CSS(屬性 display) 進行切換。
v-show
不支持 <template>
元素,也不支持 v-else
。
一般來說,v-if
有更高的切換開銷,而 v-show
有更高的初始渲染開銷。因此,如果需要非常頻繁地切換,則使用 v-show
較好;如果在運行時條件很少改變,則使用 v-if
較好。
V-for
V-for不僅可以遍歷數組也可以遍歷對象
遍歷數組時,第一個參數為數組元素,第二個參數為索引
1 <ul id="example-2"> 2 <li v-for="(item, index) in items"> 3 {{ parentMessage }} - {{ index }} - {{ item.message }} 4 </li> 5 </ul> 6 7 var example2 = new Vue({ 8 el: ‘#example-2‘, 9 data: { 10 parentMessage: ‘Parent‘, 11 items: [ 12 { message: ‘Foo‘ }, 13 { message: ‘Bar‘ } 14 ] 15 } 16 })View Code
也可以用 of
替代 in
作為分隔符, JavaScript 叠代器的語法,item為數組元素
1 <div v-for="item of items"></div>
遍歷對象時,參數分別為 值、鍵名,索引
1 <div v-for="(value, key, index) in object"> 2 {{ index }}. {{ key }}: {{ value }} 3 </div> 4 5 new Vue({ 6 el: ‘#v-for-object‘, 7 data: { 8 object: { 9 firstName: ‘John‘, 10 lastName: ‘Doe‘, 11 age: 30 12 } 13 } 14 })View Code
在遍歷對象時,是按 Object.keys()
的結果遍歷,但是不能保證它的結果在不同的 JavaScript 引擎下是一致的。
v-for
正在更新已渲染過的元素列表時,它默認用“就地復用”策略。如果數據項的順序被改變,Vue 將不會移動 DOM 元素來匹配數據項的順序, 而是簡單復用此處每個元素,並且確保它在特定索引下顯示已被渲染過的每個元素。這個默認的模式是高效的,但是只適用於不依賴子組件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出。
建議盡可能在使用 v-for
時提供 key
,除非遍歷輸出的 DOM 內容非常簡單,或者是刻意依賴默認行為以獲取性能上的提升。因為它是 Vue 識別節點的一個通用機制,key
並不與 v-for
特別關聯
1 <div v-for="item in items" :key="item.id"> 2 <!-- 內容 --> 3 </div>View Code
一段取值範圍的 v-for
v-for
也可以取整數。在這種情況下,它將重復多次模板。
<div> <span v-for="n in 10">{{ n }} </span> </div>
v-for
on a <template>
<ul> <template v-for="item in items"> <li>{{ item.msg }}</li> <li class="divider"></li> </template> </ul>
v-if
與 v-for
一起使用
當 v-if
與 v-for
一起使用時,v-for
具有比 v-if
更高的優先級。這意味著 v-if
將分別重復運行於每個 v-for
循環中。當你想為僅有的一些項渲染節點時,這種優先級的機制會十分有用,如下:
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
上面的代碼只傳遞了未 complete 的 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-for
在自定義組件裏,你可以像任何普通元素一樣用 v-for
。
<my-component v-for="item in items" :key="item.id"></my-component>
註:2.2.0+ 的版本裏,當在組件中使用 v-for
時,key
現在是必須的。
然而,任何數據都不會被自動傳遞到組件裏,因為組件有自己獨立的作用域。為了把叠代數據傳遞到組件裏,我們要用 props
:
<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>
相同,但是可以避開一些潛在的瀏覽器解析錯誤。查看 DOM 模板解析說明 來了解更多信息。
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 = ‘‘ } } })
數組變異方法
Vue 包含一組觀察數組的變異方法,所以它們也將會觸發視圖更新。這些方法如下:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
如: example1.items.push({ message: ‘Baz‘ })
變異方法 (mutation method),顧名思義,會改變被這些方法調用的原始數組。相比之下,也有非變異 (non-mutating method) 方法,例如:filter()
, concat()
和 slice()
。這些不會改變原始數組,但總是返回一個新數組。當使用非變異方法時,可以用新數組替換舊數組:
1 example1.items = example1.items.filter(function (item) { 2 return item.message.match(/Foo/) 3 })
數組更改註意事項
由於 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)
方法向嵌套對象添加響應式屬性。例如,對於:
var vm = new Vue({ data: { userProfile: { name: ‘Anika‘ } } })
你可以添加一個新的 age
屬性到嵌套的 userProfile
對象:
Vue.set(vm.userProfile, ‘age‘, 27)
你還可以使用 vm.$set
實例方法,它只是全局 Vue.set
的別名:
this.$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 }) } }
vue 條件渲染與列表渲染