개발/Vue.js

[Vue2 스터디 #2] 반응형 기초, Computed, 클래스 및 스타일 바인딩, 조건부, 목록 렌더링

TutleKing 2024. 11. 3. 22:59

반응형 기초

https://ko.vuejs.org/guide/essentials/reactivity-fundamentals.html

반응형 상태 설정

  • data 옵션을 사용하여 컴포넌트 반응형 상태 선언
    • data 에서 return 한 변수들은 this로 접근해야한다.
    • 컴포지션 API : reactive (객체일때), ref (원시값 일때 사용)
  • data에 포함하지 않고 this 에서 데이터를 선언 및 사용 할 수 있지만, 아래와 같은 warning 을 받는다.
    • Property "abc" was accessed during render but is not defined on instance.

메서드 선언

  • 컴포넌트 인스턴스에 메서드를 추가하기 위해서는 methods 옵션을 사용
    • arrow function 사용 주의 (참고2)
  • 반응 상태를 변경할 시에 DOM 업데이트가 동기적이지 않으므로, nextTick() 을 활용하여 다음 틱까지 한번만 컴포넌트가 업데이트 되도록 한다.
<script>
import { nextTick } from 'vue'

export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    async increment() {
      this.count++

      // 아직 DOM 업데이트되지 않음.
      console.log(document.getElementById('counter').textContent) // 0

      await nextTick()
      // 이제 DOM 업데이트됨.
      console.log(document.getElementById('counter').textContent) // 1
    }
  }
}
</script>

<template>
  <button id="counter" @click="increment">{{ count }}</button>
</template>

Computed

사용 이유

  • template 문법 ({{}})을 복잡하게 사용하면 유지보수성이 떨어짐
    • 이럴 경우, 자주 사용하는 계산 하는 로직을 미리 만들어 놓을 수 있음 : computed
  • 반응형 상태의 변수를 기반으로 하기 때문에, 값이 변경되면 computed의 return 값도 변경 된다.
<script>
export default {
  data() {
    return {
      author: {
        name: 'John Doe',
        books: [
          'Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - The Mystery'
        ]
      }
    }
  },
  computed: {
    // 계산된 값을 반환하는 속성
    publishedBooksMessage() {
      // `this`는 컴포넌트 인스턴스를 가리킵니다.
      return this.author.books.length > 0 ? 'Yes' : 'No'
    }
  }
}
</script>

<template>
  <p>책을 가지고 있다:</p>
  <span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
  <br>
  <p>책을 가지고 있다:</p>
<span>{{ publishedBooksMessage }}</span>
</template>

setter 사용 가능

https://velog.io/@jinsu6688/vuejs-computed

  • 필요에 의해 setter 함수 설정 가능
// 선언부
computed: {
  fullName: {
    // getter 함수
    get() {
      return this.firstName + ' ' + this.lastName
    },
    // setter 함수
    set(newValue) {
      const names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// getter 사용
{{fullName}}
// setter 사용 - method 내에서 주로 사용
this.fullName = "토란 토마토"

computed 와 method 차이

  • return 값의 차이는 없겠지만
    • 캐싱 : computed는 내부 값의 변경이 없으면 사용부에서 아무리 사용해도 값이 변하지 않음

클래스와 스타일 바인딩

HTML 클래스 바인딩

data() {
  return {
    isActive: true,
    hasError: false
  }
}
<div
  class="static"
  :class="{ active: isActive, 'text-danger': hasError }">
</div>

// 결과
<div class="static active"></div>
data() {
  return {
    classObject: {
      active: true,
      'text-danger': false
    }
  }
}
// 인라인일 필요 없음, data 단에서 다 만들고 template단에서 바인딩 되어 알아서 작동
<div :class="classObject"></div>

// 결과
<div class="active"></div>
//----------------------------------------------------------------------
// computed 로 미리 계산 해놓는 것도 가능
const classObject = computed(() => {
    return {
      active: isActive.value && !hasError.value,
      'text-danger': !isActive.value && hasError.value,
    };
});

스타일 바인딩

  • :style에서 사용하는 컨벤션은 카멜 케이스이지만 케밥 케이스도 지원함
    • fontSize <-> font-size(실제 css 사용)
data() {
  return {
    activeColor: 'red',
    fontSize: 30
  }
}
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

//----------------------------------------------------------------------
// 다중으로 값을 셋팅 가능
<div :style="{ display: ['flex', '-webkit-box', '-ms-flexbox'] }"></div>

조건부 렌더링

v-if, v-else

  • truthy 값을 반환 하는 경우에만 동작
    • falsy : false, null, undefined, 0, '' 일 경우 false
  • template 단에서도 사용가능하다는데, 나는 안됨 ㅠ
    • v-show 사용 하면 동일하게 template을 보여주기, 말기가 가능
      • v-show는 보이던 보이지 않던 무조건 렌더링 해버림
      • 그와 반대로 v-if 는 lazy 렌더링으로, true 일때만 렌더링
    • 그러므로, 화면전환이 잦다면 v-show 쓰기
<script>
export default {
  data() {
        return {
            awesome: true
        }
    }
}
</script>

<template>
  <button @click="awesome = !awesome">전환</button>

  <h1 v-if="awesome">Vue는 정말 멋지죠!</h1>
  <h1 v-else-if="awesome === false">아닌가요? 😢</h1>
  <h1 v-else>여기로 절대 못와</h1>
</template>

목록 렌더링

v-for

  • 반복문 사용
    • js 문법과 같이 in 대신 of를 사용해도 됨
<script>
export default {
  data() {
      return {
        parentMessage: 'Parent',
        items: [{ message: 'Foo' }, { message: 'Bar' }, { message: '뭐고' }, { message: '와이라노' }]
      }
    },
    methods:{
        isThird : (index)=>{
            return index === 3;
        }
    }
}
</script>

<template>
    <li v-for="(item, index) in items">
        <span v-if="isThird(index)">333333</span>
      {{ parentMessage }} - {{ index }} - {{ item.message }}
    </li>
</template>
// 결과
Parent - 0 - Foo
Parent - 1 - Bar
Parent - 2 - 뭐고
333333 Parent - 3 - 와이라노
참고 1) [JS 기초] 포인터 개념

반응형 상태의 object 가 2개 있을때, 같은 value 값을 가지고 있더라도 obj1 == obj2 를 비교하면 false가 나온다

  • 주소값이 같아지면 반응형 상태의 object는 동일한 값을 가지게 되고, 변경도 함께 된다.
<script>
export default {
 data() {
   return {
     obj1: {},
     obj2: {}
   }
 },

 mounted() {
   this.obj1.k=11; // false 도출
   this.obj2 = this.obj1; // true 도출
   this.obj2.l=1; // true 도출
 }
}
</script>

<template>
 {{obj1}},{{obj2}}, {{obj1 === obj2}}
</template>

참고 2) [JS 기초] Arrow function 과 function 의 this 사용

https://codingapple.com/unit/es6-3-arrow-function-why/?gad_source=1

  • 결론: arrow function의 this 는 사용부 바깥의 this 값을 그대로 사용
    • function 은 사용부에 따라 this의 범위가 달라짐
var 오브젝트1 = {
  함수: function () {
    console.log(this);
  },
};

오브젝트1.함수(); // 출력 : {함수: f} // 오브젝트1 이 출력

var 오브젝트1 = {
  함수: () => {
    console.log(this);
  },
};

오브젝트1.함수(); // 출력 : window

참고3) for in, of 차이

  • in : key에만 접근 가능, obj[x]로 value 접근 가능
  • of : value에 접근 가능
// in
var obj = {
  a: 1,
  b: 2,
  c: 3,
};

for (var prop in obj) {
  console.log(prop, obj[prop]); // a 1, b 2, c 3
}

// of
var iterable = [10, 20, 30];

for (var value of iterable) {
  console.log(value); // 10, 20, 30
}
반응형