Vue

지니씨·2023년 7월 27일
0

프론트엔드

목록 보기
78/83

성능

Vue.js devtools

참고

정리

  • create-vue로 생성된 공식 설정
  • setup() 함수
    • 컴포넌트 인스턴스가 생성되기 전 실행
    • 컴포넌트의 상태(data), 메서드(methods), 생명주기 훅(lifecycle hooks) 등을 정의하는 역할
  • 컴포넌트 생명주기 훅 (onMounted(), onUpdated(), onUnmounted())
    • 런타임 렌더러는 Vnode(가상돔) 트리를 탐색하고 이 트리에서 실제 DOM트리를 구성, 이 과정을 마운트(mount)라 함
  • Reactive State (반응형 상태)
    • ref(), reactvie() 함수를 사용하여 상태를 정의하면 해당 상태가 변경될 때 화면이 자동으로 업데이트 됨
  • 컴포넌트 통신방식
    • 뷰 컴포넌트는 각각 고유한 데이터 유효 범위를 가짐
    • props: 부모컴포넌트 -> 자식컴포넌트
    • emit: 자식컴포넌트 -> 부모컴포넌트
    • v-model: 양방향 바인딩
    • provide, inject: 부모 -> 자식(깊이 상관 없음)

props

emit

  • 참고
  • 참고
  • 자식 : 카멜케이스로 이벤트 발신
  • 부모 : 케밥케이스로 이벤트 수신
  • defineEmits : Vue 3의 Composition API에서 컴포넌트가 부모 컴포넌트에게 이벤트를 발생시킬 수 있게 해주는 Vue 내장 APIs 함수

디렉티브

  • v-if: 조건부 렌더링에서 사용, 조건에 따라 컴포넌트가 실제로 제거되고 생성 됨
  • v-show: 조건부 렌더링에서 사용, css 의 display 속성만 변경
  • v-for
    • Vue는 메모리를 최적화 하기위해 DOM을 재사용함, 바뀌어야할 부분만 처리하고 DOM 재사용
    • key
      • index를 key 값으로 넣는건 권장하지 않음
      • index는 요소에 속한 값이 아니라 렌더링에 따라 바뀌는 가변성 잇는 값(데이터 삭제가 발생하는 경우 index값이 변경 됨)
      • 객체와 배열과 같이 기본 타입이 아닌 값을 key로 사용해서는 안 됨

slot

v-model vs project&inject

  • v-model
    • <input>, <textarea>, <select> 요소에서만 사용 가능
    • 양방향 데이터 바인딩을 위해 사용되며, 부모와 자식 컴포넌트 간의 상태를 공유하는 데 적합
    • 주로 사용자 지정 컴포넌트 내에서 컴포넌트의 상태나 데이터를 부모 컴포넌트와 공유하고 싶을 때 v-model을 사용
    • 되도록 단일값만 사용
  • project & inject
    • provide와 inject는 데이터 공유를 위해 사용되며
    • 주로 컴포넌트 트리의 다른 부분에 데이터를 제공하고 공유하는 데 적합

컴포저블

  • Vue 컴포지션 API를 활용하여 상태 저장 로직를 캡슐화하고 재사용하는 함수
  • 컴포저블로 비즈니스 로직 분리
  • 순수 로직 재사용 시 컴포저블 사용, 로직과 시각적 레이아웃 모두 재사용할 때 컴포넌트 사용
  • reactive() 대신 ref() 사용
    • ref 객체를 반환하여 반응성을 유지하면서 컴포넌트에서 구조화할 수 있도록 도움
    • 만약 ref가 아닌 reactive를 사용한다면 return 에서 toRefs로 감싸줘야 반응성이 유지 됨
  • 컴포저블에서 사이드이펙트(ex. DOM 이벤트 리스너 추가, 데이터 가져오기) 수행하는건 괜찮지만
    • SSR에서는 DOM 관련 사이드 이펙트는 onMounted에서 수행해야 함
    • onUnmounted()에서 사이드이펙트 마무리해야 함
  • 컴포지션 함수 구현 시 흔한 실수
    • 컴포저블 문맥 밖의 사이드 이펙트 발생
      : 컴포저블에서 provide, inject 사용 (디버깅 어려움)
      : 컴포저블 내에서 템플릿 참조 (가독성 떨어짐)
      : 바닐라 DOM 조작 (Vue 전반적으로 X 지만 특히 컴포지션에서는 더더욱 X)
      : 컴포저블에서 글로벌 상태 (ex. vuex, pinia) 수정
    • 컴포저블로 데이터를 전달할 때 반응성 손실
      : 핵심적인 구성 기능인 반응성에 대한 이해 부족
    • 컴포저블 내부에 래핑된 라이브러리 만들기
      : 불필요하게 컴포저블로 구현하면 복잡도만 높아짐 (바닐라 자바스크립트 export 구문 활용)
  • 컴포저블 함수로 props를 인자로 넘기기
    • https://stackoverflow.com/questions/72408463/use-props-in-composables-vue3
    • 컴포저블 함수로 props 를 인자로 넘길 때, 반응성을 유지하려면 getter 나 toRef 사용 (참고)
    • 전체 props object 를 넘기면 반응성은 유지되지만, 추천하는 방법은 아님 (컴포저블이 모든 속성 필요하지 않음, 모든 속성이 필요하다면 컴포저블을 더 작은 단위로 쪼개라)

shallowRef vs markRaw

  • 특정 데이터를 일회성이나 정적인 데이터로 취급하고자 할 때 유용
  • shallowRef
    • 주어진 값에 대한 얕은(Shallow) 리액티브 참조 생성
    • 해당 객체의 내부 속성이 변경되어도 해당 내부 속성에 대한 반응이 일어나지 않음 (.value 접근 까지만 반응형)
    • 주로 객체나 배열과 같은 복합 데이터 구조를 다룰 때 사용
    • 내부 깊은 곳까지의 변환은 피하고 루트 수준에서만 반응형을 유지하려면, shallowReactive()를 사용
  • markRaw
    • 주어진 값을 리액티브에서 제외
    • 특정 객체나 값이 리액티브 시스템에 의해 관찰되지 않도록 하려는 경우 유용

일반변수 vs ref vs reactive

  • props 로 넘겨받을 때 UI 변경이 필요하지만, 컴포넌트 코드 내에서는 조작하지 않는 경우? 일반변수?
    template 에 사용되는 속성이면 computed? ❗️ING

  • 일반변수

    • 데이터가 변경되더라도 UI나 다른 부분에 영향을 주지 않는 경우 일반 변수 사용
    • 반응성 시스템을 이용하지 않기 때문에 성능상의 이점도 있음
    • 외부에서 데이터가 변경되며, 컴포넌트 내부에서 직접 변경하지 않는 경우에는 props 또는 computed 사용
  • 자바스크립트의 프록시

    • JavaScript의 Proxy 객체는 객체에 대한 작업을 가로채고, 필요에 따라 이러한 작업을 조작하는 기능을 제공
      (Vue.js 반응성 시스템의 핵심)
    • 객체에 새로운 속성을 추가하거나 기존 속성을 변경할 때, 이를 감지하고 필요한 반응을 트리거할 수 있음
      (변수를 저장하고 해당 변수의 값을 읽고 실행할 때마다 동작을 추가할 수 있음)
  • ref

    • Vue.js 에서 제공하는 RefImpl 객체를 사용하여 원시값(ex. 문자열, 숫자) 및 객체에 반응성 부여
    • 어떤 타입의 값이든 받을 수 있음
    • 반응성이 있는 값을 접근할 때 .value 사용
    • 템플릿에서는 .value 없이 바로 접근
  • reactvie

    • 자바스크립트의 Proxy객체를 사용하여 "객체"나 "배열"에 반응성 부여
    • 재할당 시 반응성이 사라지는 문제를 해결하기 위해 toRef 사용
  • toRef, toRefs

    • toRefreactive 객체 내의 특정 속성을 ref로 변환
    • toRefs reactive 객체의 모든 속성을 각각 ref로 변환
    • 이 방법을 사용하면 reactive 객체의 속성들을 개별적인 ref로 관리할 수 있으며, 이들은 원본 reactive 객체와 동기화된 상태를 유지 함
    // 기존 reactive의 문제점
    const words = reactive({a:"a",b:"b"})
    const msg = words.a // a = 1 (반응형 풀림)
    
    // toref
    reactive의 데이터중 하나를 ref로 만들기 위해
    const words = reactive({a:"1",b:"2"})
    const msg = toRef(words, 'a') // a = 1 (반응형 있음)
    
    // torefs
    reactive의 데이터들을 각각 ref로 만들기 위해
    const words = reactive({a:"1",b:"2"})
    const {a,b} = toRefs(words) // a = 1 (반응형 있음)
  • 만약 제대로 반응성이 동작하지 않는다면 객체 타입 확인 후 toRef 를 사용해 ProxyRefImpl로 변경해보자

  • 참고 : https://wallydev.com/posts/3f8abc32-3f4b-417e-a1a2-fd38eb5cc0b1

일반함수 vs computed vs watch vs watchEffect

  • Vue 컴포넌트의 template에서 사용되는 속성은 일반 변수로 선언하면 안 됨, template에서 사용되는 속성은 Vue의 반응성 시스템에 포함되어야 하므로, 이러한 속성은 반응형 데이터로 선언해야 함. 그렇지 않으면, 데이터가 변경되었을 때 Vue가 이를 감지하지 못해 UI가 업데이트되지 않음
  • 일반함수(메서드)
    • 함수 호출하는대로 실행
    • 계산 결과 캐싱 X
    • 특정 기능별로 묶을 수 있는 자바스크립트 함수를 의미
    • 메서드는 흔히 뷰 템플릿의 버튼 이벤트 처리부터 HTTP 통신까지 다양한 성격의 코드로 구성됨
  • computed()
    • Computed Properties, 계산된 속성
    • 계산 결과 캐싱 O (중복호출 방지)
    • 기본적으로 readonly, readonly 로 사용하자
    • 의존하는 상태가 변경될 때마다 자동으로 다시 계산(연결된 값이 변경되면 자동으로 새로운 값 계산), 화면에 반영
    • 기존에 정의된 데이터 값을 기반으로 새로운 데이터 값을 활용하기 위해 사용
    • 주의
      • 컴퓨티드 속성은 인자를 받지 않는다.
      • HTTP 통신과 같이 컴퓨팅 리소스가 많이 필요한 로직을 정의하지 않는다. (템플릿 코드의 가독성을 위한 기능으로 HTTP 통신과 같이 브라우저 리소스가 많이 할애되는 코드들은 watch나 methods에 넣는 것이 적합)
    • 다음 경우에는 일반 변수 사용
      • 변수의 값이 한 번 설정되고 그대로 유지되는 경우
      • 상태 변화에 따라 화면이 다시 렌더링되지 않는 경우
  • watch(), watchEffect()
    • watch 속성은 특정 데이터의 변화를 감지하여 자동으로 특정 로직을 수행해주는 속성
    • computed, watch 둘 다 Vue 인스턴스 내에 정의된 데이터 값에 변경이 일어나는지를 감시하고 변경될 때마다 정의된 함수 실행
    • watch
      • watch에 정의된 데이터 값 하나만을 감시하기 위한 용도
      • reactive value(ref, reactive, computed)가 변경될 때마다 특정 함수(사이드 이펙트를 포함한 함수)를 실행하고 싶다면 watch 사용
    • 실제 데이터 변경이 일어나기 전까지는 실행 X, 초기에 실행되지 X, 초기에도 실행되길 원하면 옵션 추가
    • watch는 computed의 대안이 아님, 다음 경우에는 computed 사용
      • watch 함수 내에서 다른 reactive property의 값을 설정하는 경우
      • watch 함수 내에서 무언가를 반환하는 경우
    • watch vs watchEffect
      • watch는 이전값과 변경된 값을 알 수 있지만, watchEffect는 알 수 없음
      • watch는 감시 대상을 지정해야하지만, watchEffect는 함수 안에서 참조되는 변수만 감시(computed와 유사)
      • watchEffect는 데이터의 변화를 추적하는 것이기 때문에, slots의 내부 구조 변경에는 반응하지 않을 수 있음
      • watch와 다르게 watchEffect는 "즉시" 실행

nextTick

  • Vue3에서 비동기적으로 DOM 업데이트가 완료된 후 콜백을 실행하기 위해 사용되는 유틸리티 함수
  • DOM 업데이트 후 코드 실행, 렌더링 완료 후 작업 예약

애니메이션

Transition

  • 엘리먼트 또는 컴포넌트가 DOM에 들어오고 나갈 때 애니메이션을 적용하기 위한 빌트인 컴포넌트
  • 아래 항목 중 하나의 조건이 충족하면 발생
    • v-if 조건부 렌더링
    • v-show 조건부 표시
    • <component>를 통해 전환되는 동적 컴포넌트
    • key라는 특수한 속성 값 변경 중
  • 트랜지션 클래스
  • name prop을 통해 이름 지정 가능
  • JavaScript 훅
    -재사용 가능한 트랜지션
    • <Transition> 컴포넌트 래핑, name 속성 활용
    • 슬롯 컨텐츠 전달

TransitionGroup

애니메이션 기법

https://ko.vuejs.org/guide/extras/animation

Teleport

  • <Teleport> : Modal 컴포넌트 같은 경우에서 활용
<button @click="open = true">모달 열기</button>

<Teleport to="body" :disabled="isMobile">
  <div v-if="open" class="modal">
    <p>짜자잔~ 모달입니다!</p>
    <button @click="open = false">닫기</button>
  </div>
</Teleport>




Vue3 공식문서

https://v3-docs.vuejs-korea.org/guide/introduction.html

컴포지션 API

<script setup>
import { ref, onMounted } from 'vue'

// 반응적인 상태의 속성
const count = ref(0)

// 속성 값을 변경하고 업데이트 할 수 있는 함수.
function increment() {
  count.value++
}

// 생명 주기 훅
onMounted(() => {
  console.log(`숫자 세기의 초기값은 ${ count.value } 입니다.`)
})
</script>

<template>
  <button @click="increment">숫자 세기: {{ count }}</button>
</template>

핵심 가이드

반응형 기초

  • 반응 상태를 변경하면 DOM이 자동으로 업데이트됨
  • 반응 상태를 변경하면 DOM이 자동으로 업데이트되지만 DOM 업데이트는 동기적으로 적용되지 않음
  • 상태 변경 후, DOM 업데이트가 완료될 때까지 기다리려면 nextTick() 전역 API 사용 가능
    : Vue 에서 데이터를 수정하면 DOM에 바로 업데이트 되는 것이 아니라 다음 이벤트 루프가 시작 될때까지 버퍼에 저장됨, nextTick 은 데이터 변경 후 DOM까지 업데이트가 완료된 후 인자로 전달받은 콜백함수 실행
  • reactive()
    : 객체 또는 배열을 반응형으로 만들 수 있음
    : 반환 값은 원본 객체와 같지 않고, 원본 객체를 재정의한 프락시(Proxy)
    : Vue의 반응형 시스템 작업 시 가장 좋은 방법은 상태를 재정의한 프락시만 사용 (원본 객체를 변경해도 업데이트 트리거 X)
    : 제한 사항 1. 객체, 배열, 컬렉션 유형(Map, Set 같은)에만 동작 (string, number, boolean 과 같은 기본형에서 사용 X)
    : 제한 사항 2. 객체를 쉽게 교체하지 못함 (반응형 객체의 속성을 로컬 변수에 할당, 분해 할당, 함수에 전달하면 반응형 연결 끊어짐)
  • ref()
    : reactive()의 제한사항 해결하기 위함
    : 받은 인자를 .value 속성을 포함하는 ref 객체에 래핑 후 반환 (template에서는 자동으로 언래핑되어 .value없이 사용 가능)

계산된 속성

  • 반응형 데이터를 포함하는 복잡한 논리의 경우 계산된 속성 사용
  • 캐싱, 계산된 속성은 기본적으로 getter 전용 / 수정 가능한 계산된 속성이 필요한 경우 getter 와 setter 모두 제공하여 속성 만들 수 있음

Form 입력 바인딩

  • v-model은 모든 폼 엘리먼트에서 감지되는 value, checked, selected 속성 값을 무시한다. reactivity API를 사용하여 JavaScript에서 초기 값을 선언해야 한다.
  • IME가 필요한 언어(중국어, 일본어, 한국어 둥)의 경우 IME 구성 중에 v-model이 업데이트되지 않는 것을 알 수 있다. 이러한 업데이트에도 응답하려면 v-model 대신 직접 input 이벤트리스너와 value 바인딩을 사용해 기능을 구성해야 한다. ??
  • select 엘리먼트가 선택되지 않은 상태로 렌더링되면 ios에서는 변경 이벤트를 발생시키지 않아 사용자가 첫 번째 항목을 선택할 수 없게 된다. 예시처럼 비활성화된 옵션에 빈 값을 제공하는 것이 좋다.
  • *.* 수정자

생명 주기 훅
아래 세 생명 주기가 가장 많이 사용 됨

  • onMounted (컴포넌트 초기 렌더링, DOM 노드 생성이 완료된 후 코드 실행)
  • onUpdated
  • onUnmounted

감시자

  • computed(), method로 대부분 커버?
  • 상태 변경에 대한 반응으로 사이드이펙트(ex. DOM변경, 비동기 작업의 결과를 기반으로 다른 상태 변경 등)를 수행해야 하는 경우 Watchers 활용
  • wtach()
    : 반응형 속성이 변경될 때마다 함수 실행 (source가 변경된 뒤에 콜백 호출)
    : Deep Watchers (감시된 객체의 모든 중첩 속성을 탐색하기에 성능에 영향을 주는지 고려해서 사용해야 함)
    : Eager Watchers (immediate:true옵션을 전달하면 워처의 콜백이 즉시 실행된 다음 source가 변경되면 다시 실행 됨)
  • watchEffect()
    : ex. 초기 데이터를 가져온 다음 관련 상태가 변경될 때마다 데이터를 다시 가져오길 원할 때
    : // immediate: true 옵션 설정시와는 뭐가 다른거지?
  • watch()는 명시적으로 감시된 소스(첫번째 인자)만 추적, 콜백 내에서 조회하는 항목은 추적X, 소스가 실제로 변경된 경우에만 콜백 실행
  • watchEffect()는 동기적(sync) 실행중 조회되는 모든 반응형 속성 자동으로 추적, 콜백 실행되어야 하는 시기가 덜 명시적
  • 개발자가 생성한 워쳐 콜백은 Vue 컴포넌트가 업데이트되기 전에 실행 됨, 워쳐 콜백 내에서 DOM 접근하면 DOM이 Vue에 의해 업데이트되기 전 상태임, 워쳐 콜백에서 Vue에 의해 업데이트된 후의 DOM을 접근하려면 flush:'post' 옵션 지정해줘야 함.
  • watchPostEffect()
    : flush: 'post' 옵션이 적용된 watchEffect()
    : flush: 'pre'|'post' 옵션은 콜백을 버퍼링하여, 동일한 "틱(tick)"에서 여러 번 상태 변경이 되더라도 마지막에 한 번만 호출
  • setup() 또는 <script setup> 내부에서 동기적으로 선언된 감시자는 해당 컴포넌트 인스턴스에 바인딩되며, 해당 컴포넌트가 마운트 해제되면 자동으로 중지 된다. 대부분의 경우 감시자 해제에 대해 고민할 필요가 없다. 감시자가 비동기 콜백에서 생성되는 겨우 감시자는 컴포넌트에 바인딩되지 않으며 누수를 방지하기 위해 수동으로 중지해줘야하지만, 감시자를 비동기식으로 생성하는 경우는 거의 없고 가능하면 동기식 생성을 해야한다.

템플릿 참조

  • Vue의 선언적 렌더링 모델은 개발자가 직접 DOM에 접근 해야하는 대부분을 추상화하지만, 개발자가 DOM 엘리먼트에 직접 접근해야 하는 경우가 여전히 있을 수 있다. 이럴때 ref를 사용한다.

컴포넌트 기초

  • 싱글 파일 컴포넌트(SFC)라고 하는 .vue 확장자를 사용하는 전용 파일에 각 Vue 컴포넌트 정의
  • props는 컴포넌트에 등록할 수 있는 사용자 정의 속성
  • 이벤트 청취 ... ?
  • slot
  • 동적 컴포넌트 (ex. tab)
  • DOM 템플릿 작성시 주의 사항
    : JS에서 camelCase, HTML에서 kebab-case
    : <MyComponent/>, <my-component></my-component>
    : ul, ol, table, select와 같은 일부 HTML 엘리먼트에는 내부에 표시할 수 있는 엘리먼트에 대한 제한이 있음 / li, tr, option 과 같은 일부 엘리먼트는 특정 다른 엘리먼트 내부에만 사용할 수 있음 => is속성으로 해결

컴포넌트 심화

  • <PascalCase />로 컴포넌트 등록, template 태그 내에서는 <kebab-case></kebab-case> 로 사용
  • props: 부모 -> 자식
  • emit: 자식 -> 부모
    : 네이티브 이벤트(예: click)가 emits 옵션에 정의된 경우 리스너는 이제 컴포넌트에서 발생하는 click 이벤트만 수신 대기하고 네이티브 click 이벤트에 더 이상 응답하지 않습니다.

v-model: 양방향 바인딩

<!-- 방법1. CustomInput.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>

<template>
  <input
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>

<!-- 방법2. CustomInput.vue -->
<script setup>
import { computed } from 'vue'

const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

const value = computed({
  get() {
    return props.modelValue
  },
  set(value) {
    emit('update:modelValue', value)
  }
})
</script>

<template>
  <input v-model="value" />
</template>
<!-- App.vue -->
<CustomInput v-model="searchText" />

폴스루(fallthrough:대체) 속성 : class, style, id ?

슬롯(slot)
: <slot> 태그 사이에 배치하면 대체 컨텐츠
: 슬롯에 이름 지정 가능 v-slot 의 전용 단축 문법은 #
: 슬롯 컨텐츠는 자식 컴포넌트의 상태에 접근 불가능 / 슬롯에 props 전달 가능
: 범위가 지정된 슬롯?

Provide(제공) / Inject(주입)
: 하위 트리의 모든 컴포넌트는 깊이에 관계없이 상위 체인의 컴포넌트에서 제공(provide)하는 의존성을 주입(inject)할 수 있다.
: provide(Key:String|Symbol, value:refs와 같은 반응 상태를 포함한 모든 유형) / 주입 대상 컴포넌트에서 데이터 업데이트해아 하는 경우, 상태 변경을 담당하는 함수까지 함께 제공 / 주입된 컴포넌트에 의해 데이터가 변경될수 없도록 하려면, 제공된 값을 readonly()로 래핑 / 심볼키 사용하는게 좋음
: inject(Key, 기본값)

defineAsyncComponent(비동기 컴포넌트)

  • Promise를 반환하는 로더 함수
  • ES 모듈 동적으로 가져오기도 Promise를 반환하므로, 대부분의 경우 defineAsyncComponent와 함께 사용합니다.

TypeScript

  • 개발 환경
    : Vite 기반 설정을 사용하면 개발 서버와 번들러가 트랜스파일만 수행, 타입 검사 X (타입스크립트를 사용하는 경우에도 Vite dev 서버가 빠른 속도로 유지됨)
    : Volar (Vue2용 공식 VSCode extension인 Vetur 대체, Vetur 설치되어 있는 경우 Vue3 프로젝트에서 비활성화)
    : Vue CLI 타입스크립트 지원 하지만 권장 X

  • 일반 사용
    : defineComponent()를 사용해 컴포넌트를 정의해야 타입스크립트가 컴포넌트 옵션 내에서 타입을 올바르게 추론할 수 있음
    : <script>태그에 lang="ts"속성 추가

  • 컴포넌트 Props 작성 : defineProps

// 타입 기반 선언(type-based declaration)
export interface Props {
  msg?: string
  labels?: string[]
}

// withDefaults로 프로퍼티의 기본값을 선언
const props = withDefaults(defineProps<Props>(), {
  msg: 'hello',
  labels: () => ['one', 'two']
})
  • 컴포넌트 Emits 작성 : defineEmits
  • ref(), reactive(), computed() 타입 적용
  • 이벤트 핸들러 타입 지정
  • Provide, Inject 타입 지정 ?
  • 템플릿 Refs 타입 지정
  • 컴포넌트 템플릿 Template Refs에 타입 지정
<!-- App.vue -->
<script setup lang="ts">
import MyModal from './MyModal.vue'

const modal = ref<InstanceType<typeof MyModal> | null>(null)

const openModal = () => {
  modal.value?.open()
}
</script>

API (Composition API, 빌트인)

Composition API

  • 컴포넌트의 setup옵션 사용법, 싱글 파일 컴포넌트(*.vue)에서 컴포지션 API 사용하는 경우 <script setup> 사용 권장
  • ref()
  • computed()
    : getter 함수를 사용하며 getter로부터 반환된 값을 읽기 전용 반응형 ref객체로 반환한다.
    : getset 함수가 있는 객체를 사용하면 쓰기가 가능한 ref 객체를 반환한다.

빌트인

싱글 파일 컴포넌트

  • <script setup>
  • <script setup>은 싱글 파일 컴포넌트(SFC) 내에서 컴포지션 API를 더 쉽게 읽거나 사용하기 위한 컴파일 타임 문법
  • defineExpose

재사용성 (컴포저블, )

컴포저블

  • Vue 컴포지션 API를 활용하여 "상태 저장 로직"을 캡슐화하고 재사용하는 함수
  • 중복 로직 및 코드 재사용 & 체계화를 위해 추출
  • useCamelCase : 함수 이름 관례
  • 컴포저블에서는 ref 객체를 반환하여 반응성을 유지하면서 컴포넌트 구조화 하는게 좋음, reactive() 대신 ref()를 독점적으로 사용, 항상 ref 객체 반환하기
  • Vue 2에서 사용하던 mixins 옵션 사용 지양
  • 순수 로직 재사용 시 컴포저블 사용, 로직과 시각적 레이아웃 모두 재사용할 때 컴포넌트 사용
  • 다른 개발자가 사용할 수 있는 컴포저블 작성 시, 입력 인수가 원시 값 대신 ref나 getter인 경우 처리하는게 좋음

플러그인

  • Vue에 앱 레벨 기능을 추가하는 자체 코드
  • install() 메서드에서 정의, app.use에서 사용
  • ex. i18nPlugin

// 커스텀 디렉티브

  • 일반적으로 컴포넌트에 커스텀 디렉티브를 사용하는 것은 권장 X
  • 원하는 기능을 직접 DOM 조작을 통해서만 달성할 수 있는 경우에만 사용, 가능하면 v-bind와 같은 내장 디렉티브를 사용하여 선언적 템플릿 사용하는 것이 더 효율적이고 서버 렌더링에 친숙
  • mounted, updated에 대해 동일한 동작을 갖는 것이 일반적
  • ex. v-focus

확장하기

상태관리

  • 여러 뷰가 동일한 상태에 따라 달라질 수 있습니다.
    : 공유 상태를 공통 조상 컴포넌트로 "끌어올린" 다음 props로 전달
  • 서로 다른 뷰의 기능이 동일한 상태를 변경시킬 필요가 있을 수 있습니다.
    : 템플릿 refs를 통해 직접적인 부모/자식 인스턴스에 도달하거나, 발송(emit)된 이벤트를 통해 상태의 여러 복사본을 변경 및 동기화하려는 것과 같은 솔루션에 의존하는 경우가 많습니다. 이러한 패턴은 모두 깨지기 쉽고 빠르게 유지 관리할 수 없는 코드로 이어집니다.
  • Pinia (Vuex대체하는 공식 상태관리 라이브러리)

테스팅

  • 테스트 유형 : 단위, 컴포넌트, End-toEnd
  • 단위 테스트
    : 함수, 클래스, 컴포저블이 의도한 입출력과 사이드이펙트를 생성하는지
    : 일반적으로 UI 렌더링, 네트워크 요청 또는 기타 환경 문제를 포함하지 않는 자체적으로 해야 할 일에 대한 논리: , 컴포넌트, 클래스, 모듈 또는 함수에 적용
    : 일반적으로 Vue와 관련이 없는 일반 JavaScript/TypeScript 모듈
    : Vue 관련 기능의 단위 테스트1 - 컴포저블 테스트 ()
    : Vue 관련 기능의 단위 테스트1 - 컴포넌트 (Whitebox, Blackbox)
    : vitest(추천), peeky, Jest
  • 컴포넌트 테스트
    : 마운트, 렌더링, 상호작용 의도대로 작동 확인
    (컴포넌트의 props, 이벤트, 제공하는 슬롯, 스타일, 클래스, 생명 주기 훅 등과 관련된 문제 포착)
    : 각 Vue 컴포넌트에는 자체 스팩 파일이 있는것이 좋음
    : 내부 구현 세부 사항보다는 컴포넌트의 공개 인터페이스에 중점(이벤트 발생, props, 슬롯)
    : "컴포넌트가 어떻게 작동하는지가 아니라, 어떤 작동을 하는지" 테스트
    : Vitest(헤드리스로 렌더링되는 컴포넌트), @testing-library/vue(컴포넌트와 DOM), @testing-library/cypress(예상 동작이 스타일을 올바르게 렌더링하거나 기본 DOM 이벤트를 트리거하는 데 의존하는 컴포넌트), @uve/test-utils(Vue 전용 내부 테스트가 필요한 고급 컴포넌트를 빌드하는 경우에만 사용 권장) / Nightwatch(Vue 컴포넌트 테스트를 지원하는 E2E 테스트 러너)
  • E2E 테스트
    : 여러 페이지에 걸쳐 있는 기능 확인, 네트워크 요청 등

튜토리얼

  • Vue SFC(Single File Component)
    : HTML, CSS, JavaScript를 캡슐화한 코드 블록으로 재사용 가능한 .vue 파일
  • reactive(), ref(), {{}}
  • 속성 바인딩
    : v-bind 디렉티브 (:)
    : 바인딩된 값이 null 또는 undefined이면 엘리먼트의 속성이 제거된 상태로 렌더링 됩니다.
    : 인자없는 v-bind는 객체의 모든 속성을 대상 엘리먼트의 속성으로 묶음
  • 이벤트 리스터 : v-on (@)
  • 폼 바인딩 : `v-model₩
  • 조건부 바인딩 : v-if, v-else, `v-else-if₩
  • 리스트 렌더링 : v-for, :key
  • computed() : 반응 데이터 소스를 기반으로 .value를 계산하는 계산된 참조(ref)를 만듬
  • 템플릿 참조(ref), 컴포넌트 생명주기 훅(onMounted(), onUpdated(), onUnmounted())
  • 감시자
  • 부모 -> 자식 props (defineProps에 선언) / 자식 -> 부모 emit (defineEmits에 선언) / 부모 -> 자식 slot컴포넌트

참고

  • v-show는 초기 렌더링 비용이 더 높음, 매우 자주 전환해야 하는 경우 v-show, 실행 중 조건이 변경되지 않는 경우 v-if 사용
  • v-ifv-for함께 사용하면 v-if가 먼저 평가되기때문에 함께 사용하는 것은 권장 X


강의

command + shift + p
file icon > material icon
color theme > night owl

State of JS
https://2022.stateofjs.com/en-US/

State of Vue.js
https://www.monterail.com/state-of-vue-2021-report
Ease of integration : 점진적인 적용이 쉬움

[ Vue.js 라이브러리 생태계 ]

공식 라이브러리

  • Vue Core - 화면 렌더링 및 DOM 조작 API
  • Vue Router - 라우팅
  • Vuex - 상태 관리
  • Vue CLI - 프로젝트 설정 도구
  • Vue Devtools - 디버깅 도구
  • Nuxt - 서버 사이드 렌더링
  • Vue Test Utils - 테스트 도구

기타

  • 코딩컨벤션 - Style Guide
  • Vetur, Prettier, ESLint 와 함께

[ Vue.js 개념 ]

MVVM패턴과 Reactivity

Object.defineProperty() API를 라이브러리화 시킨게 Vue.js

[ 컴포넌트란? ]

  • 화면의 특정 영역을 재사용 가능한 형태로 구성한 코드
  • 컴포넌트에 대한 코드(HTML/JS/CSS)를 한 파일에서 관리 가능한게 SPA
// 전역 컴포넌트 - 서비스 구현시 쓸 일 없음 (컴포넌트간의 관계 모호, 재사용 한계)
Vue.component('컴포넌트 이름', 컴포넌트 내용);

// 지역 컴포넌트
new Vue({
	components: {
    	'컴포넌트 이름': 컴포넌트 내용
	}
})

Props Validation

  • 최소한 타입이라도 정의, required 까지 정의해주기

// Event Bus

  • 공식문서에서 제거 됨 / 사용 지양
  • 컴포넌트 통신의 기본 규칙을 따르지 않고 특정 컴포넌트 간에 통신하는 방법
  • 쓰고 나면 반드시 $off()로 해제. 안그러면 적체됨

컴포넌트 통신 방법 vs Event Bus

  • 위->아래 Props, 아래->위 Event Emit
  • 컴포넌트 레벨이 2단계 이하면 props, event emit 쓰는 것이 좋음
  • 토스트 팝업, 프로그레스 바와 같이 on, off 성격의 UI 컴포넌트에서는 Event Bus 활용하는 것도 좋음

Smart Watch

Computed

  • data 속성의 변화에 따라 함께 변하는 속성
  • 대부분의 케이스에서는 computed, methods로 해결 가능, watch 사용은 지양
  • watch는 데이터 호출 로직과 연관된 동작에만 사용하면서 최소한으로 사용
    ex.

템플릿 문법

  • directives : 화면의 돔 조작을 쉽게해주는 문법 (v-*)
  • 데이터 바인딩 : 뷰 인스턴스의 데이터를 화면에 연결해주는 문법

데이터 바인딩의 내부 동작 과정

Render Function

  • 개발자가 직관적인 템플릿 코드를 작성하도록 뷰 내부적으로 실행하는 함수
  • 템플릿 코드 까지 작성하면, 템플릿 변환 순서는 아래와 같음
    : Render Function 으로 변환 -> Virtual DOM 반환 -> 실제 DOM 부착

뷰 컴포넌트 스타일링 팁

  • 싱글 파일 컴포넌트의 scoped 속성 활용 (특정 컴포넌트에만 국한된 CSS코드)
  • 디자인 공통 CSS : App.vue 파일에 @import 로 적용
  • 페이지 전용 CSS : 페이지 기준 최상위 컴포넌트에 페이지 전용 공통 CSS 적용 (@import, scoped 사용)

[ Vue.js 데이터 호출 패턴 ]

axios

  • 뷰에서 가장 많이 사용되는 HTTP 통신 라이브러리
  • Promise 기반의 API 제공
  • Instance 기반의 설정 옵션 확장
  • Inteceptor를 이용한 Request, Response 전처리
  • 우수한 사용 예시 및 문서화

1. 뷰 인스턴스 라이프 사이클

  • https://vuejs.org/guide/essentials/lifecycle.html#lifecycle-diagram
  • 뷰 인스턴스가 생성되고 소멸되기까지의 생애 주기
  • 라이프 사이클 훅을 이용한 데이터 호출
    created : 컴포넌트가 생성되자마자 호출
    beforeMount 도 사용할때는 큰 차이 없음 (팀 내부적으로 규칙 정해서 사용하면 됨)

2. Router Navigation Guard

  • 뷰라우터 뷰엑스
  • nuxt asyncdata 넉스트만 가지고 있는 컴포넌트 속성 => 구현 공부
  • 뷰 라우터 : 뷰에서 페이지 이동과 관련된 API 및 속성을 제공하는 공식 라이브러리
  • 뷰 라우터 네비게이션 가드 : 특정 페이지로 진입하기 전 커스텀 로직을 반영할 수 있는 속성
  • 스토어와 라우터 네비게이션 가드를 이용한 데이터 호출
  • 스토어 - 뷰 상태 관리 라이브러리 Vuex
  • 상태란 여러 컴포넌트에서 공통으로 사용하는 data 속성
  • View -> Action -> State -> View -> Action...

[ 패턴 ]

컴포넌트 생성절차
: component 내용 작성
-> component 내용을 component 속성에 연결
-> component 태그 이름 정의
-> 태그 이름으로 사용

1. Common Approach

  • UI영역 기준으로 코드 나눠서 관리
  • 단점
    : 요구사항 변경 대응에 어려움
    : 작은 UI 변경에도 새로 컴포넌트를 생성하거나 기능 확장이 어려움

2. Component with Slots

  • 하위 컴포넌트의 표현부(태그,스타일)을 유연하게 확장할 수 있는 방법
  • slot 여러개 사용하는 방법도 있음, 2.7부터 문법 좀 바뀜
  • 마크업 구조가 변경되었다고 컴포넌트 추가할 필요 없음
  • 모달, 툴팁, 토스트 등 공통 UI 컴포넌트에 활용 / 카드, 테이블 등의 그리드 컴포넌트에도 사용 가능
  • vue create slots => 실습

3. Controlled Component

  • 상위 컴포넌트에서 하위 컴포넌트를 더 쉽게 제어할 수 있는 방법
  • 날짜 선택기, 체크박스 등의 UI 컴포넌트에서 활용도 높음

// 4. Renderless Component

  • (이런게 있다 정도만 알아둬도 될 듯)
  • UI로직을 제외하고 데이터 로직만 구분하여 관리하는 방법

5. Mixins

  • 중복되는 컴포넌트 로직을 재활용하기 위한 첫 번째 방법
  • 한 페이지에서 필요한 비지니스 로직만 남겨두고 나머지 공통 로직들은 공통으로 빼줌
  • 믹스인 사용하는 컴포넌트의 로직과 충돌나지 않게 팀내 코딩 컨벤션 선정
  • 믹스인과 컴포넌트의 로직이 충돌나면 컴포넌트의 로직이 우선순위를 가짐

// 6. High Order Component

  • (Mixins으로 충분한 듯?)
  • 중복되는 컴포넌트 로직을 재활용하기 위한 두 번째 방법
  • 데이터불러오는 로직이 list-view가 다 들고있음
  • 컴포넌트의 레벨이 깊어져서 데이터 전달이 번거로워지지 않게 주의


SSR

  • https://v3-docs.vuejs-korea.org/guide/scaling-up/ssr.html
  • Vue.js는 클라이언트측 앱을 빌드하기 위한 프레임워크, 기본적으로 브라우저에서 DOM으로 출력되는 Vue 컴포넌트를 생성하고 조작 함 but 동일한 컴포넌트를 서버의 HTML 문자열로 렌더링하여 정적 마크업을 클라이언트 브라우저로 보내는 완전한 대화형 앱으로 '하이드레이트(변환)'하는 것도 가능
  • Hydrate는 Server Side 단에서 렌더링 된 정적 페이지와 번들링된 JS파일을 클라이언트에게 보낸 뒤,
    클라이언트 단에서 HTML 코드와 Vue인 JS코드를 서로 매칭 시키는 과정
  • SSR환경에서는 클라이언트 측 하이드레이션이 완료된 후에 브라우저 관련 작업을 수행해야 함,
    이를 위해 mounted훅을 사용할 수 있음,
    mounted 훅은 컴포넌트가 클라이언트 측에서 마운트된 후에 호출되므로 이 시점에서 document.body와 같은 브라우저 API를 안전하게 사용할 수 있음

SSR 친화적인 코드 작성

  • 기본적으로 반응성은 더 나은 성능을 위해 SSR 동안 비활성화 됨
  • 동적 업데이트가 없기 때문에 onMounted 또는 onUpdated와 같은 생명 주기 훅은 SSR 중에 호출되지 않고 클라이언트에서만 실행 됨
    : setInterval 코드 같이 사이드이펙트를 유발할 수 있는 코드는 onMounted로 이동
  • 플랫폼별 API에 대한 접근
    : 브라우저 전용 API(ex. window, document 등)의 경우 onMounted와 같은 클라이언트 전용 생명 주기 훅 내에서 접근
  • 교차 요청 상태 오염 ?
    : 반영형 API를 통한 상태관리 패턴은 JavaScript 모듈의 루트 범위에서 공유 상태를 선언하는 싱글톤. 앱의 전체 생명 주기 동안 반응형 객체의 인스턴스가 하나만 존재. 우리 앱의 모듈이 각 브라우저 페이지 방문에 대해 새로 초기화되기 때문에 순수한 클라이언트 측 Vue 앱에서는 예상대로 작동하지만, SSR 컨텐스트에서 앱 모듈은 일반적으로 서버가 부팅될 때 서버에서 한 번만 초기화 됨. 동일한 모듈 인스턴스가 여러 서버 요청에서 재사용되고 싱글톤 상태 객체도 재사용 됨. 공유 싱글톤 상태를 한 사용자와 관련된 데이터로 변경하면 실수로 다른 사용자의 요청으로 유출될 수 있음
    : 각 요청에 대해 라우터 및 전역 저장소를 포함한 전체 앱의 새 인스턴스를 만들기를 권장. 컴포넌트에서 직접 가져오는 대신 앱 수준 provide를 사용하여 공유 상태를 제공하고 이를 필요로 하는 컴포넌트에 주입.
    : 상태 관리 라이브러리 사용 (ex. Pinia)
  • 개발 중에 하이드레이션 불일치 제거
    : 잘못된 HTML 중첩 구조가 브라우저의 기본 HTML 구문 분석 동작에 의해 렌더링된 HTML을 수정하는 경우
    : 렌더링 중에 사용되는 데이터에 무작위 생성값이 포함된 경우
    : 서버와 클라이언트가 서로 다른 시간대에 있는 경우
    : Vue에서 하이드레이션 불일치가 발생하면 클라이언트 측 상태와 일치하도록 사전 렌더링된 DOM을 자동으로 복구하고 조정하려고 시도 함
  • 커스텀 디렉티브 getSSRProps 디렉티브 훅
  • 텔레포트와 SSR을 함께 사용할 때 body 타겟팅 X
    : <body>에는 다른 서버 렌더링 컨텐츠가 포함되므로, Teleports가 하이드레이션을 위한 올바른 시작 위치를 결정할 수 없음,
    텔레포트된 컨텐츠만 포함하는 전용 컨테이너 방식으로 사용



Vue 생태계에서 권장되는 몇 가지 SSR 솔루션

Nuxt

  • 범Vue 생태계 위에 구축된 상위 수준 프레임워크
  • 정적 사이트 생성기로도 사용 가능
  • Nuxt는 Vue.js로 빠르게 웹을 제작할 수 있게 도와주는 프레임워크입니다. 웹 애플리케이션을 제작할 때 필요한 뷰엑스, 라우터, Axios와 같은 라이브러리들을 미리 구성하여 싱글 페이지 애플리케이션(Single Page Application), 서버 사이드 렌더링(Server Side Rendering), 정적 웹 사이트(Static Generated Website)를 쉽게 제작할 수 있습니다.
  • https://day0404.tistory.com/8
<NuxtLayout>
   <NuxtPage />
</NuxtLayout>

ClientOnly

  • Nuxt.js에서 ClientOnly 태그를 사용해 특정 컴포넌트나 코드 블록을 클라이언트 측에서만 실행되도록 지정 가능
  • 일반적으로 서버 측에서 실행할 필요가 없는 코드 또는 브라우저 API를 사용하는 코드와 같은 경우에 유용
  • ClientOnly 태그를 사용하는 것이 좋은 방향인지 여부는 프로젝트의 특정 요구사항과 사용 사례에 따라 다름
  • 일반적으로 다음과 같은 상황에서 ClientOnly 태그를 사용하는 것이 좋음
    • 브라우저 API 사용: 브라우저 API를 사용하는 코드를 실행할 때, 이것은 클라이언트 측에서만 작동하므로 ClientOnly 태그를 사용하여 이를 명시적으로 지정하는 것이 좋음
    • 성능 최적화: 서버 측에서 렌더링되는 것이 필요하지 않은 경우, ClientOnly 태그를 사용하여 해당 컴포넌트를 클라이언트 측에서만 렌더링하여 초기 로드 속도를 향상
    • 서버 사이드 렌더링에서 오류 발생: 일부 코드 블록이 서버 사이드 렌더링 도중 에러를 발생시키는 경우, 이를 회피하기 위해 ClientOnly 태그를 사용하여 해당 코드 블록을 클라이언트 측에서만 실행할 수 있음
  • 그렇다고 ClientOnly 태그 남용 X, 서버 측 렌더링의 이점을 활용하기 위해서는 가능한 한 많은 코드를 서버 측에서 실행하는 것이 이상적, ClientOnly를 사용하는 것은 코드가 반드시 클라이언트 측에서 실행되어야 하는 경우에만 사용하는 것이 중요

Quasar

  • 하나의 코드베이스를 사용하여 SPA, SSR, PWA, 모바일 앱, 데스크톱 앱 및 브라우저 확장을 모두 타겟팅할 수 있는 완전한 Vue 기반 솔루션

Vite SSR

  • Vite는 내장된 Vue 서버 사이드 렌더링 지원을 제공하지만 의도적으로 저수준
  • SSR/빌드 도구에 대한 경험이 있고 더 높은 수준의 아키텍처를 완전히 제어하려는 경우에만 권장



Router

  • 웹페이지간의 이동방법
  • 페이지를 이동할때 서버에 요청하여 새로 갱신하는 것이 아니라 미리 해당 페이지를 받아 놓고 페이지 이동시 클라이언트의 라우팅을 이용하여 화면 갱신 (이러한 방식을 SPA라고도 함)

Nuxt Routing

  • https://nuxt.com/docs/getting-started/routing
  • external속성
    • <NuxtLink>를 외부 링크로 사용할 수 있지만 권장하지않고 external 속성 사용 권장
    • <NuxtLink>는 주로 Nuxt.js의 내부 라우팅 최적화를 위해 설계 됨, 내부적으로 Vue Router의 <RouterLink>를 활용하여 페이지 로드 시 서버로의 요청 없이 클라이언트 사이드에서 뷰를 변경할 수 있게 도와줌 (내부 페이지 간의 빠른 전환과 성능 최적화에 큰 이점)
    • 외부 링크의 경우 새로운 호스트나 도메인으로 이동해야 하기 때문에 Vue Router의 라우팅 로직이 불필요, <NuxtLink>를 이용하여 외부 링크를 처리하려면 <RouterLink>가 처리할 수 없는 href와 같은 HTML 속성을 정확하게 매핑해주어야 함
    • <NuxtLink>external속성 추가 시 내부 라우팅 로직을 비활성화하고 순수한 <a>태그로서 동작(불필요한 자바스크립트 처리를 줄여 성능 최적화), 개발자가 명시적으로 내부 라우팅이 아닌 외부 URL에 대한 링크를 사용하고자 할 때 유용(명확성)

Vue Router

Test

Vitest

mount 와 shallowMount 차이
https://jh-7.tistory.com/20

profile
하루 모아 평생 🧚🏻

1개의 댓글

comment-user-thumbnail
2023년 7월 27일

많은 도움이 되었습니다, 감사합니다.

답글 달기