# 列表渲染
## [用`v-for`把一個數組對應為一組元素](https://cn.vuejs.org/v2/guide/list.html#%E7%94%A8-v-for-%E6%8A%8A%E4%B8%80%E4%B8%AA%E6%95%B0%E7%BB%84%E5%AF%B9%E5%BA%94%E4%B8%BA%E4%B8%80%E7%BB%84%E5%85%83%E7%B4%A0 "用 v-for 把一個數組對應為一組元素")
我們用`v-for`指令根據一組數組的選項列表進行渲染。`v-for`指令需要使用`item in items`形式的特殊語法,`items`是源數據數組并且`item`是數組元素迭代的別名。
~~~
<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' }
]
}
})
~~~
結果:
* Foo
* 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`](https://cn.vuejs.org/v2/guide/list.html#%E4%B8%80%E4%B8%AA%E5%AF%B9%E8%B1%A1%E7%9A%84-v-for "一個對象的 v-for")
你也可以用`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: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
})
~~~
結果:
* How to do lists in Vue
* Jane Doe
* 2016-04-10
你也可以提供第二個的參數為 property 名稱 (也就是鍵名):
~~~
<div v-for="(value, name) in object">
{{ name }}: {{ value }}
</div>
~~~
title: How to do lists in Vue
author: Jane Doe
publishedAt: 2016-04-10
第三個參數為索引:
~~~
<div v-for="(value, name, index) in object">
{{ index }}. {{ name }}: {{ value }}
</div>
~~~
0\. title: How to do lists in Vue
1\. author: Jane Doe
2\. publishedAt: 2016-04-10
在遍歷對象時,是按`Object.keys()`的結果遍歷,但是不能保證它的結果在不同的 JavaScript 引擎下是一致的。
## [維護狀態](https://cn.vuejs.org/v2/guide/list.html#%E7%BB%B4%E6%8A%A4%E7%8A%B6%E6%80%81 "維護狀態")
當 Vue.js 用`v-for`正在更新已渲染過的元素列表時,它默認用“就地復用”策略。如果數據項的順序被改變,Vue 將不會移動 DOM 元素來匹配數據項的順序, 而是簡單復用此處每個元素,并且確保它在特定索引下顯示已被渲染過的每個元素。這個類似 Vue 1.x 的`track-by="$index"`。
這個默認的模式是高效的,但是**只適用于不依賴子組件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出**。
為了給 Vue 一個提示,以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素,你需要為每項提供一個唯一`key`屬性:
~~~
<div v-for="item in items" v-bind:key="item.id">
<!-- 內容 -->
</div>
~~~
建議盡可能在使用`v-for`時提供`key`attribute,除非遍歷輸出的 DOM 內容非常簡單,或者是刻意依賴默認行為以獲取性能上的提升。
因為它是 Vue 識別節點的一個通用機制,`key`并不與`v-for`特別關聯,`key`還具有其他用途,我們將在后面的指南中看到其他用途。
不要使用對象或數組之類的非原始類型值作為`v-for`的`key`。用字符串或數類型的值取而代之。
更多`key`attribute 的細節用法請移步至[`key`的 API 文檔](https://cn.vuejs.org/v2/api/#key)。
## [數組更新檢測](https://cn.vuejs.org/v2/guide/list.html#%E6%95%B0%E7%BB%84%E6%9B%B4%E6%96%B0%E6%A3%80%E6%B5%8B "數組更新檢測")
### [變異方法](https://cn.vuejs.org/v2/guide/list.html#%E5%8F%98%E5%BC%82%E6%96%B9%E6%B3%95 "變異方法")
Vue 包含一組觀察數組的變異方法,所以它們也將會觸發視圖更新。這些方法如下:
* `push()`
* `pop()`
* `shift()`
* `unshift()`
* `splice()`
* `sort()`
* `reverse()`
你打開控制臺,然后用前面例子的`items`數組調用變異方法:`example1.items.push({ message: 'Baz' })`。
### [替換數組](https://cn.vuejs.org/v2/guide/list.html#%E6%9B%BF%E6%8D%A2%E6%95%B0%E7%BB%84 "替換數組")
變異方法 (mutation method),顧名思義,會改變被這些方法調用的原始數組。相比之下,也有非變異 (non-mutating method) 方法,例如:`filter()`,`concat()`和`slice()`。這些不會改變原始數組,但**總是返回一個新數組**。當使用非變異方法時,可以用新數組替換舊數組:
~~~
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
~~~
你可能認為這將導致 Vue 丟棄現有 DOM 并重新渲染整個列表。幸運的是,事實并非如此。Vue 為了使得 DOM 元素得到最大范圍的重用而實現了一些智能的、啟發式的方法,所以用一個含有相同元素的數組去替換原來的數組是非常高效的操作。
### [注意事項](https://cn.vuejs.org/v2/guide/list.html#%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9 "注意事項")
由于 JavaScript 的限制,Vue 不能檢測以下變動的數組:
1. 當你利用索引直接設置一個項時,例如:`vm.items[indexOfItem] = newValue`
2. 當你修改數組的長度時,例如:`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`](https://cn.vuejs.org/v2/api/#vm-set)實例方法,該方法是全局方法`Vue.set`的一個別名:
~~~
vm.$set(vm.items, indexOfItem, newValue)
~~~
為了解決第二類問題,你可以使用`splice`:
~~~
vm.items.splice(newLength)
~~~
## [對象更改檢測注意事項](https://cn.vuejs.org/v2/guide/list.html#%E5%AF%B9%E8%B1%A1%E6%9B%B4%E6%94%B9%E6%A3%80%E6%B5%8B%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9 "對象更改檢測注意事項")
還是由于 JavaScript 的限制,**Vue 不能檢測對象屬性的添加或刪除**:
~~~
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` 現在是響應式的
vm.b = 2
// `vm.b` 不是響應式的
~~~
對于已經創建的實例,Vue 不能動態添加根級別的響應式屬性。但是,可以使用`Vue.set(object, propertyName, 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'
})
~~~
## [顯示過濾/排序結果](https://cn.vuejs.org/v2/guide/list.html#%E6%98%BE%E7%A4%BA%E8%BF%87%E6%BB%A4-%E6%8E%92%E5%BA%8F%E7%BB%93%E6%9E%9C "顯示過濾/排序結果")
有時,我們想要顯示一個數組的過濾或排序副本,而不實際改變或重置原始數據。在這種情況下,可以創建返回過濾或排序數組的計算屬性。
例如:
~~~
<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`](https://cn.vuejs.org/v2/guide/list.html#%E4%B8%80%E6%AE%B5%E5%8F%96%E5%80%BC%E8%8C%83%E5%9B%B4%E7%9A%84-v-for "一段取值范圍的 v-for")
`v-for`也可以取整數。在這種情況下,它將重復多次模板。
~~~
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
~~~
結果:
12345678910
## [`v-for`on a`<template>`](https://cn.vuejs.org/v2/guide/list.html#v-for-on-a-lt-template-gt "v-for on a ")
類似于`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`](https://cn.vuejs.org/v2/guide/list.html#v-for-with-v-if "v-for with v-if")
注意我們**不**推薦同時使用`v-if`和`v-for`。更多細節可查閱[風格指南](https://cn.vuejs.org/v2/style-guide/#%E9%81%BF%E5%85%8D-v-if-%E5%92%8C-v-for-%E7%94%A8%E5%9C%A8%E4%B8%80%E8%B5%B7-%E5%BF%85%E8%A6%81)。
當它們處于同一節點,`v-for`的優先級比`v-if`更高,這意味著`v-if`將分別重復運行于每個`v-for`循環中。當你想為僅有的*一些*項渲染節點時,這種優先級的機制會十分有用,如下:
~~~
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
~~~
上面的代碼只傳遞了未完成的 todos。
而如果你的目的是有條件地跳過循環的執行,那么可以將`v-if`置于外層元素 (或[`<template>`](https://cn.vuejs.org/v2/guide/conditional.html#%E5%9C%A8-lt-template-gt-%E4%B8%AD%E9%85%8D%E5%90%88-v-if-%E6%9D%A1%E4%BB%B6%E6%B8%B2%E6%9F%93%E4%B8%80%E6%95%B4%E7%BB%84))上。如:
~~~
<ul v-if="todos.length">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
<p v-else>No todos left!</p>
~~~
## [一個組件的`v-for`](https://cn.vuejs.org/v2/guide/list.html#%E4%B8%80%E4%B8%AA%E7%BB%84%E4%BB%B6%E7%9A%84-v-for "一個組件的 v-for")
> 了解組件相關知識,查看[組件](https://cn.vuejs.org/v2/guide/components.html)。完全可以先跳過它,以后再回來查看。
在自定義組件里,你可以像任何普通元素一樣用`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`的運作緊密耦合。明確組件數據的來源能夠使組件在其他場合重復使用。
下面是一個簡單的 todo list 的完整例子:
~~~
<div id="todo-list-example">
<form v-on:submit.prevent="addNewTodo">
<label for="new-todo">Add a todo</label>
<input
v-model="newTodoText"
id="new-todo"
placeholder="E.g. Feed the cat"
>
<button>Add</button>
</form>
<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 模板解析說明](https://cn.vuejs.org/v2/guide/components.html#%E8%A7%A3%E6%9E%90-DOM-%E6%A8%A1%E6%9D%BF%E6%97%B6%E7%9A%84%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9)來了解更多信息。
~~~
Vue.component('todo-item', {
template: '\
<li>\
{{ title }}\
<button v-on:click="$emit(\'remove\')">Remove</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 = ''
}
}
})
~~~
Add a todoAdd
* Do the dishesRemove
* Take out the trashRemove
* Mow the lawnRemove
- 內容介紹
- EcmaScript基礎
- 快速入門
- 常量與變量
- 字符串
- 函數的基本概念
- 條件判斷
- 數組
- 循環
- while循環
- for循環
- 函數基礎
- 對象
- 對象的方法
- 函數
- 變量作用域
- 箭頭函數
- 閉包
- 高階函數
- map/reduce
- filter
- sort
- Promise
- 基本對象
- Arguments 對象
- 剩余參數
- Map和Set
- Json基礎
- RegExp
- Date
- async
- callback
- promise基礎
- promise-api
- promise鏈
- async-await
- 項目實踐
- 標簽系統
- 遠程API請求
- 面向對象編程
- 創建對象
- 原型繼承
- 項目實踐
- Classes
- 構造函數
- extends
- static
- 項目實踐
- 模塊
- import
- export
- 項目實踐
- 第三方擴展庫
- immutable
- Vue快速入門
- 理解MVVM
- Vue中的MVVM模型
- Webpack+Vue快速入門
- 模板語法
- 計算屬性和偵聽器
- Class 與 Style 綁定
- 條件渲染
- 列表渲染
- 事件處理
- 表單輸入綁定
- 組件基礎
- 組件注冊
- Prop
- 自定義事件
- 插槽
- 混入
- 過濾器
- 項目實踐
- 標簽編輯
- 移動客戶端開發
- uni-app基礎
- 快速入門程序
- 單頁程序
- 底部Tab導航
- Vue語法基礎
- 模版語法
- 計算屬性與偵聽器
- Class與Style綁定
- 樣式與布局
- Box模型
- Flex布局
- 內置指令
- 基本指令
- v-model與表單
- 條件渲染指令
- 列表渲染指令v-for
- 事件與自定義屬性
- 生命周期
- 項目實踐
- 學生實驗
- 貝店商品列表
- 加載更多數據
- 詳情頁面
- 自定義組件
- 內置組件
- 表單組件
- 技術專題
- 狀態管理vuex
- Flyio
- Mockjs
- SCSS
- 條件編譯
- 常用功能實現
- 上拉加載更多數據
- 數據加載綜合案例
- Teaset UI組件庫
- Teaset設計
- Teaset使用基礎
- ts-tag
- ts-badge
- ts-button
- ta-banner
- ts-list
- ts-icon
- ts-load-more
- ts-segmented-control
- 代碼模版
- 項目實踐
- 標簽組件
- 失物招領客戶端原型
- 發布頁面
- 檢索頁面
- 詳情頁面
- 服務端開發技術
- 服務端開發環境配置
- Koajs快速入門
- 快速入門
- 常用Koa中間件介紹
- 文件上傳
- RestfulApi
- 一個復雜的RESTful例子
- 使用Mockjs生成模擬數據
- Thinkjs快速入門
- MVC模式
- Thinkjs介紹
- 快速入門
- RESTful服務
- RBAC案例
- 關聯模型
- 應用開發框架
- 服務端開發
- PC端管理界面開發
- 移動端開發
- 項目實踐
- 失物招領項目
- 移動客戶端UI設計
- 服務端設計
- 數據庫設計
- Event(事件)
- 客戶端設計
- 事件列表頁面
- 發布頁面
- 事件詳情頁面
- API設計
- image
- event
- 微信公眾號開發
- ui設計規范