[vue3] 리스트 렌더링(v-for)

송인호·2023년 8월 13일
0

vue3

목록 보기
9/15

v-for

v-for 디렉티브를 사용하여 배열을 리스트로 렌더링할 수 있음.
v-for 디렉티브는 item in items 형식의 특별한 문법이 필요함.
여기서 items는 배열이고, item은 배열 내 반복되는 엘리먼트의 별칭(alias) 임.

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

v-for 범위 내 템플릿 표현식은 모든 상위 범위 속성에 접근할 수 있음.
또한 v-for는 현재 아이템의 인덱스를 가리키는 선택적 두 번째 별칭도 지원함.

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

Parent - 0 - Foo
Parent - 1 -Bar

v-for 에 주어진 값이forEach의 콜백 함수의 특징과 유사함. 실제로 콜백 함수인자를 분해 할당해 사용할 수 있는 것처럼, v-for의 아이템도 분해 할당해 사용할 수 있음.

중첩된 v-for의 경우, 중첩된 함수와 유사한 범위를 가짐. 각 v-for 범위에는 상위 범위에 대한 접근 권한이 있음.

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

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

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

객체에 v-for 사용하기

v-for 를 객체의 속성을 반복하는 데 사용할 수 있음. 순회 순서는 해당 객체를 Object.keys()를 호출한 결과에 기반함.

const myObject = reactive({
  title: 'Vue에서 목록을 작성하는 방법',
  author: '홍길동',
  publishedAt: '2016-04-10'
})
<ul>
  <li v-for="value in myObject">
    {{ value }}
  </li>
</ul>

속성 명을 가리키는 두 번째 별칭을 사용할 수도 있음.

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

그리고 인덱스를 가리키는 세 번째 별칭을 사용할 수도 있음.

<li v-for="(value, key, index) in myObject>
{{ index }}, {{ key }}: {{ value }}
</li>
  1. title: How to do lists in Vue
  2. author: Jane Doe
  3. publishedAt: 2016-04-10

숫자 범위에 v-for 사용하기

v-for 는 정수를 사용할 수도 있음. 이경우 1...n 범위를 기준으로 템플릿을 여러 번 반복함

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

여기서 n의 값은 0이 아니라 1부터 시작함.

<template> 에서 v-for 사용하기

v-if 와 유사하게 <template> 태그에 v-for를 사용하여 여러 엘리먼트 블록을 렌더링 할 수도 있음.

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

v-if에 v-for 사용하기

v-ifv-for를 함께 사용하는 것은 권장되지 않음.

v-ifv-for보다 우선순위가 높기 때문에 v-if 조건문에서 v-for 변수에 접근할 수 없음.

// "todo" 속성이 인스턴스에 정의되어 있지 않기 때문에 에러가 발생.
<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo.name }}
</li>

<template> 태그로 감싼 후, v-for 를 옮겨서 해결할 수 있음.

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

key를 통한 상태유지

Vue가 v-for로 렌더링된 리스트를 업데이트할 때, 기본적으로 "in-place patch" 전략을 사용함.
리스트 아이템의 순서가 변경된 경우, 아이템의 순서와 일치하도록 DOM 엘리먼트를 이동하는 대신, 변경이 필요한 인덱스의 엘리먼트들을 제자리에서 패치해 아이템을 렌더링하도록 함.

이러한 기본동작은 효율적이지만, 리스트 렌더링 출력이 자식 컴포넌트 상태 또는 임시 DOM 상태에 의존하지 않는 경우에만 유효함.

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

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

<template v-for>를 사용할 때 key<template> 컨테이너에 있어야 함.

반복되는 DOM 컨텐츠가 단순하거나(컴포넌트도 없고, 상태를 가지는 DOM 엘리먼트도 없을 떄), 의도적으로 기본 리스트 렌더링 동작을 통해 성능 향상을 꾀하는 경우가 아니라면, 가능한 한 언제나 v-forkey 속성과 함께 사용하는 것을 권장함.

key에는 문자열, 숫자, 심볼 형식의 값만 바인딩 해야함.

컴포넌트에 v-for 사용하기

컴포넌트에 v-for를 직접 사용할 수 있음(key 사용 잊지 말기.)

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

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

<MyComponent
  v-for="(item, index) in items"
  :item="item"
  :index="index"
  :key="item.id"
/>

컴포넌트에 item이 자동으로 전달되지 않는 이유는, v-for를 사용해야만 컴포넌트 사용이 가능하도록 의존관계가 되기 때문. 반면, 데이터를 명시해서 전달하는 방법은 v-for를 사용하지 않는 다른 상황에서도 컴포넌트가 데이터 전달 기능을 재사용할 수 있기 때문임.

배열 변경 감지

수정 메서드

Vue는 반응형 배열의 변경 메서드가 호출 되는 것을 감지하며, 필요한 업데이트를 발생시킴. 지원하는 변경 메서드 목록은

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

배열 교체

수정 메서드는 원래 배열을 수정함. 이에 비해 수정이 아닌 방법도 있음.
filter(), concat()slice()는 원본 배영을 수정하지 않고 항상 새 배열을 반환함.
이러한 방법으로 작업하는 경우, 이전 배열을 새 배열로 교체해야함.

// `items`는 값이 있는 배열의 ref라고 가정된 경우
items.value = items.value.filter((item) => item.message.match(/Foo/))

필터링/정렬 결과 표시

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

const numbers = ref([1, 2, 3, 4, 5])

const evenNumbers = computed(() => {
  return numbers.value.filter((n) => n % 2 === 0)
})
<li v-for="n in evenNumbers">{{ n }}</li>
profile
프론트엔드 개발자

0개의 댓글