programing

Vue3에서 어레이 타입의 프로펠러 재렌더링 회피

goodsources 2022. 8. 11. 22:21
반응형

Vue3에서 어레이 타입의 프로펠러 재렌더링 회피

나는 이것을 근본적인 질문이라고 생각한다.vue2/vue3 반응성의 원리에 관한 것입니다.

태그가 있는 항목(다대다)이 있다고 가정합니다.다음과 같이 vuex에 저장됩니다(normalizr 접근법 사용).

export const state = () => ({
  itemsOrder: [1, 2, ...],
  itemsByIds: { 1: { id: 1, title: 'Item 1' }, 2: { id: 2, title: 'Item 2' } ... },
  tagsByIds: { 1: { id: 1, title: 'Tag 1' }, 2: { id: 2, title: 'Tag 2'}, ... },
  itemTags: [{ itemId: 1, tagId: 1 }, ...]
})

우리의 목표는 태그가 달린 아이템을 보여주는 것이다. 가지 <ItemWithTags>항목 제목과 태그를 표시해야 하며 해당 항목에 할당된 구성 요소.그리고 - 매우 중요한 조건 - 불필요한 재렌더링을 가능한 한 피해야 합니다.되어 있는 되어 있는 ), 그의 「」(itemTags」(temTags)만이 할당되어 .<ItemWithTags>이치노

어떻게 하는 거야?

해서 '이렇게 하다'라고 할 요.state.itemTags으로부터의 <ItemWithTags>그러면 참조가 새 항목을 이 배열에 푸시할 때 모든 구성 요소를 업데이트하도록 트리거하기 때문입니다. 쉽게 하기 위해 「」라고 가정합니다.<ItemWithTags>에는 does does does to to to to to to to to to to to to to to에 대한 .$store , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,

getter를 구축합니다.

getters = {
  itemsWithTags (state) {
    return state.itemsOrder.map(id => ({
      id,
      item: state.itemsByIds[id],
      tags: state.itemTags
        .filter(itemTag => itemTag.itemId === id)
        .map(itemTag => state.tagsByIds[itemTag.tagId])
    })
  }
}

새 itemTag 관계가 생성되면 이 getter는 {id, item, tags}과(와) 같은 새 개체로 구성된 새 어레이를 구축합니다. 각 개체에는 이전과 동일한 항목에 대한 참조와 새 태그 배열이 있습니다.하나의 항목을 제외한 모든 항목의 태그 배열은 내부적으로 동일합니다. 태그 배열은 동일한 태그 개체로 구성되어 있습니다.각 엔트리의 새로운 어레이입니다.

한 번 반복해 봅시다itemsWithTags일부 상위 컴포넌트(현재는 태그 없이 항목만 표시):

<ItemWithTags
  v-for="entry in itemsWithTags"
  :key="entry.id"
  :item="entry.item"
/>

은 올바르게단, 각 엔트리에 됩니다.새로운 관계는 각 엔트리에 대해 getter를 재구축하도록 트리거합니다.<ItemWithTags> 것을 entry.item되지 않고 모든 한다.을 사용법

처음 할당된 태그 하나로 아이템을 표시할 수도 있어 재렌더링을 피할 수 있습니다.

<ItemWithTags
  v-for="entry in itemsWithTags"
  :key="entry.id"
  :item="entry.item"
  :first-tag="entry.tags.length ? entry.tags[0] : null"
/>

한 번 같은 것을 .entry.item 같은 「」entry.tags[0]이전과 같은 오브젝트여기 데모입니다.

하지만 어떻게 모든 태그를 올바른 방법으로 처리할 수 있을까요?필요한 경우:

<ItemWithTags
  v-for="entry in itemsWithTags"
  :key="entry.id"
  :item="entry.item"
  :tags="entry.tags"
/>

후 모든 가 트리거 .모든 새로운 관계가 트리거 됩니다.<ItemWithTags>갱신할 수 있습니다.가 새로운 getter를 에 발생합니다.entry.tags배열이 필요합니다.데모가 하나 더 있어요.

지금까지 제가 찾은 유일한 해결책은 JSON.stringify(마지막 데모)를 사용하여 어레이를 원시 타입으로 변환하는 것입니다만, 그것은 매우 보기 흉하고 퍼포먼스가 좋지 않기 때문에, 적절한 방법을 찾고 싶다고 생각하고 있습니다.제대로 말하자면, 우리는 게터가 있고, 어떤 의존관계는 그것을 업데이트해야 한다고 말한다.업데이트는 좀 더 선별적으로 해야 합니다.새 getter 값을 빌드하는 동안 이전 getter 값의 일부를 재사용할 수 있습니다.

그게 Vue의 계산된 속성이 작동하는 방식입니다...

업데이트는 좀 더 선별적으로 해야 합니다.새 getter 값을 빌드하는 동안 이전 getter 값의 일부를 재사용할 수 있습니다.

계산된 속성으로는 불가능합니다.유일한 선택사항은,itemsWithTags"수작업"....생성하여 상태를 유지하고 필요에 따라 선택적으로 업데이트합니다.

이 실제 퍼포먼스의 문제입니까, 아니면 마이크로 최적화입니까?모든 컴포넌트가 갱신되어도 Vue 가상 DOM 확산 알고리즘은 영향을 받는 컴포넌트의 DOM만 갱신하기 때문입니다.

또 다른 솔루션은 데이터 구조를 변경하는 것입니다.

  1. 사용하는 대신itemTags, 태그를 각 항목으로 이동합니다.{ id: 1, title: 'Item 1', tagIds: [1, 2, 5] }(물론 모든 항목에서 태그를 완전히 제거하는 것은 다소 어려워집니다.)
  2. 내부 태그를 해결합니다.<ItemWithTags>컴포넌트 자체와 계산 프로펠러 매핑tagIds태그 객체의 배열로 배열
  3. 바꾸다itemsWithTagsgetter - 부품 생성tags더 이상 필요하지 않습니다.심플하게itemsOrdered원본을 반환하는 getteritemsByIds오브젝트, 순서대로itemsOrdergetter가 새로운 오브젝트를 작성하지 않기 때문에 하나의 아이템(태그 이름 변경, 태그 추가/삭제)을 변경해도 모든 컴포넌트의 재렌더가 트리거되지 않습니다.
getters = {
  itemsOrdered(state) {
    return state.itemsOrder.map(id => state.itemsByIds[id])
  }
}

언급URL : https://stackoverflow.com/questions/66486735/avoid-re-rendering-for-array-type-prop-in-vue3

반응형