본문 바로가기

Vue.js

리스트 렌더링

v-for로 엘리먼트에 배열 매핑하기


v-for 디렉티브를 사용하여 배열을 기반으로 리스트를 렌더링 할 수 있다. 
v-for 디렉티브는item in items형태로 특별한 문법이 필요하다. 
여기서 items는 원본 데이터 배열이고 item은 반복되는 배열 엘리먼트의 별칭.

# 기본 사용 방법

<body>
<ul id="example-1">
<li v-for="item in items">{{ item.message }}</li>
-------------------------
<li v-for="item in list">{{ item }}</li>
</ul>

<script>
var example1 = new Vue({
el: "#example-1",
data: {
items: [{ message: "Foo" }, { message: "Bar" }],
list: ["루비", "낑깡", "티버", "라시코스"]
}
});
</script>
</body>

결과 : 


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" }]
}
});

결과: 


in 대신에 of를 구분자로 사용할 수 있다. 그래서 JavaScript의 이터레이터에 대한 자바스크립트 구문과 유사하다.

<div v-for="item of items"></div>




v-for 와 객체


v-for를 사용하여 객체의 속성을 반복할 수도 있다.

키에 대한 두번째 전달 인자를 제공할 수도 있다.


예 : 

<div id="v-for-object">
<div v-for="(value, key, index) in object">
{{ index }}. {{ key }} : {{ value }}
</div>
</div>
    new Vue({
el: "#v-for-object",
data: {
object: {
firstName: "Kim",
lastName: "Jye",
age: 100
}
}
});

결과 :


객체를 반복할 때 순서는 Object.keys()의 키 나열 순서에 따라 결정된다. 

이 순서는 JavaScript 엔진 구현간에 일관적이지는 않는다.




key


Vue가 각 노드의 ID를 추적하고 기존 엘리먼트를 재사용하고 재정렬할 수 있도록 힌트를 제공하려면 각 항목에 고유한 key 속성을 제공해야 한다. 
key에 대한 이상적인 값은 각 항목의 고유한 ID.
v-bind를 사용하여 동적 값에 바인딩 해야한다.

<ul id="example-1">
<li v-for="item in items" :key="item.id">{{ item.message }}</li>
-------------------------
<li v-for="item in list" :key="item.id">{{ item }}</li>
</ul>

반복되는 DOM 내용이 단순하지 않거나 의도적인 성능 향상을 위해 기본 동작에 의존하지 않는한 가능하면 언제나 v-for에 key를 추가하는 것이 좋다.

key는 Vue가 노드를 식별하는 일반적인 메커니즘이기 때문에 v-for와 특별히 연관되지 않는 다른 용도로도 사용된다. 뒤에서 이와 관련된 내용이 나올 것이다.




배열 변경 감지


# 변이 메소드

Vue는 감시중인 배열의 변이 메소드를 래핑하여 뷰 갱신을 트리거한다. 래핑된 메소드는 다음과 같다.
  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()


 

# 배열 대체


이름에서 알 수 있듯 변이 메소드는 호출된 원본 배열을 변형한다. 이와 비교하여 변형을 하지 않는 방법도 있다.

  • filter()
  • concat()
  • slice()

이 방법을 사용하면 원본 배열을 변형하지 않고 항상 새 배열을 반환. 변형이 없는 방법으로 작업할 때 이전 배열을 새 배열로 바꿀 수 있다.


example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})


=> 콘솔창에 위코드를 실행해보면 'Foo'만 나옴





# 주의 사항


JavaScript의 제한으로 인해 Vue는 배열에 대해 다음과 같은 변경 사항을 감지할 수 없다.

  1. 인덱스로 배열에 있는 항목을 직접 설정하는 경우, 예: vm.items[indexOfItem] = newValue
  2. 배열 길이를 수정하는 경우, 예: vm.items.length = newLength


//1번 극복 위한 코드
Vue.set(example1.items, indexOfItem, newValue)
//2번 극복 위한 코드
example1.items.splice(newLength)





객체 변경 감지에 관한 주의사항


모던 JavaScript의 한계로 Vue는 속성 추가 및 삭제를 감지하지 못한다.

Vue는 이미 만들어진 인스턴스에 새로운 루트레벨의 반응형 속성을 동적으로 추가하는 것을 허용하지 않는다. 

그러나 Vue.set(object, key, value) 메소드를 사용하여 중첩된 객체에 반응형 속성을 추가할 수 있다.


var vm = new Vue({
data: {
userProfile: {
name: 'Anika'
}
}
})


다음과 같이 중첩된 userProfile 객체에 새로운 속성 age를 추가한다.


Vue.set(vm.userProfile, 'age', 27)


인스턴스 메소드인 vm.$set를 사용할 수도 있다. 이는 전역 Vue.set의 별칭이다.



vm.$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 루프 내부)에서는 다음 방법을 사용할 수 있다.

<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
})
}
}





Range v-for


v-for 는 숫자를 사용할 수 있다. 이 경우 템플릿을 여러번 반복한다.


예 : 

<div>
<span v-for="n in 10">{{ n }} </span>
</div>

결과 :                                     1 2 3 4 5 6 7 8 9 10




v-for 템플릿


템플릿 v-if와 마찬가지로, <template>태그를 사용해 여러 엘리먼트의 블럭을 렌더링 할 수 있다.


<div id="app">
<ul>
<template v-for="item in numbers">
<li>{{ item }}</li>
<li>{{ item * item }}</li>
</template>
</ul>
</div>
new Vue({
el: "#app",
data: {
numbers: [1, 2, 3, 4]
}
});




v-for 와 v-if


동일한 노드에 두가지 모두 있다면, v-for가 v-if 보다 높은 우선순위를 갖는다. 
즉, v-if는 루프가 반복될 때마다 실행된다. 이는 _일부_ 항목만 렌더링 하려는 경우 유용한다.
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>


위의 경우 완료되지 않은 할일만 렌더링.

위 방법 대신 실행을 조건부로 하는 것이 목적이라면 래퍼 엘리먼트(또는 <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-forkey 가 필수.

그러나 컴포넌트에는 자체 범위가 분리되어있기 때문에 컴포넌트에 데이터를 자동으로 전달하지는 않는다.

반복할 데이터를 컴포넌트로 전달하려면 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의 작동 방식과 밀접하게 결합되기 때문이다. 

데이터의 출처를 명확히 하면 다른 상황에서 컴포넌트를 재사용할 수 있다.



간단한 예제 :

<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" 속성을 보면 <li> 엘리먼트는 <ul> 안에서만 유효하다. <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.js' 카테고리의 다른 글

폼 입력 바인딩  (0) 2019.01.15
이벤트 렌더링  (0) 2019.01.15
조건부 렌더링  (0) 2019.01.14
클래스와 스타일 바인딩  (0) 2019.01.14
MVVM, MVC 패턴  (0) 2019.01.08