Development/Vue.js

[Vue.js] Document 따라하기 - List Rendering

도롱뇽도롱 2022. 6. 12. 18:00
반응형

v-for

v-for 디렉티브를 사용하여 배열을 리스트로 렌더링 할 수 있다. v-for 디렉티브는 'item in items' 형식의 특수 구문이 필요하다. 여기서 items는 소스 데이터 배열이고 item은 반복되는 배열 요소의 별칭(alias)이다.

data() {
  return {
    items: [{ message: 'Foo' }, { message: 'Bar' }]
  }
}
<li v-for="item in items">
  {{ item.message }}
</li>

v-for
v-for

v-for 범위 내에서 템플릿 표현식은 모든 상위 범위 속성에 액세스 할 수 있다. 또한 v-for은 현재 아이템의 인덱스에 대한 선택적 두 번째 별칭(alias)도 지원한다.

data() {
  return {
    parentMessage: 'Parent',
    items: [{ message: 'Foo' }, { message: 'Bar' }]
  }
}
<li v-for="(item, index) in items">
  {{ parentMessage }} - {{ index }} - {{ item.message }}
</li>

Directive v-for access to all parent scope properties
Directive v-for access to all parent scope properties

v-for의 변수 범위는 다음 JavaScript와 유사하다.

const parentMessage = 'Parent'
const items = [
  /* ... */
]

items.forEach((item, index) => {
  // 콜백함수 외부에 있는 `parentMessage`에 접근 가능
  // item과 index는 콜백함수 내부에서만 접근 가능
  console.log(parentMessage, item.message, index)
})

v-for의 값이 forEach 콜백 함수의 특징과 유사하다는 것을 느낄 수 있을 것이다. 실제로 콜백 함수의 인수를 분해 할당하여 사용할 수 있는 것과 마찬가지로 v-for의 아이템도 분해 할당하여 사용할 수 있다.

<li v-for="{ message } in items">
  {{ message }}
</li>

<!-- with index alias -->
<li v-for="({ message }, index) in items">
  {{ message }} {{ index }}
</li>

v-for with index
v-for with index

중첩된 v-for의 경우 중첩된 함수와 유사한 범위를 가진다. 각 v-for 범위는 상위 범위에 액세스 할 수 있다.

<li v-for="item in items">
  <span v-for="childItem in item.children">
    {{ item.message }} {{ childItem }}
  </span>
</li>

Nested v-for
Nested v-for

in 대신 of를 구분 기호로 사용 가능하여 JavaScript 반복문 구문처럼 사용할 수 있다.

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

v-for using of
v-for using of

 

v-for with an Object

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

data() {
  return {
    myObject: {
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    }
  }
}
<ul>
  <li v-for="value in myObject">
    {{ value }}
  </li>
</ul>

v-for with an Object
v-for with an Object

속성 명(a.k.a. key)에 대한 두 번째 별칭(alias)을 제공할 수도 있다.

<li v-for="(value, key) in myObject">
  {{ key }}: {{ value }}
</li>

v-for with an Object using key
v-for with an Object using key

tip. 객체 속성을 반복할 때 순서는 Object.keys()의 열거 순서를 기반으로하며 JavaScript 엔진 구현에서 일관성이 보장되지 않기에 순서는 변경될 수 있다.

 

v-for with a Range

v-for은 정수를 취할 수도 있다. 이 경우 1...n의 범위를 기반으로 템플릿을 여러 번 반복한다.

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

여기서 n의 값은 0이 아닌 1부터 시작한다.

v-for with a Range
v-for with a Range

 

v-for on <template>

v-if와 유사하게 v-for과 <template> 태그를 사용하여 여러 element의 블록을 렌더링 할 수도 있다. 예를 들면 다음과 같다.

<ul>
  <template v-for="item in items">
    <li>{{ item.msg }}</li>
    <li class="divider" role="presentation"></li>
  </template>
</ul>

v-for on <template>
v-for on <template>

 

v-for with v-if

암시적 우선 순위 때문에 동일한 element에 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>

<template> 태그로 감싼 후 v-for를 <template>으로 옮겨서 해결할 수 있으며 이것이 더 명시적이기도 하다.

<template v-for="todo in todos">
  <li v-if="!todo.isComplete">
    {{ todo.name }}
  </li>
</template>

v-for with v-if
v-for with v-if

 

Maintaining State with key

Vue가 v-for로 렌더링 된 element list를 업데이트할 때, 기본적으로 "in-place patch" 전략을 사용한다. 리스트 아이템의 순서가 변경된 경우 아이템의 순서와 일치하도록 DOM element를 이동하는 대신 Vue는 각 element를 제자리에 패치하고 특정 인덱스에서 렌더링 되어야 하는 아이템을 반영하는지 확인한다.

 

이러한 기본동작은 효율적이지만 리스트 렌더링 출력이 하위 component 상태 또는 임시 DOM 상태(form input values)에 의존하지 않는 경우에만 유효하다.

 

Vue가 각 노드의 ID를 추적하고 기존 element를 재사용하고 재 정렬할 수 있도록 힌트를 제공하려면 각 항목에 대해 고유한 'key' 속성을 제공해야 한다.

<div v-for="item in items" :key="item.id">
  <!-- content -->
</div>

v-for with key
v-for with key

<template v-for>를 사용하는 경우 key를 <template> 컨테이너에 위치해야 한다.

<template v-for="todo in todos" :key="todo.name">
  <li>{{ todo.name }}</li>
</template>

여기서 key는 v-bind와 결합되는 특별한 속성으로 상단에서 언급하였던 객체와 함께 v-for를 사용할 때의 속성 key 변수와 혼동해서는 안된다.

v-for with key on template
v-for with key on template

반복되는 DOM 컨텐츠가 단순하지 않거나(component나 상태 저장 DOM 요소가 포함되지 않은 경우), 성능 향상을 위해 의도적으로 기본 동작에 의존하는 경우가 아니라면 v-for에 key 속성을 제공하는 것이 좋다.

 

key에는 문자열, 숫자, 심볼 형식의 값만 바인딩해야 한다. 객체를 v-for의 키로 사용하지 말아야 하며 키 속성의 자세한 사용법은 추후 key API 문서 참조.

 

v-for with a Component

이 섹션에서는 Components에 대한 사전 지식이 있다고 가정한다. 만약 Components에 대한 지식이 없다면 건너뛰고 나중에 읽어볼 것을 권장한다.

 

일반 element와 마찬가지로 component에 v-for를 직접 사용할 수 있다(key 사용을 잊지 말자).

<my-component v-for="item in items" :key="item.id"></my-component>

그러나 component에는 자체적으로 구분된 범위가 있기 때문에 component에 데이터를 자동으로 전달하지 않는다. 반복된 데이터를 component에 전달하려면 props도 사용해야 한다.

v-for with a Component
v-for with a Component

Component에 item이 자동으로 전달되지 않는 이유는 item이 자동으로 전달된다면 component가 v-for의 작동 방식과 밀접하게 연결되기 때문이다. 데이터를 명시해서 전달하는 방법은 v-for를 사용하지 않는 다른 상황에서도 component가 재사용될 수 있게 한다.

 

v-for를 사용하여 component 목록을 렌더링하고 각 인스턴스에 다른 데이터를 전달하는 방법을 보려면 간단한 todo list 예제를 참고하자.

 

Array Change Detection

Mutation Methods

Vue는 배열을 수정하는 메서드를 래핑하고 관찰하며 view 업데이트를 트리거한다. 래핑 된 메서드는 다음과 같다.

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

 

Replacing an Array

Mutation 메서드는 이름에서 알 수 있듯이 호출된 원래 배열을 수정하며 수정이 아닌 방법도 있다. filter(), concat(), slice()는 원본 배열을 수정하지 않지고 항상 새 배열을 반환한다. 이러한 방법으로 작업할 때에는 이전 배열을 새 배열로 교체해야 한다.

this.items = this.items.filter((item) => item.message.match(/Foo/))

이로 인해 Vue가 기존 DOM을 버리고 전체 리스트를 다시 렌더링할 것이라고 생각할 수 있지만 다행히도 그렇지는 않다. Vue는 DOM element 재사용을 최대화하기 위해 몇 가지 스마트 휴리스틱을 구현하므로 이전 배열을 다른 배열로 바꾸는 과정에서 서로 중복되는 객체를 가지는 부분을 매우 효율적으로 처리한다.

 

Displaying Filtered/Sorted Results

때로는 원본 데이터를 실제로 수정하거나 교체하지 않고 필터링되거나 정렬된 결과를 표시하고 싶을 때가 있다. 이 경우 필터링되거나 정렬된 배열을 반환하는 computed 속성을 만들 수 있다.

예를 들어

data() {
  return {
    numbers: [1, 2, 3, 4, 5]
  }
},
computed: {
  evenNumbers() {
    return this.numbers.filter(n => n % 2 === 0)
  }
}
<li v-for="n in evenNumbers">{{ n }}</li>

v-for with computed
v-for with computed

Computed 속성이 실현 가능하지 않은 상황(중첩된 v-for 루프 내부 등)에서는 다음과 같은 방법으로 해결할 수 있다.

v-for with method
v-for with method

Computed 속성에서 reverse()와 sort() 사용에 주의하여야 한다. 이 두 가지 방법은 원본 배열을 수정하기 때문에 computed 속성의 getter 함수에서 피해야 한다. 다음 메서드를 호출하기 전에 원본 배열의 복사본을 만들자.

- return numbers.reverse()
+ return [...numbers].reverse()

 

 

관련 Repo

https://github.com/galaxyuliana/VueExercises/tree/master/first-vue-project/src/components/Exercise/06

 

참고 자료

https://vuejs.org/guide/essentials/list.html

반응형