본문 바로가기
Front-end/vue.js

[Vue3] Vue.js에서 자식 컴포넌트의 렌더링 순서에 대한 오해(v-for)

by sky-j 2023. 10. 19.
반응형

부모 컴포넌트

//부모 컴포넌트
<script setup="">
import {ref} from 'vue'
import Children from '@/views/Children.vue'
const data = ref()

const list = ref(['test3', 'test2', 'test1'])
const add = () => {
  list.value = [data.value, ...list.value]
}
</script>
<template>
  <div class="container">
    <input type="text" v-model="data"/>
    <button type="button" @click="add">추가</button>
    <div class="w-100" v-for="(item, index) in list" :key="index">
      <Children :index="index" :item="item"/>
    </div>
  </div>
</template>
<style></style>

자식 컴포넌트

//자식 컴포넌트
<script setup="">
import {defineProps, ref, toRefs} from 'vue'
const props = defineProps({
  item: {
    type: String,
    required: true
  },
  index: {
    type: Number,
    required: true
  }
})
const {item, index} = toRefs(props)
const testData = ref(item.value)
</script>

<template>

  <div class="w-25 border mb-2">
    {{item + '::->' + testData}}
  </div>

</template>
<style></style>

첫 번 째 이미지

처음 마운트 하는 경우 예상하는 값으로 랜더링이 되고 있다. 이때 test4를 추가해보자

(상황: list 0번째에 새로운 데이터를 추가하기 위해   list.value = [data.value, ...list.value] 로 추가했다)

두 번 째 이미지

새로 추가된 값에 경우 testData의 값이 test3으로 들어오는 게 확인 된다.

잘 살펴 보면 마지막 test1만 원하는 값이 나오고 나머지는 그전에 값과 동일한 값이 나온다.

 

이유

mount

처음 부모가 마운트 될 때 자식 컴포넌트도 마운트를 하게 된다. 이 경우에는 정상적으로 동작.

 

이후 부모에 list가 변경될 경우

mount2

test3, test2, test1을 랜더링 했던 컴포넌트는 마운트가 이미 됐으므로 데이터 변경사항만 리랜더링 및 패치가 되고

마운트 되지 않는다. list를 추가할 때 마지막 데이터의 경우 새로운 컴포넌트를 만들기 때문에 mount가 되면서 setup 변수를 설정하게 된다. 그래서 testData가 이미 마운트 됐던 3개의 컴포넌트에서는 앞전에 마운트 될때 들어간 값이 들어가 있고 새로운 컴포넌트에서는 새롭게 받은 item.name에 값이 들어가게 된 것이다.

 

해결법 : watch 사용

<script setup="">
import {defineProps, ref, toRefs, watch} from 'vue'
const props = defineProps({
  item: {
    type: String,
    required: true
  },
  index: {
    type: Number,
    required: true
  }
})
const {item, index} = toRefs(props)
const testData = ref(item.value)
watch(
  () => item, (nv, ov) => {
    testData.value = nv.value
  },
  {deep: true}
)


</script>
<template>

  <div class="w-25 border mb-2">
    {{item + '::->' + testData}}
  </div>

</template>
<style></style>

해결

감시자를 사용하여 setup에 위치한 변수를 바꿔준다면 해결할 수 있다.

 

참고: list에 첫 번 째 데이터를 삭제하는 경우에도 실제 vue에서는 마지막 컴포넌트가 삭제 된다. 단지 데이터가 변경 될 뿐이다.

반응형