v-for을 통해서 요소를 반복해서 출력할 수 있다.
<li v-for="item in items">
{{ item.message }}
</li>
현재 항목의 인덱스를 두 번째 전달인자 옵션으로 제공한다.
<li v-for="(item, index) in items">
{{ index }} - {{ item.message }}
</li>
in 대신에 of를 구분자로 사용할 수 있다.
JavaScript의 이터레이터 구문과 유사하다.
<div v-for="item of items"></div>
객체의 속성을 반복 (value를 반복)
<ul>
<li v-for="value in myObject">
{{ value }}
</li>
</ul>
key에 대한 두번째 전달 인자를 제공한다.
<ul>
<li v-for="(value, key) in myObject">
{{ key }}: {{ value }}
</li>
</ul>
세번째 전달 인자로는 index를 제공한다.
<ul>
<li v-for="(value, key, index) in myObject">
{{ index }}. {{ name }}: {{ value }}
</li>
</ul>
객체를 반복할 때는 순서를 보장할 수 없다.
v-for 디레틱브를 사용할 때는 항상 key 속성을 사용하여 고유한 값을 지정해주어야 한다.
<div v-for="item in items" :key="item.id">
<!-- content -->
</div>
배열이 반응형 데이터면 변이 메소드를 사용해서 배열이 변경된 것을 감지할 수 있고 화면을 갱신할 수 있다.
filter(), concat(), slice()와 같은 원래 배열을 변경하지는 않지만 항상 새 배열을 반환하는 비-변이 메소드가 있다.
example.items = example.items.filter(item => item.message.match(/Foo/))
이 메소드를 사용할 시 기존 DOM을 버리고 전체 목록을 다시 렌더링 할 것이라고 생각할 수 있지만 그렇지 않다.
Vue는 이전 배열과 현재 배열의 차이점을 비교해서 변경된 데이터만 다시 화면에 출력한다. (최적화)
v-for에서 computed 데이터를 사용해도 마찬가지로 변경된 데이터만 리렌더링한다.
<li v-for="n in evenNumbers">{{ n }}</li>
<script>
data() {
return {
numbers: [ 1, 2, 3, 4, 5 ]
}
},
computed: {
evenNumbers() {
return this.numbers.filter(number => number % 2 === 0)
}
}
</script>
computed 속성이 실행 가능하지 않은 상황에서는 메소드를 사용할 수 있다.
<ul v-for="numbers in sets">
<li v-for="n in even(numbers)>{{ n }}</li>
</ul>
<script>
data () {
return {
sets: [[ 1, 2, 3, 4, 5 ], [ 6, 7, 8, 9,10 ]]
}
},
methods: {
even(numbers) {
return numbers.filter(number => number % 2 === 0)
}
}
</script>
v-for도 정수를 사용할 수 있다. 이 경우 템플릿을 여러 번 반복한다.
(0이 아닌 1부터 시작)
<div>
<!-- 12345678910 -->
<span v-for="n in 10">{{ n }}</span>
</div>
v-if와 마찬가지로, v-for와 함께 <template>
태그를 사용하여 여러 요소의 블록을 렌더링 할 수도 있다.
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li></li>
</template>
</ul>
components 옵션: 외부에 다른 컴포넌트를 정의
emit(이름, 전달할 인수): 커스텀 이벤트 지정
예제
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<form @submit="addNewTodo">
<label for="new-todo">Add a todo</label>
<input id="new-todo" v-model="newTodoText" placeholder="E.g. Feed the cat" />
<button>Add</button>
</form>
<ul>
<todo-item v-for="todo in todos" :key="todo.id" :todo="todo" @remove="removeTodo" />
</ul>
</div>
<script>
function generateId() {
return `${Date.now()}${Math.random()}`;
}
const TodoItem = {
template: `
<li>
{{ todo.title }}
<button @click="$emit('remove', todo.id)">Remove</button>
</li>
`,
props: ["todo"], // todo를 컴포넌트 내부에서 사용
};
const App = {
components: {
TodoItem, // 컴포넌트를 정의
},
data() {
return {
newTodoText: "",
todos: [],
};
},
methods: {
addNewTodo(event) {
event.preventDefault();
this.todos.push({
id: generateId(),
title: this.newTodoText,
});
this.newTodoText = "";
console.log(this.todos);
},
removeTodo(todoId) {
// Vue는 내용을 비교하고 다른 점만 새로 갱신
this.todos = this.todos.filter((todo) => {
return todo.id !== todoId;
});
},
},
};
const vm = Vue.createApp(App).mount("#app");
</script>
</body>
</html>