# リストレンダリング
# v-for で配列に要素をマッピングする
配列に基づいて、アイテムのリストを描画するために、v-for
ディレクティブを使用することができます。 v-for
ディレクティブには、 item in items
の形式の特別な構文が必要で、 items
はソースデータの配列、 item
は繰り返される配列要素のエイリアスです:
<ul id="array-rendering">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
2
3
4
5
Vue.createApp({
data() {
return {
items: [{ message: 'Foo' }, { message: 'Bar' }]
}
}
}).mount('#array-rendering')
2
3
4
5
6
7
結果:
v-for
ブロック内では、親スコープのプロパティへの完全なアクセスを持っています。また v-for
は現在のアイテムに対する配列のインデックスを、任意の2つ目の引数としてサポートしています。
<ul id="array-with-index">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
2
3
4
5
Vue.createApp({
data() {
return {
parentMessage: 'Parent',
items: [{ message: 'Foo' }, { message: 'Bar' }]
}
}
}).mount('#array-with-index')
2
3
4
5
6
7
8
結果:
また、区切り文字として in
の代わりに of
を使用することができます。これは JavaScript のイテレータ構文に近いものです:
<div v-for="item of items"></div>
# オブジェクトの v-for
オブジェクトのプロパティに対して、v-for
を使って反復処理することもできます。
<ul id="v-for-object" class="demo">
<li v-for="value in myObject">
{{ value }}
</li>
</ul>
2
3
4
5
Vue.createApp({
data() {
return {
myObject: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
}
}).mount('#v-for-object')
2
3
4
5
6
7
8
9
10
11
結果:
2 つ目の引数としてプロパティ名(すなわちキー)も提供できます:
<li v-for="(value, name) in myObject">
{{ name }}: {{ value }}
</li>
2
3
index も提供できます:
<li v-for="(value, name, index) in myObject">
{{ index }}. {{ name }}: {{ value }}
</li>
2
3
Note
オブジェクトを反復処理するとき、順序は Object.keys()
の列挙順のキーに基づいており、全ての JavaScript エンジンの実装で一貫性が保証されていません。
# 状態の維持
Vue が v-for
で描画された要素のリストを更新する際、デフォルトでは “その場でパッチを適用する” (in-place patch) 戦略が用いられます。データのアイテムの順序が変更された場合、アイテムの順序に合わせて DOM 要素を移動する代わりに、 Vue は各要素にその場でパッチを適用して、その特定のインデックスに何を描画するべきかを確実に反映します。
このデフォルトのモードは効率がいいです。しかしこれは、描画されたリストが子コンポーネントの状態や、一時的な DOM の状態に依存していないときにだけ適しています (例: フォームのインプットの値)。
Vue が各ノードの識別情報を追跡できるヒントを与えるために、また、先ほど説明したような既存の要素の再利用と並び替えができるように、一意な key
属性を全てのアイテムに与える必要があります:
<div v-for="item in items" :key="item.id">
<!-- content -->
</div>
2
3
繰り返される DOM の内容が単純な場合や、性能向上のためにデフォルトの動作に意図的に頼る場合を除いて、可能なときはいつでも v-for
に key
属性を与えることが推奨されます。
これは Vue がノードを識別する汎用的な仕組みなので、key
はガイドの後半でわかるように v-for
に縛られない他の用途もあります。
Note
オブジェクトや配列のような非プリミティブ値を v-for
のキーとして使わないでください。代わりに、文字列や数値を使ってください。
key
属性の詳細な使い方は、key
API ドキュメントを参照してください。
# 配列の変化を検出
# 変更メソッド
Vue は画面の更新もトリガするために、監視された配列の変更メソッドをラップします。ラップされたメソッドは次のとおりです:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
コンソールを開いて前の items
配列の例で変更メソッドを呼び出して試してみてください。例えば example1.items.push({ message: 'Baz' })
のようにしてみましょう。
# 配列の置き換え
変更メソッドは、名前が示唆するように、それらが呼ばれると元の配列を変更します。一方で、変更しないメソッドもあります。例えば、filter()
、concat()
、slice()
は、元の配列を変更せず、常に新しい配列を返します。これらのメソッドを使用する場合は、新しいもので古い配列を置き換えることで変更できます:
example1.items = example1.items.filter(item => item.message.match(/Foo/))
これにより、Vue が既存の DOM を破棄し、リスト全体を再描画すると思われるかもしれませんが、幸いなことにそうではありません。Vue は DOM 要素の再利用を最大化するためにいくつかのスマートなヒューリスティックを実装しているので、重複するオブジェクトを含んでいる別の配列で元の配列を置き換えることは、とても効率的な操作です。
# フィルタ/ソートされた結果の表示
元のデータを実際に変更またはリセットせずに、フィルタリングやソートされたバージョンの配列を表示したいことがあります。このケースでは、フィルタリングやソートされた配列を返す算出プロパティを作ることができます。
例えば:
<li v-for="n in evenNumbers" :key="n">{{ n }}</li>
data() {
return {
numbers: [ 1, 2, 3, 4, 5 ]
}
},
computed: {
evenNumbers() {
return this.numbers.filter(number => number % 2 === 0)
}
}
2
3
4
5
6
7
8
9
10
算出プロパティが使えない状況ではメソッドを使うこともできます。(例: 入れ子になった v-for のループの中):
<ul v-for="numbers in sets">
<li v-for="n in even(numbers)" :key="n">{{ n }}</li>
</ul>
2
3
data() {
return {
sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
}
},
methods: {
even(numbers) {
return numbers.filter(number => number % 2 === 0)
}
}
2
3
4
5
6
7
8
9
10
# 範囲付き v-for
v-for
は整数値を取ることもできます。このケースでは、指定された数だけテンプレートが繰り返されます。
<div id="range" class="demo">
<span v-for="n in 10" :key="n">{{ n }} </span>
</div>
2
3
結果:
# <template>
での v-for
テンプレート v-if
と同様に、複数の要素のブロックをレンダリングするために、 v-for
で <template>
タグを使うこともできます。例:
<ul>
<template v-for="item in items" :key="item.msg">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
2
3
4
5
6
# v-for
と v-if
TIP
v-if
と v-for
を同時に利用することは推奨されません。 詳細については スタイルガイド を参照ください。
それらが同じノードに存在するとき、 v-if
は v-for
よりも高い優先度を持ちます。つまり v-if
の条件は、 v-for
のスコープの変数にはアクセスできないということです:
<!-- インスタンスに "todo" プロパティが定義されていないため、エラーが発生します。 -->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>
2
3
4
5
これは v-for
を <template>
タグで囲み、移動させることで修正できます:
<template v-for="todo in todos" :key="todo.name">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
2
3
4
5
# コンポーネントと v-for
このセクションでは、コンポーネントについての知識を前提としています。もし分からなければ、このセクションを遠慮なく飛ばして、理解した後に戻ってきてください。
通常の要素のように、カスタムコンポーネントで直接 v-for
を使うことができます:
<my-component v-for="item in items" :key="item.id"></my-component>
ただし、コンポーネントは自身の隔離されたスコープを持っているため、これによってコンポーネントにデータが自動的に渡されることはありません。繰り返されたデータをコンポーネントに渡すには、プロパティも使用する必要があります:
<my-component
v-for="(item, index) in items"
:item="item"
:index="index"
:key="item.id"
></my-component>
2
3
4
5
6
自動的に item
をコンポーネントに渡さない理由は、それが v-for
の動作と密結合になってしまうからです。どこからデータが来たのかを明確にすることで、他の場面でコンポーネントを再利用できるようになります。
これは、単純な ToDo リストの完全な例です:
<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>
<todo-item
v-for="(todo, index) in todos"
:key="todo.id"
:title="todo.title"
@remove="todos.splice(index, 1)"
></todo-item>
</ul>
</div>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const app = Vue.createApp({
data() {
return {
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() {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText
})
this.newTodoText = ''
}
}
})
app.component('todo-item', {
template: `
<li>
{{ title }}
<button @click="$emit('remove')">Remove</button>
</li>
`,
props: ['title'],
emits: ['remove']
})
app.mount('#todo-list-example')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
← 条件付きレンダリング イベントハンドリング →