본문 바로가기
front/vue

[vue3 공식문서 번역]Essentials.7.List Rendering

by juniKang 2022. 4. 22.

v-for

배열에 기반을 둔 아이템의 리스트를 렌더링하기위해 v-for 지시어를 사용할 수 있다. v-for 지시어는 item in items의 형태인 특별한 구문을 필요로 하는데, items는 소스 데이터 배열이고, item은 반복되는 배열의 원소의 별칭이다.

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의 변수 범위는 다읨의 자바스크립트와 비슷하다: 

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="{ meesage } in items">
  {{ message }}
</li>

<!-- index 별칭과 함께 -->
<li v-for="({ message }, index) in items">
  {{ message }} {{ index }}
</li>

중첩된 v-for로, 범위는 또한 중첩 함수와 비슷하게 동작한다. 각각의 v-for 범위는 부모 범위에 접근한다.

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

in을 대신하는 구획문자로 of를 사용할 수 있다. iterator를 위한 자바스크립트의 구문에 좀더 가깝다:

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

 

객체와 v-for

객체의 프로퍼티를 반복하기위해  v-for를 사용할 수 있습니다.

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

프로퍼티의 이름을 위한 두번째 별칭을 제공할 수도 있습니다(키로 알려진):

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

index를 위한 별칭도 제공할 수 있습니다:

<li v-for="(value, key, index) in myObject">
  {{ index }}. {{ key }}: {{ value }}
</li>
주의
 객체를 반복할 때, 순서는 object.keys()의 정렬순서를 따른다, 자바스크립트 엔진의 실행에 따라 일관성을 보장하지 않는다.

 

범위와 v-for

v-for에 숫자를 줄 수도 있다. 이 경우에, 1...n같은 범위에 기초해서, 여러번 템플릿을 반복할것이다, 

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

여기서 n은 0대신에 1을 초기값으로 시작한다.

 

템플릿에 v-for

템플릿 v-if 와 비슷하게, 여러 원소의 블록을 렌더링하기 위해 v-for를 <template>태그와 사용할 수 있다. 예를 들면 :

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

 

v-if와 v-for

주의
암묵적 우선순위에 따라 같은 엘리먼트에 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를 옮겨서 고칠 수 있다.(더 명백하다)

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

 

key로 상태를 유지

뷰가 v-for로 렌더링된 원소들의 목록을 업데이트 할 때, 기본적으로 "in-place patch"전략을 사용한다. 만약 데이터 아이템의 순서가 변경 되었을 때, 아이템들의 순서를 매칭하기위해 돔 엘리먼트를 옮기는 대신에, 뷰는 각각의 엘리먼트를 제자리에서 패치하고, 특정 인덱스에서 렌더링되야 하는 내용을 반영합니다.

 

이 기본전략을 효율적이지만, 일시적인 돔 상태 (form input values같은) 또는 자식 컴포넌트 상태에 의존하지 않고 목록이 렌더링될때에만 적합합니다.

 

각각의 노드의 신원을 추적할 수 있도록 뷰에게 힌트를 줘서, 존재하는 엘리먼트들을 재사용하고 정렬할 수 있도록, 각각의 아이템의 특별한 key 어트리뷰트를 제공해야 한다:

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

<template v-for>를 사용할 때, 키는 <template> 컨테이너에 배치되어야 한다.

<template v-for="todo in todos" :key="todo.name">
  <li>{{ todo.name }}</li>
</template>
주의
 키는 v-bind와 바운드 되는 특별한 애트리뷰트 이다. 객체에 v-for를 사용할 떄 프로퍼티 key 변수와 혼동하지 말아야 한다.

반복하는 DOM 컨텐츠가 단순하거나(즉, 상태저장 DOM 엘리먼트나 컴포넌트를 포함하지 않는) 의도적으로 성능을 높이기 위해 기본 동작에 의존하는 경우가 아니라면, 가능한 언제든지 v-for과함께 key 어트리뷰트를 제공하는걸 추천한다.

 

key바인딩은 원시값을 넣어야 한다 - 즉, string이나 number를 넣어야 한다. v-for 키값으로 객체를 쓰지 마라. key 어트리뷰트의 상세 사용법은 key API 문서를 참고해달라.

 

컴포넌트와 v-for

// 이 섹션은 컴포넌트에 대한 지식이 있다고 가정한다. 가볍게 스킵하고 다음에 봐도 된다.

일반적인 객체와 같이 컴포넌트에 v-for를 직접 사용할 수 있다.(key를 제공하는걸 잊지마라)

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

컴포넌트로 어떤 데이터를 자동적으로 넘기지 않는데, 컴포넌트가 그들 스스로 격리된 범위를 가지고 있기 때문이다. 컴포넌트에 반복되는 데이터를 넘기기 위해, 우리는 props를 사용할 수 있다.

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

컴포넌트에 자동적으로 item을 삽입하지 않는 이유는, v-for이 어떻게 동작하는 지에 컴포넌트가 밀접한 결합력을 갖도록 하게 만들기 때문이다. 데이터가 어디서 오는지 명시하는 것은 다른 상황에 컴포넌트를 재사용가능하도록 만든다.

 

각각 인스턴스에 다른 데이터는 넘기는, v-for를 사용한 컴포넌트의 목록이 어떻게 렌더링 되는지 보고싶다면 simple todo list 예제를 살펴보라.

 

배열 변경 탐지

상태변화 메소드

뷰는 관찰되는 배열의 상태변화 메소드를 래핑해서, 뷰 업데이트를 쫒을 수 있다. 래핑 메소드는 다음과 같다:

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

배열 교체

상태변화 메소드는 이름에서 알수 있듯, 그들이 호출된 오리지널 객체를 상태변화 시킨다. 대조적으로, filter(), concat(), slice()와 같은 항상 새로운 배열을 리턴하고 오리지널 객체를 상태변화 시키지않는 non-mutating 메소드도 있다. non-mutating 메소드를 사용할 때, 새로운 배열로 사용하던 배열을 교체해줘야 한다.

// 'item' 은 배열값의 ref다
items.value = items.value.filter((item) => item.message.amtch(/Foo/))

아마 당신은 이것이 존재하는 DOM을 버리고 전체 리스트를 재렌더링 한다고 생각할지도 모른다 - 다행히, 그렇지 않다. 뷰는 최대한 돔 엘리먼트를 재사용하는 똑똑한 방법(heuristics)을 구현해서, 중복된 객체를 담고있는 다른 배열로 교체해도 매우 효율적인 수행이 된다.

 

필터링/ 정렬된 결과 표시

때로 오리지널 데이터의 실제 상태변화나 리세팅 없이 배열의 필터링된, 정렬된 버전을 보고싶은 경우가 있습니다. 이 경우에, 필터링된 정렬된 배열을 리턴하는 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>

computed 프로퍼티가 실현가능하지 않은 상황에(중첩된 v-for 루프같은), 메소드를 사용할 수 있다:

const sets = ref([
  [1, 2, 3, 4, 5],
  [6, 7, 8, 9, 10]
])

function even(numbers) {
  return numbers.filter((number) => number % 2 === 0)
}
<ul v-for="numbers in sets">
  <li v-for="n in even(numbers)">{{ n }}</li>
</ul>

computed 프로퍼티에 reverse()와 sort()를 사용하는걸 주의하라! 이 두 메소드는 오리지널 배열을 상태변화시켜서, computed getters를 피할 수 있다. 이 메소드를 호출하기 전에 오리지널 배열의 복사본을 만들어라.

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

 

 

 

 

 

댓글