...
Vue Vue Vue.. 컴포넌트 커스텀 이벤트, Slots, 동적 컴포넌트, Refs, 플러그인, 믹스인, 텔레포트, Provide와 inject
props 데이터가 자식 컴포넌트로 흘러가는 것은 가능하지만, 다른데서 조작하는 것은 불가능하다. 결국 일반적으로는 부모 컴포넌트에 영향을 줄 수 없지만 $emit이라는 내장 메서드를 사용해서 자식에서 부모로 이벤트를 발생시키는 커스텀 이벤트로 가능하다.
하지만 이 방식은 함수를 결국 부모에 선언해줘야 하지 않나..?
그래서 그냥 v-model로 간단하게 구현할 수도 있다.
컴포넌트에 컨텐츠를 추가할 때 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>
태그를 찾는 여러 방법이 있겠지만 태그의 수가 많아지면 하나 콕
집어서 찾고 싶을때 쓰면 좋은 방식이다. 위 예시인 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의 기능을 사용하는 이유는 바로 body태그 내부로 순간이동 시켜준다는 것이다. 그래서 이름도 Teleport인데 강의에서는 모달에서의 예시로 들었는데, 모달을 화면에 그려줄 때 해당 모달이 상속받는 속성에 따라서 css position
을 fixed
를 지정해두었어도 의도하지 않은 동작이 일어날 수 있기 때문에 아예 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태그로 순간이동 해서 화면에 출력된다.
상위 컴포넌트에서 하위 컴포넌트들로 데이터를 내려줄 때 그 계층이 많으면 굉장히 번거로워진다. 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분이 넘었다;