[프론트엔드 데브코스 TIL] 2023.11.24 Day49 Vue 5일차

SoJuSo·2023년 11월 24일
1

2023.09 ~ 2024.01 TIL

목록 보기
52/105

...

📚금일 학습 내용 Vue

Vue Vue Vue.. 컴포넌트 커스텀 이벤트, Slots, 동적 컴포넌트, Refs, 플러그인, 믹스인, 텔레포트, Provide와 inject

🏫데브코스

📌VUE Day5

📌컴포넌트 커스텀 이벤트

props 데이터가 자식 컴포넌트로 흘러가는 것은 가능하지만, 다른데서 조작하는 것은 불가능하다. 결국 일반적으로는 부모 컴포넌트에 영향을 줄 수 없지만 $emit이라는 내장 메서드를 사용해서 자식에서 부모로 이벤트를 발생시키는 커스텀 이벤트로 가능하다.

하지만 이 방식은 함수를 결국 부모에 선언해줘야 하지 않나..?

그래서 그냥 v-model로 간단하게 구현할 수도 있다.

📌Slots

컴포넌트에 컨텐츠를 추가할 때 slot이라는 키워드를 사용하면 slot이 키워드로 대체되는 방식이다.

몇 가지 특징이 있는데 아래 코드처럼 # 키워드로 특정 이름을 가지는 slot을 선언해줄 수 있고 또 slot을 동적인 값으로 사용할 수도 있다.

// App.vue
// # 키워드로 이름 지정
    <template #abc>
      <h2>ABC</h2>
    </template>
    <template #xyz>
      <h2>XYZ</h2>
    </template>

// 동적으로 할당도 가능하다.
    <template #default="{ hello }">
      <h2>Hello {{ hello }}</h2>
    </template>
    <template #[slotName]="{ hello }">
      <h2>ABC{{ hello }}</h2>
    </template>
    ... 중략
	<script>
    data() {
      return {
        msg: "Hello Vue!",
        slotName: "xyz",
      };
    },
    </script>
// Hello.vue
  <slot name="abc"></slot>
  <h1>Hello</h1>
  <slot name="xyz"></slot>

📌동적 컴포넌트

말 그대로 컴포넌트도 동적으로 활용이 가능한데, 태그를 통해서 특정 컴포넌트를 지정할 수 있다.

// App.vue
<template>
  <!-- <h1 @click="currentComponent = 'World'">
    {{ msg }}
  </h1> -->
  <button @click="currentComponent = 'Hello'">Hello!</button>
  <button @click="currentComponent = 'World'">World!</button>
  <!-- 아래 방법은 동적이지 않다. -->
  <!-- <component is="Hello" />  -->
  <!-- 그래서 아래처럼 해줘야한다. -->
  <!-- <component :is="currentComponent" /> -->
  
  // v-show 처럼 초기에 렌더링을 하게 해주고 등장했다가 사라졌다 하는 keep-alive 문법
  <keep-alive>
    <component :is="currentComponent" />
  </keep-alive>
</template>
  
  <script>
import Hello from "~/components/Hello";
export default {
  components: {
    Hello,
  },
  data() {
      currentComponent: "Hello",
  },
};
</script>

📌Refs

태그를 찾는 여러 방법이 있겠지만 태그의 수가 많아지면 하나 집어서 찾고 싶을때 쓰면 좋은 방식이다. 위 예시인 id와 비슷하게 사용하고 찾을때는 this.$refs.hello 와 같은 형식으로 찾을 수 있다.

다만 태그가 깊어지면 this.$refs.hello.$refs.world 와 같이 사용해야 한다.
그리고 settimeout 또는 $nextTick을 사용해 주어야 하는데 데이터 수정 후 화면이 바뀌는 것을 보장하여 내부 콜백을 실행하도록 동작한다. 따라서 실행 순서를 보장해줄 수 있다.

<h1 id="hello">Hello</h1>
<h1 ref="hello">Hello</h1>

<script>
export default {
mounted() {
  // 당연히 created()에서는 안되겠지?
  // const h1El = document.querySelector("#hello");
  const h1El = this.$refs.hello;
  console.log(h1El);
},
};
</script>

📌플러그인

plugins 폴더에 객체 리터럴을 반환해주는 인스턴스를 선언한다. app.config.globalProperties에 등록하면 어디서든 우리가 원하는대로 사용할 수 있다. 또한 이름도 원하는 대로 동적으로 사용할 수 있다.

//fetch.js 파일
export default {
install(app, options) {
  app.config.globalProperties[options.pluginName || "$fetch"] = (url, opts) => {
    return fetch(url, opts).then((res) => res.json());
  };
},
};

//main.js 에서 등록해서 사용할 수 있다.
const app = createApp(App);
app.use(fetchPlugin, {
//   pluginName: "$myName",
});
//스크립트에서 바로 사용 가능하다.
<script>
... 중략
methods: {
  async init() {
    const res = await this.$fetch("https://jsonplaceholder.typicode.com/todos/1");
    console.log(res, "Done!");
  },
};
</script>

📌믹스인

... 코드가 날아가서 다시 듣고 내일 작성

📌Teleport

일단 Teleport의 기능을 사용하는 이유는 바로 body태그 내부로 순간이동 시켜준다는 것이다. 그래서 이름도 Teleport인데 강의에서는 모달에서의 예시로 들었는데, 모달을 화면에 그려줄 때 해당 모달이 상속받는 속성에 따라서 css positionfixed를 지정해두었어도 의도하지 않은 동작이 일어날 수 있기 때문에 아예 body태그로 올려버리는 Teleport를 사용한 것이다.

<teleport to="body">
  <template v-if="modelValue">
    <div class="modal" @click="offModal">
      <div :style="{ width: `${parseInt(width, 10)}px` }" class="modal__inner" @click.stop>
        <!-- 기본값이 false라 따로 옵션을 지정해줄 필요가 있다. -->
        <!-- 버튼을 오른쪽 위로 보내주자 -->
        <button v-if="closeable" @click="offModal" class="close">x</button>
        <slot></slot>
      </div>
    </div>
  </template>
</teleport>

진짜 태그 이름이 텔레포트다.. to="body"로 body로 아예 보내버리면

요렇게 아예 body태그로 순간이동 해서 화면에 출력된다.

📌Provide, Inject

상위 컴포넌트에서 하위 컴포넌트들로 데이터를 내려줄 때 그 계층이 많으면 굉장히 번거로워진다. props를 사용해도 되지만 그 계층이 많을 수록 복잡해지기 때문에 그럴 때는 조상 컴포넌트에서 모든 후손 컴포넌트에 데이터를 전달할 수 있는 provide를 사용하는 것이 좋다. 약간 react의 context api같은 느낌인가? 싶다. 물론 잘 몰라서 하는 소리일수도...

여튼 사용 방법은 다음과 같다. 구조 App → Parent → Child

// App.vue에서
import { computed } from "vue";

  provide() {
  return {
    msg: computed(() => this.msg),
  };
},
 data() {
  return {
    msg: "Hello Vue!",
  };
},

// Parent.vue
얘도 사용 가능

// Child.vue
<template>
<h2>Child! / {{ msg }}</h2>
</template>

<script>
export default {
inject: ["msg"],
mounted() {
  console.log(this.msg);
},
};
</script>

최상위에서 provide로 제공 해주고 자식들이 inject로 가져와서 사용하면 된다. 다만 반응성을 띄지 않는다는 문제점이 있는데 그때는 computed를 사용하면 된다. 위 예제 참조.


📖소회

오랜만에 새벽반에 늦게까지 남아있었다. 매니저님, 동영 멘토님이랑 같이 공부를 처음해봤는데 사람들 목소리를 라디오처럼 들으면서 공부하는 것도 나름 매력이 있는 것 같다.

다만 오늘은 개인적은 의문을 가진 공부가 없었다는게 아쉬운데 뭐 이런 날도 있는거지~ 할거는 다 했으니까 너무 상심하지 말자. 내일도 성장에 도움되는 강의가 준비되어 있다. 열심히 들어보자. 내일도 가봅시다🔥

TIL 작성 소요시간 1시간
밤은 길다~ 생각하고 여유롭게 썼는데 타이머가 55분이 넘었다;

profile
Junior Frontend Engineer

0개의 댓글