[TIL] 프론트엔드 Day 39

jo_cw·2022년 5월 15일
0

TIL

목록 보기
17/23
post-thumbnail

📚 공부한 내용

refs

기존 Javascript에서 html element에 접근하기 위해서 doucument.querySelector() 등의 메서드를 사용해왔다. 하지만 document.querySelector()는 html 문서를 처음부터 탐색하여 컴포넌트 단위로 탐색할 수 없다.
이를 위해 ref 속성과 $refs를 사용할 수 있다. html element의ref속성에 이를 식별할 수 있는 키워드를 등록하면 전역 객체인 $refs에 등록되어 this.$refs.name 과 같은 형식으로 등록해 놓은 요소에 접근이 가능하다.

동적 컴포넌트

컴포넌트의 속성을 v-bind를 이용해서 동적으로 이용할 수 있듯이 컴포넌트 또한 동적으로 변경이 가능하다. 동적 컴포넌트를 사용하기 위해서 component 태그가 사용되며, 이 태그에 is 속성으로 컴포넌트의 이름을 지정한다.
이 방식은 v-if와 유사하게 is 속성 값이 변경될 때마다 (즉, 동적 컴포넌트가 바뀔 때 마다) 기존의 컴포넌트는 unmount되고 새로운 컴포넌트가 create되는 동작이 반복되어 변경이 일어날 때의 비용이 크다.

keep-alive 태그

keep-alive 태그 안에 있는 동적 컴포넌트는 컴포넌트가 변경되어 unmount되어도 이를 캐싱하고 다시 사용할 때 create 과정을 거치지 않는다. 하지만 캐싱에도 메모리 등의 자원이 소모되므로 사용에 주의해야한다.

Slots

컴포넌트 태그 사이에 text content 또는 html element가 존재하는 경우 이 요소들은 모두 컴포넌트의 template에 존제하는 slot 태그 위치에 위치하게 된다.

v-slot

만약 태그 사이에 여러개의 요소들이 존재하고 각 요소들의 위치를 다른 곳에 위치하고 싶을 때 v-slot 디렉티브를 사용할 수 있다. v-slot="after" 속성을 가진 element는name="after" 속성을 가진 slot에 위치하게 된다. 이를 위해서 slot 태그는 name 속성을 지정해 줘야한다.

약어

v-slot 디렉티브는 #으로 대체가 가능하다. 즉, v-slot="after"#after로 표기할 수 있다.

동적 v-slot

다른 디렉티브처럼 v-slot도 대괄호로 감싸 동적으로 사용이 가능하다.
요소의 속성이 #[slotName]이라면 slotName에 저장된 값에 따라서 위치가 동적으로 바뀐다.

Plugin

만약 하나의 함수가 여러 컴포넌트에서 공통적으로 사용된다면 이를 plugin으로 만들어 사용할 수 있다.

생성

plugininstall 함수를 가진 객체 리터럴로 생성된다. install 함수는 appoption을 매개변수로 입력받으며, 이 매개변수 중 appapp.config.globalProperties에 플러그인의 이름과 동작할 함수를 key-value 쌍으로 선언한다.

// '$fetch' plugin을 생성하는 경우
export default {
  install(app, options) {
	app.config.globalProperties['$fetch'] = () => {
    	// $fetch plugin 동작 코드
    }
  }
}

등록

전역 컴포넌트의 등록을 위해서 어플리케이션 인스턴스에 component() 메서드를 사용하듯이 pluginuse() 메소드를 이용해서 등록한다.

const app = Vue.createApp(App)
app.use(fetchPlugin, {})
app.mount('#app')

사용

위의 과정을 거쳐 등록된 plugin은 component안에서 this로 바로 사용이 가능하다.

this.$fetch()

mixin

scss의 @mixin에서 유추할 수 있듯 mixin은 컴포넌트 내부의 값들이 반복될 때 코드의 중복을 줄이기 위해서 사용할 수 있다.

사용

methods, props, computed 등 컴포넌트의 옵션이 중복되는 부분을 별도의 객체 리터럴로 선언한다.
이후 mixin이 필요한 컴포넌트의 mixins 옵션에 배열 리터럴로 mixin을 추가한다.
component(), plugin()과 동일하게 mixin() 메서드로 전역 mixin을 등록할 수 있다.


// mixin 생성
const myMixin = {
  props: {
	name: {
	  type: String,
      default: ''
    }
  },
  methods: {
    duplicatedMethod() {
      // do something
    }
  }
}
// mixin 사용
export default {
  mixins: ['myMixin', 'anotherMixin']
}
      

주의점

mixin 안에 있는 옵션과 컴포넌트 안에 있는 옵션 중 이름이 같은 것이 존재한다면 컴포넌트 안에 있는 옵션이 우선권을 가진다.
(mixin의 옵션이 컴포넌트의 옵션으로 덮어 쓰여진다.)
created, mounted 등과 같은 life cycle hook의 경우 배열에 병합되어 모두 실행되며 mixin hook -> component hook 순으로 실행된다.

provide와 inject

조상-부모-자식 3 개의 컴포넌트가 존재하고 조상 컴포넌트의 데이터를 자식 컴포넌트가 써야하는 상황을 가정하자. 만약 props를 이용해서 데이터를 전달한다면 조상 컴포넌트의 데이터를 부모 컴포넌트를 거쳐 자식 컴포넌트에 전달해야하는 아주 불필요한 과정이 필요하다. 데이터를 공유해야하는 두 컴포넌트 사이에 다른 컴포넌트들이 많을 수록 문제는 더욱 심각해진다.
이 문제점을 해결하기 위해서 provideinject를 사용할 수 있다.

사용

공유할 데이터를 가진 컴포넌트에서 provide 옵션을 지정한다. 이 때 provide는 데이터를 전달하므로 반드시 함수로 선언한다.

export default {
   provide() {
    return {
      msg: this.msg
    }
  }
}

데이터가 필요한 컴포넌트에서 inject 옵션을 배열 리터럴 안에 지정한다.

export default {
  inject: ['msg']
}

주의점

위처럼 값을 provide에 바로 입력할 경우 이 데이터는 반응형 데이터가 아니며 따라서 값이 변경되도 렌더링이 적용되지 않는다. 반응형 데이터로 만들기 위해서 vuecomputed 함수를 사용해야한다.
(이 때도 computed()의 매게 변수에 들어가는 데이터는 함수로 입력한다.)

export default {
   provide() {
    return {
      msg: computed(() => this.msg)
    }
  }
}

전역 스토어

props의 문제점을 provide & inject를 사용해서 해결했지만 이 또한 한계가 존재한다. provide & inject는 어디까지나 데이터의 전달만이 이루어지기 때문에 데이터의 수정은 데이터를 가지고 있는 컴포넌트만이 가능하다.
전역 스토어를 사용하면 이 문제점을 해결할 수 있다.

구조

전역 스토어는 4개의 구성요소를 가지고 있다.

  1. 상태를 보관하는 "state"
  2. 상태를 변경하는 "mutations"
  3. 상태의 변경을 요청하는 "actions"
  4. 상태를 가공하여 출력하는 "getters"

생성

각 구성요소들은 정해진 규칙 없이 자유롭게 작성이 가능하지만 상태를 보관하는 state의 경우 반응형 데이터로 사용하기 위해서 vuereactive()를 사용한다. reactive()는 객체 리터럴을 입력받아 반응형 데이터인 proxy를 반환하는 함수이다.

Vuex

vuex는 위의 전역 스토어를 좀 더 체계적으로 구현한 라이브러리다.

구조


전역 스토어와 구조 자체는 같을 수 있지만 동작 방식에 차이가 있다. vuex에서는 Mutaions의 함수가 동작하기 위해서 commit 함수를 사용하며, Action의 함수가 동작하기 위해서 dispatch함수를 사용한다.

생성

vuexcreateStore 함수를 사용하며 컴포넌트의 옵션처럼 객체 리터럴에 필요한 값을 지정하여 매개변수로 입력한다.
component의 data와 같은 이유로 state는 함수로 작성한다.
gettersmutations에서 함수의 첫 번째 매게변수를 통해서 state의 값에 접근이 가능하며, actions에 있는 함수의 첫 번째 매게변수인 contextstate, getters, commit, dispatch가 저장된 객체이다.

// vuex store 생성 예시
const myStore = createStore({
  state() {
    return {
      data: 'something'
    }
  },
  getters: {
  	important(state) {
      return data + 'important'
    }
  },
  mutations: {
  	updateData(state, nextData) {
      state.data = nextData
    }
  },
  actions: {
  	fetchData(context) {
      //fetch data
    }
  }
})

사용

가장 먼저 전역으로 사용하기 위해서 어플리케이션 인스턴스에 use() 메서드로 등록이 필요하다.

const app = Vue.createApp(App)
app.use(myStore)
app.mount('#app')

컴포넌트 내부에서 store를 사용하는 경우

  • state
    this.$store.state로 접근

  • getters
    this.$store.getters로 접근

  • action
    this.$store.dispatch()로 함수 호출

  • mutations
    this.$store.commit()로 함수 호출

// state
this.$store.state.data

// getters
this.$store.getters.important

// action
this.$store.dispatch('fetchData')

// state
this.$store.state.commit('updateData')

모듈화

하나의 store가 모든 컴포넌트의 데이터를 관리할 경우 코드가 길어져 유지보수가 어려울 수 있으며 이 외에도 여러 단점이 존재한다. 모듈화는 과도하게 커질 수 있는 store를 여러 개로 나누어 관리할 수 있게 해준다.

생성

모듈은 전역 스토어 생성시 createStore에 입력하는 객체와 거의 동일하다. 추가적으로 namespaced: true 값을 객체 리터럴에 추가해주면 끝이다.

const myModule = {
  namespaced: true,
  state() {},
  getters: {},
  mutations: {},
  actions: {}
}

등록

생성된 모듈은 전역 스토어의 옵션 중 modules: {...}에 입력하여 등록할 수 있다.

const myStore = {
  modules: {
    moduleName: myModule 
  }
}

사용

기존의 전역스토어에 namespace 명을 추가하는 방식으로 사용한다.

// namespace 가 'module'인 경우
// state
this.$store.state.module.data

// getters
this.$store.getters.['module/important']

// action
this.$store.dispatch('module/fetchData')

// state
this.$store.state.commit('module/updateData')

mapping

vuex의 함수 mapState, mapGetters, mapActions, mapMutations를 사용해 Store의 값을 컴포넌트의 옵션에 빠르게 mapping이 가능하다. 모든 함수의 입력은 아래와 같다.

(namespace 이름, [사용할 값 또는 함수의 이름, ...])
전역 스토어의 경우 namespace 이름은 생략한다. 참고 링크


Reference

  1. 프로그래머스 프론트엔드 데브코스
  2. Vue 3 가이드
    https://v3.vuejs-korea.org/guide/introduction.html
  3. vuex
    https://vuex.vuejs.org/
profile
개발자로 발돋움

0개의 댓글