Untitled

πŸ˜‰ ν—·κ°ˆλ Έλ˜ & λͺ°λžλ˜ λΆ€λΆ„λ“€λ§Œ μ •λ¦¬ν•˜λŠ” λ‚˜λ§Œμ˜ TIL
😯 λͺ¨λ“  κ°•μ˜ λ‚΄μš©μ€ 적지 μ•Šμ•„μš”!

였늘의 μ†Œκ°μ€?
Vue의 μ»΄ν¬λ„ŒνŠΈ ꡬ쑰, 데이터 λ°©ν–₯ 등을 νŒŒμ•…ν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.
ν•˜μ§€λ§Œ 아직 λ©€μ—ˆμ–΄μš”..
과제 μ–΄λ–‘ν•˜μ§€... μ–΄λ–‘ν•˜μ§€....




[1] μ»΄ν¬λ„ŒνŠΈ 등둝

μ»΄ν¬λ„ŒνŠΈλŠ” μ „μ—­, 지역 등둝이 κ°€λŠ₯ν•©λ‹ˆλ‹€.

등둝을 μœ„ν•΄μ„œλŠ” μ»΄ν¬λ„ŒνŠΈμ˜ 이름을 λ°˜λ“œμ‹œ 지정해야 ν•©λ‹ˆλ‹€.

ν•΄λ‹Ή μ»΄ν¬λ„ŒνŠΈλŠ” 파슀칼 μΌ€μ΄μŠ€, μΌ€λ°₯ μΌ€μ΄μŠ€λ‘œ μž‘μ„±ν•  수 μžˆμŠ΅λ‹ˆλ‹€.


[1-1] μ „μ—­, 지역 등둝

μ „μ—­ 등둝은 자주 μ‚¬μš©λ˜κ±°λ‚˜ μ–Έμ œλ“ μ§€ μ‚¬μš©ν•  μ»΄ν¬λ„ŒνŠΈλ“€λ§Œ λ“±λ‘ν•©λ‹ˆλ‹€.

import { createApp } from "vue";
import App from "./App.vue";
import Btn from "./components/Btn";

const app = createApp(App);
app.component("Btn", Btn); // μ „μ—­ 등둝
app.mount("#app");


λ”°λΌμ„œ λŒ€λΆ€λΆ„μ˜ μ»΄ν¬λ„ŒνŠΈλŠ” 지역 등둝을 톡해 μ‚¬μš©ν•©λ‹ˆλ‹€.

import Hello from "~/components/Hello";
import Btn from "~/components/Btn";

export default {
  components: {
    Hello,
    Btn, // 지역 등둝
  },
// ...




[2] μ»΄ν¬λ„ŒνŠΈ Props

propsλŠ” 데이터 νƒ€μž…μ„ λͺ…μ‹œν•΄μ€„ 수 μžˆμŠ΅λ‹ˆλ‹€.

참고둜 JavaScriptμ—μ„œ μž‘μ„±ν•œ props λͺ…이 카멜 μΌ€μ΄μŠ€μ—¬λ„, htmlμƒμ—μ„œλŠ” μΌ€λ°₯ μΌ€μ΄μŠ€λ‘œ μž‘μ„±ν•΄μ•Ό μΈμ‹λ©λ‹ˆλ‹€.

props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // λ˜λŠ” λ‹€λ₯Έ μƒμ„±μž
}


:(v-bind λ””λ ‰ν‹°λΈŒ)λ₯Ό μ‚¬μš©ν•˜μ—¬ 동적인 데이터λ₯Ό 전달할 수 μžˆμŠ΅λ‹ˆλ‹€.

<!-- λ³€μˆ˜μ˜ 값을 λ™μ μœΌλ‘œ ν• λ‹Ή -->
<blog-post :title="post.title"></blog-post>

<!-- λ³΅μž‘ν•œ ν‘œν˜„μ„ ν¬ν•¨ν•œ 동적 κ°’ ν• λ‹Ή -->
<blog-post :title="post.title + ' by ' + post.author.name"></blog-post>


λ˜ν•œ μ΄ˆκΈ°κ°’μ„ 지정해쀄 수 μžˆμŠ΅λ‹ˆλ‹€.

<Hello :message="msg" name="Hello!" />
export default {
  props: {
    message: String, // μ΄λ•Œ { type: String, default: μ΄ˆκΈ°κ°’ }을 지정해쀄 수 μžˆμŠ΅λ‹ˆλ‹€.
    name: String,
  },
// ...


[2-1] Props μœ νš¨μ„± 검사

app.component("my-component", {
  props: {
    // κΈ°λ³Έ νƒ€μž… 체크 (`null`κ³Ό `undefined`λŠ” λͺ¨λ“  νƒ€μž… μœ νš¨μ„± 검사λ₯Ό ν†΅κ³Όν•©λ‹ˆλ‹€.)
    propA: Number,
    // μ—¬λŸ¬ νƒ€μž… ν—ˆμš©
    propB: [String, Number],
    // λ¬Έμžμ—΄ ν•„μˆ˜
    propC: {
      type: String,
      required: true,
    },
    // κΈ°λ³Έ 값을 κ°–λŠ” μˆ«μžν˜•
    propD: {
      type: Number,
      default: 100,
    },
    // κΈ°λ³Έ 값을 κ°–λŠ” 객체 νƒ€μž…
    propE: {
      type: Object,
      // κ°μ²΄λ‚˜ λ°°μ—΄μ˜ κΈ°λ³Έ 값은 항상 νŒ©ν† λ¦¬ ν•¨μˆ˜λ‘œλΆ€ν„° λ°˜ν™˜λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.
      default: function () {
        return { message: "hello" };
      },
    },
    // μ»€μŠ€ν…€ μœ νš¨μ„± 검사 ν•¨μˆ˜
    propF: {
      validator: function (value) {
        // 값이 κΌ­ μ•„λž˜ μ„Έ λ¬Έμžμ—΄ 쀑 ν•˜λ‚˜μ™€ μΌμΉ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.
        return ["success", "warning", "danger"].indexOf(value) !== -1;
      },
    },
    // κΈ°λ³Έ 값을 κ°–λŠ” ν•¨μˆ˜
    propG: {
      type: Function,
      // κ°μ²΄λ‚˜ λ°°μ—΄κ³Ό 달리 μ•„λž˜ ν‘œν˜„μ€ νŒ©ν† λ¦¬ ν•¨μˆ˜κ°€ μ•„λ‹™λ‹ˆλ‹€. κΈ°λ³Έ κ°’μœΌλ‘œ μ‚¬μš©λ˜λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€.
      default: function () {
        return "Default function";
      },
    },
  },
});




[3] μ»΄ν¬λ„ŒνŠΈ Non-Prop 속성

$attrs ν”„λ‘œνΌν‹°λ₯Ό 톡해 propsκ°€ μ•„λ‹Œ Non-Prop 속성듀에 μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.

<!-- App.vue(μƒμœ„ μ»΄ν¬λ„ŒνŠΈ)  -->
<Hello class="hello" style="font-size: 100px" @click="msg += '!'" />
<!-- Hello.vue(ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈ)  -->
<h1 v-bind="$attrs">Hello</h1>

Non-Prop 속성듀이 μžλ™μœΌλ‘œ 루트 μš”μ†Œμ— μƒμ†λ˜λŠ” 것을 μ›μΉ˜ μ•Šμ„ 땐, inheritAttrs 속성을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

export default {
  inheritAttrs: false, // false둜 μ„€μ •ν•˜λ©΄ 상속이 μΌμ–΄λ‚˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
  props: {
    style: Object,
  },
  mounted() {
    console.log(this.$attrs);
  },
};




[4] μ»΄ν¬λ„ŒνŠΈ μ»€μŠ€ν…€ 이벀트

@ λ””λ ‰ν‹°λΈŒμ™€ emit을 μ‚¬μš©ν•΄μ„œ μ»€μŠ€ν…€ 이벀트λ₯Ό μ •μ˜ν•˜κ³  μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.


<!-- App.vue(μƒμœ„ μ»΄ν¬λ„ŒνŠΈ) -->
<template>
  <h1>{{ msg }}</h1>
  <Hello @add="msg += '?!'" />
</template>

<!-- Hello.vue(ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈ) -->

<template>
  <h1 @click="$emit('add')">Hello</h1>
</template>

<script>
  export default {
    emits: ["add"],
  };
</script>


λ‹€λ§Œ λ„€μ΄ν‹°λΈŒ 이벀트λ₯Ό μ£Όμ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.

clickμ΄λΌλŠ” μžλ°”μŠ€ν¬λ¦½νŠΈ λ„€μ΄ν‹°λΈŒ 이벀트λ₯Ό μ»€μŠ€ν…€ 이벀트둜 μ„ μ–Έν•  수 μžˆμŠ΅λ‹ˆλ‹€.

export default {
  emits: ["click"],
};

μ΄λ ‡κ²Œ 되면 μžλ°”μŠ€ν¬λ¦½νŠΈμ— λ‚΄μž₯된 클릭 μ΄λ²€νŠΈλŠ” λ™μž‘ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.




[5] μ»΄ν¬λ„ŒνŠΈ Slots

slot νƒœκ·Έλ₯Ό μ‚¬μš©ν•˜μ—¬ μƒμœ„ μ»΄ν¬λ„ŒνŠΈμ—μ„œ μ„ μ–Έν•œ ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈ μ‚¬μ΄μ˜ Contentλ₯Ό λ Œλ”λ§ν•  수 μžˆμŠ΅λ‹ˆλ‹€.


<!-- App.vue(μƒμœ„ μ»΄ν¬λ„ŒνŠΈ) -->
<template>
  <h1>{{ msg }}</h1>
  <Hello>Hello Compnent</Hello>
  <!-- ν•΄λ‹Ή λ‚΄μš©μ΄ ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈ slot에 λ“€μ–΄κ°‘λ‹ˆλ‹€. -->
</template>

<!-- Hello.vue(ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈ) -->
<template>
  <h1>Hello!</h1>
  <slot></slot>
</template>

Untitled 1


[5-1] Fallback Content

ν•΄λ‹Ή slot νƒœκ·Έ 내뢀에 Fallback Contentλ₯Ό μž‘μ„±ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μƒμœ„ μ»΄ν¬λ„ŒνŠΈμ˜ ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈ νƒœκ·Έ 내뢀에 contentκ°€ 없을 κ²½μš°μ— μž‘μ„±λ˜λŠ” contentλ₯Ό Fallback content라 ν•©λ‹ˆλ‹€.


<!-- App.vue(μƒμœ„ μ»΄ν¬λ„ŒνŠΈ) -->
<template>
  <h1>{{ msg }}</h1>
  <Hello>Hello Compnent</Hello>
  <Btn></Btn>
</template>

<!-- Btn.vue(ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈ) -->
<template>
  <button>
    <slot>Click me!</slot>
    <!-- μƒμœ„ μ»΄ν¬λ„ŒνŠΈμ˜ contentκ°€ μ—†μœΌλ―€λ‘œ ν•΄λ‹Ή contentκ°€ λ³΄μ—¬μ§‘λ‹ˆλ‹€. -->
  </button>
</template>

Untitled 2


[5-2] v-slot

v-slot λ””λ ‰ν‹°λΈŒλ₯Ό μ΄μš©ν•˜μ—¬ μ›ν•˜λŠ” μŠ¬λ‘―μ— ν•΄λ‹Ή ν…œν”Œλ¦Ώ λ‚΄μš©μ„ μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

v-slot λ””λ ‰ν‹°λΈŒλŠ” #의 μ•½μ–΄λ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.


<!-- App.vue(μƒμœ„ μ»΄ν¬λ„ŒνŠΈ) -->
<template>
  <h1>{{ msg }}</h1>
  <Hello>Hello Compnent</Hello>
  <Btn>
    <template v-slot:buttonSlot>
      <h2>ABC</h2>
    </template>
  </Btn>
</template>

<!-- Btn.vue(ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈ) -->
<template>
  <button>
    <slot name="buttonSlot">Click me!</slot>
  </button>
</template>

μ΄λ•Œμ˜ slot μ’…λ₯˜λŠ” 두 κ°€μ§€λ‘œ λ‚˜λ‰˜μ–΄μ§‘λ‹ˆλ‹€.


[1] name slot

μ΄λ ‡κ²Œ name 속성을 가진 slot을 name을 κ°€μ§€λŠ” slot이라고 ν•©λ‹ˆλ‹€.

<slot name="buttonSlot">Click me!</slot>

μƒμœ„ μ»΄ν¬λ„ŒνŠΈμ—μ„œ v-slot λ””λ ‰ν‹°λΈŒλ‘œ 직접 접근이 κ°€λŠ₯ν•©λ‹ˆλ‹€.

<Btn>
  <template v-slot:buttonSlot>
    <h2>ABC</h2>
  </template>
</Btn>


[2] λ²”μœ„λ₯Ό κ°€μ§€λŠ” 슬둯

slot은 λ²”μœ„λ₯Ό κ°€μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.

λ²”μœ„λŠ” ν•΄λ‹Ή 슬둯이 μ‚¬μš©λ˜λŠ” ꡬ간을 λœ»ν•©λ‹ˆλ‹€.

<slot :hello="123"></slot>
<!-- 
	μ΄λ ‡κ²Œ name을 지정해주지 μ•ŠμœΌλ©΄ #default둜 μ ‘κ·Όν•  수 있으며
	default 슬둯이라 ν•©λ‹ˆλ‹€. 
-->

λ™μΌν•˜κ²Œ v-slot λ””λ ‰ν‹°λΈŒλ‘œ ν•΄λ‹Ή 슬둯 λ‚΄λΆ€μ˜ λ²”μœ„μ— μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.


<Hello>
  <template #default="{ hello }">
    <!-- ν•΄λ‹Ή hello 데이터에 μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€. -->
    <h2>{{ hello }}</h2>
  </template>
</Hello>

ν•΄λ‹Ή name 속성과 λ°μ΄ν„°λŠ” λ™μ μœΌλ‘œ μ œμ–΄ν•  수 μžˆμŠ΅λ‹ˆλ‹€.




[6] 동적 μ»΄ν¬λ„ŒνŠΈ

ν•„μš”μ— 따라 νŠΉμ • μ»΄ν¬λ„ŒνŠΈλ₯Ό ꡐ체해쀄 수 μžˆλŠ” 동적 μ»΄ν¬λ„ŒνŠΈμ˜ κ°œλ…μ„ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

<component :is="currendComponent"></component>
<!-- 
	ꡐ체될 수 μžˆλŠ” λͺ¨λ“  μ»΄ν¬λ„ŒνŠΈλ“€μ„ is 속성에 μ§€μ •ν•©λ‹ˆλ‹€
	보톡 μ—¬λŸ¬κ°œμ˜ μ»΄ν¬λ„ŒνŠΈκ°€ κ΅μ œλ˜λ―€λ‘œ, dataλ₯Ό λ„£μ–΄μ€λ‹ˆλ‹€. 
-->

동적 μ»΄ν¬λ„ŒνŠΈλŠ” 자주 μ „ν™˜λ  λ•ŒλŠ” μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

μ „ν™˜ λΉ„μš©μ΄ λ†’κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.


μ΄λ•Œ keep-alive νƒœκ·Έλ₯Ό μ‚¬μš©ν•˜μ—¬ 캐싱 κΈ°λŠ₯을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.


<template>
  <h1 @click="currentComponent = 'World'">{{ msg }}</h1>
  <keep-alive>
    <component :is="currentComponent" />
  </keep-alive>
</template>

높은 μ „ν™˜ λΉ„μš©μ„ 캐싱 κΈ°λŠ₯을 μ‚¬μš©ν•˜μ—¬ 해결해쀄 수 있기 λ•Œλ¬Έμ—

동적 μ»΄ν¬λ„ŒνŠΈ κ°œλ…μ„ μ‚¬μš©ν•  땐, ν•΄λ‹Ή keep-alive νƒœκ·Έλ‘œ λ¬Άμ–΄μ£ΌλŠ” 것이 ꢌμž₯λ©λ‹ˆλ‹€.




[7] Refs

보톡 μ–΄λ– ν•œ html νŠΉμ • μ—˜λ¦¬λ¨ΌνŠΈλ₯Ό μ°ΎκΈ° μœ„ν•΄ querySeletor을 μ‚¬μš©ν•©λ‹ˆλ‹€.

ν•˜μ§€λ§Œ querySeletor은 ν•΄λ‹Ή μ»΄ν¬λ„ŒνŠΈμ˜ 전체 documentλ₯Ό μŠ€μΊ”ν•˜μ—¬ μ›ν•˜λŠ” μš”μ†Œλ₯Ό μ°Ύμ•„μ˜΅λ‹ˆλ‹€.

λ”°λΌμ„œ νš¨μœ¨μ„±μ„ μœ„ν•΄ ref 속성을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.


ref 속성은 reference의 μ€„μž„λ§μ΄μž html νŠΉμ • μ—˜λ¦¬λ¨ΌνŠΈλ₯Ό μ°Έμ‘°ν•  수 μžˆλŠ” μ†μ„±μž…λ‹ˆλ‹€.

<h1 ref="hello">Hello</h1>
<!-- μ»΄ν¬λ„ŒνŠΈμ˜ ref 속성에 λΆ€μ—¬λœ μ΄λ¦„μœΌλ‘œ μ ‘κ·Όν•©λ‹ˆλ‹€. -->
const $h1El = this.$refs.hello; // μ°Έμ‘°ν•΄μ„œ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
console.log($h1El);

ν•΄λ‹Ή ref 속성은 html μš”μ†Œκ°€ μ—°κ²°λœ 이후뢀터 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. (mount)


νŠΉμ • component νƒœκ·Έμ— 직접 μ μš©λ„ κ°€λŠ₯ν•©λ‹ˆλ‹€.

λ‹€λ§Œ component λ‚΄λΆ€ μš”μ†Œμ— 직접 μ ‘κ·Όν•˜κΈ° μœ„ν•΄μ„œλŠ” $el을 μΆ”κ°€ν•΄μ•Ό ν•©λ‹ˆλ‹€.

const $componentEl = this.$refs.hello.$el;

이외에도 λ‹€μ–‘ν•œ component에 λ‚΄μž₯된 속성에 μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.


λ”°λΌμ„œ ref둜 μ ‘κ·Όν•œ html μš”μ†Œμ˜ 속성 λ˜ν•œ μ ‘κ·Όν•΄μ„œ μ €μž₯ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
$refsλŠ” 쀑첩이 κ°€λŠ₯ν•©λ‹ˆλ‹€.


[+] $nextTick

보톡 데이터가 λ³€κ²½λœ μ΄ν›„μ—λŠ” λ°”λ‘œ μš”μ†Œκ°€ λ Œλ”λ§λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

데이터가 λ³€κ²½λ˜λŠ” ν•¨μˆ˜ μ•ˆμ˜ 둜직이 μ–΄λŠμ •λ„ μ™„λ£Œλœ ν›„, λ Œλ”λ§ μž‘μ—…μ΄ μΌμ–΄λ‚©λ‹ˆλ‹€.

λ”°λΌμ„œ 데이터가 λ³€κ²½λœ 이후에 νŠΉμ • μš”μ†Œμ— μ ‘κ·Όν•΄μ„œ λ Œλ”λ§ 속성을 μ‘°μž‘ν•΄λ„

ν•΄λ‹Ή μ‘°μž‘μ΄ μ •μƒμ μœΌλ‘œ λ™μž‘ν•˜μ§€ μ•Šμ„ λ•Œκ°€ λ§ŽμŠ΅λ‹ˆλ‹€.

μ΄λ•Œ, λ°μ΄ν„°μ˜ 변경이 μ™„λ£Œλ˜κ³  λ Œλ”λ§(DOM μ—…λ°μ΄νŠΈ)을 μ²˜λ¦¬ν•΄μ£ΌλŠ” $nextTick을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

onEdit() {
	this.data = true;
	this.$nextTick(() => {
		this.$refs.editor.focus()
	});
}


$refs와 $nextTick은 λŒ€λΆ€λΆ„ 같이 μ‚¬μš©λ©λ‹ˆλ‹€.





좜처

ν”„λ‘œκ·Έλž˜λ¨ΈμŠ€

Vue.js3 Document

μΉ΄ν…Œκ³ λ¦¬: ,

μ—…λ°μ΄νŠΈ:

λŒ“κΈ€λ‚¨κΈ°κΈ°