728x90
★ 단일 파일 컴포넌트
- 단일 파일 컴포넌트는 컴포넌트 하나를 .vue 파일 하나에 작성하기 때문에 붙여진 이름
- 한 파일에 컴포넌트 구성을 위해 템플릿, 스크립트, 스타일 정보를 모두 포함하기 때문에 컴포넌트 단위로 분리 가능
- Vue 애플리케이션을 개발하기 위해서는 Webpack, Rollup 과 같은 모듈 번들러 도구와 ES6, TypeScript와 같은 트랜스파일러를 함께 사용하도록 개발 프로젝트 환경을 설정해야 한다.
1. 프로젝트 설정 도구
- 1.1 Vue CLI 도구
- Webpack 기반의 Vue 공식 프로젝트 설정 도구였다.
- 하지만, 이 방법은 프로젝트를 생성하고 구동하기까지 다소 긴 시간이 소요가 된다.
- 또한, 번들링 과정을 거쳐야하기 때문에 작성한 코드를 테스트하기에 조금 긴 시간을 기다려야 한다.
- 번들링 : 여러 모듈을 묶어서 하나 또는 몇개의 모듈 파일로 만드는 과정
- 프로젝트 생성 방법
npx @vue/cli create [프로젝트명]
cd [프로젝트명]
npm run serve
- 1.2 Vite 기반의 도구
- vite(비트)는 프랑스어로 '빠르다' 라는 뜻을 가진 단어로, Vue의 창시자인 Evan You가 만든 차세대 프론트엔드 개발 도구이다.
- webpack과 같은 빌드 도구는 자바스크립트 언어로 만들어져있지만, Vite의 ESBuild는 Go 라는 네이티브 언어로 만들어진 도구를 사용하기 때문에 빌드 속도가 아주 빠르다.
- 프로젝트 생성 방법
npm init vite [프로젝트명]
npm init vue@latest
- vite 기반 프로젝트가 생성되면 프로젝트 디렉터리로 이동하며, 관련 패키지를 설치하고 실행
cd [프로젝트명]
npm install
npm run dev
- vite 기반 프로젝트의 폴더 구조
src | JavaSciprt 코드, .vue 파일을 이곳에 작성한다. 시작진입(Entry) 파일은 src/main.js |
public | 이미지와 같은 정적 파일, 자원을 이곳에 작성한다. |
dist | 빌드 후 생성된 산출물이 저장되는 디렉터리이다. |
index.html | Vue 애플리케이션의 컴포넌트 트리는 index.html의 id가 app인 div 요소 내부에 렌더링된다. |
- 프로젝트 내에서 다음 명령어를 사용해 개발서버를 구동하거나 빌드할 수 있다.
빌드 명령어 | npm run build |
개발 서버 시작 명령어 | npm run dev |
미리보기 명령어 | npm run preview |
★ 프로젝트 생성하기
- vite 방법을 이용한 프로젝트 생성
2. 간단한 단일 파일 컴포넌트 작성과 사용
- src/components 디렉터리의 모든 하위 디렉터리와 컴포넌트 파일을 삭제하고, CheckboxItem 컴포넌트를ㅈ추가
- 작성할 때는, vue VSCode Snippets 도구를 이용하면 편리하다.
- 간단한 파일 컴포넌트 작성
<template>
<li>
<input type="checkbox" v-model="checked" /> 옵션1
</li>
</template>
<script>
export default {
name : "ChecboxItem",
data() {
return {
checked: false
};
}
}
</script>
■ 전역 컴포넌트
- Vue 애플리케이션 인스턴스의 component() 메서드를 이용해 등록.
- 전역 컴포넌트로 등록한 경우에는 루트 컴포넌트 하위의 모든 자식 컴포넌트 어디에서나 등록한 컴포넌트를 이용할 수 있다.
- main.js에서 전역 컴포넌트를 등록
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import CheckboxItem from './components/CheckboxItem.vue'
import './assets/main.css'
createApp(App)
.component('CheckboxItem', CheckboxItem)
.mount('#app')
■ 지역 컴포넌트
- 특정 컴포넌트에서 컴포넌트를 직접 등록하여 사용하는 방법
- 컴포넌트 객체 내에서 components라는 옵션을 만들고 그곳에 템플릿에서 사용할 태그명과 컴포넌트 객체를 등록하여 사용
import CheckboxItem from './components/CheckboxItem.vue'
export default {
name : "App",
components: { "CheckboxItem" : CheckboxItem },
}
■ 최적안
- 지역 컴포넌트의 사용을 추천. 전역 컴포넌트로 등록할 경우 컴포넌트를 더이상 사용하지 않아도 빌드되는 패키지에 포함되어 사용자에게 배포될 코드의 크기를 증가시키기 때문이다.
- 전역 컴포넌트로 등록하는 경우는 여러 화면, 컴포넌트에서 공통적으로 자주 사용하는 컴포넌트일 때로 한정하는 편이 바람직하다.
- src/App.vue 다시 작성
<template>
<div>
<h2> App 컴포넌트 </h2>
<hr>
<ul>
<CheckboxItem />
<CheckboxItem />
<CheckboxItem />
<CheckboxItem />
</ul>
</div>
</template>
<script>
import CheckboxItem from './components/CheckboxItem.vue'
export default {
name: "App",
components: { CheckboxItem },
}
</script>
3. 컴포넌트의 조합
- Vue 애플리케이션은 여러 컴포넌트들을 조합하여 개발한다.
- 컴포넌트들은 속성(Props)을 통해서 자식 컴포넌트로 정보를 전달할 수 있다. 전당 방향은 부모에서 자식으로만 향한다.
- 자식 컴포넌트는 부모 컴포넌트로 이벤트를 발신할 수 있다.
- 자식 컴포넌트에서 사용자 정의 이벤트를 정의하고, 이벤트를 발생시키면 부모 컴포넌트에서 이벤트 핸들러 메소드를 호출하도록 작성한다.
4. 속성
- 자식 컴포넌트는 props 옵션으로 속성을 정의하고 부모 컴포넌트는 v-bind 디렉티브를 이용해 자식 컴포넌트의 속성에 정보를 전달한다.
- 속성으로 전달받은 데이터를 변경할 수 없다는 점, 속성으로 전달받은 값은 읽기 전용으로 다루어진다.
- 부모 컴포넌트에서 데이터를 변경하면, 부모 컴포넌트가 렌더링되면서 다시 속성을 전달하기 때문에 부모 컴포넌트의 데이터만 변경하면 속성을 전달 받는 모든 자식 컴포넌트에서 변경된 속성을 확인할 수 있다.
- Checkboxitem.vue
<template>
<li>
<input type="checkbox" :checked="checked" /> {{name}}
</li>
</template>
<script>
export default {
name : "ChecboxItem",
props : ["name", "checked"],
}
</script>
<style lang="stylus" scoped>
</style>
- App.vue
<template>
<div>
<h2> 관심있는 K-POP 가수 </h2>
<hr>
<ul>
<CheckboxItem v-for="idol in idols" :key="idol.id"
v-bind="idol" />
</ul>
</div>
</template>
<script>
import CheckboxItem from './components/CheckboxItem.vue'
export default {
name: "App",
components: { CheckboxItem },
data() {
return {
idols : [
{ id:1, name:"BTS", checked:true},
{ id:2, name:"Blak Pink", checked:false},
{ id:3, name:"2NE1", checked:false},
{ id:4, name:"ITZY", checked:false},
]
}
}
}
</script>
5. 사용자 정의 이벤트
- 사용자 정의 이벤트를 이용한 정보 전달
- $emit() 메소드 사용
this.$emit('event-name', eventArgs1, eventArgs2, ... )
- InputName.vue -> 작성
<template>
<div style="border: solid 1px gray; padding:5px;">
이름 : <input type="text" v-model="name" />
<button @click="$emit('nameChanged', { name })">이벤트 발신</button>
</div>
</template>
<script>
export default {
name : "InputName",
data() {
return {
name : ""
};
},
}
</script>
- App4.vue
<template>
<div>
<InputName @nameChanged="nameChangedHandler" />
<br>
<h3>App 데이터 : {{ parentName }}</h3>
</div>
</template>
<script>
import InputName from './components/InputName.vue'
export default {
name : "App4",
components : { InputName },
data() {
return { parentName: "" }
},
methods: {
nameChangedHandler(e) {
this.parentName = e.name;
}
},
}
</script>
- 자식 컴포넌트 인스턴스의 $emit() 메서드를 이용해서 이벤트를 발신한다.
- v-on 디렉티브로 자식 컴포넌트에서 발신한 이벤트명으로 이벤트를 수신해 등록한 핸들러 메서드를 호출한다.
- 이벤트 발신을 위한 $emit() 내장 메서드는 직계 부모 컴포넌트로만 이벤트 정보를 전송한다.
- 컴포넌트 계층 구조가 복잡할 때는 중간에 거쳐가는 컴포넌트에서 이벤트 정보를 받아서 다시 부모로 전달해야 한다.
6. 이벤트 유효성 검증
- emits 옵션을 등록하여, 발신 하는 이벤트에 대한 유효성 검사를 수행한다.
const Component = {
....
emits : ["이벤트명1", "이벤트명2"]
....
}
- 또 다른 방법
- 컴포넌트의 작동방식에 대한 명확한 문서화를 위해서도 emits 옵션을 정의할 것을 권장한다.
const Component = {
....
emits : {
이벤트명1 : (e) => {
//true가 리턴되면 유효
//false가 리턴되면 유효하지 않음
},
//유효성 검사 하지 않음
이벤트명2 : null,
....
},
.....
}
- 해당 방법을 사용하면 단순히 발신하는 이벤트명만 확인하는 것이 아니라 전달하는 이벤트 아규먼트의 유효성 여부까지 확인할 수 있다.
<script>
export default {
name : "InputName",
//emits : ["nameChanged1"],
emits: {
nameChanged: (e) => {
return e.name && typeof(e.name) === "string" && e.name.trim().length >= 3 ? true : false
}
},
data() {
return {
name : ""
};
},
}
</script>
7. 이벤트 에미터 사용
- 부모-자식-손자와 같이 계층적으로 복잡하게 컴포넌트가 구성된 경우 속성과 이벤트를 조합하여 애플리케이션을 개발할 수 있다.
- 이벤트를 일일이 경로를 거슬러 올라가도록 전달해야 하는 측면 불편함, 형제 관계에 있는 컴포넌트는 개발하기 힘들 수 있다.
- 이런 경우 사용하는 것이 에미터이다.
CTRL + C 를 눌러 Vue를 종료
npm install --save mitt
main.js
-> import mitt from 'mitt';
const emitter = mitt();
emitter.on('*', (type, e) => console.log(`## 이벤트 로그 : ${type} -> `, e))
const app = createApp(App)
app.config.globalProperties.emitter = emitter;
app.mount("#app");
■ Sender, Receiver 컴포넌트 작성
- Sender.vue
<template>
<div style="border: solid 1px gray; margin:5px; padding:5px;">
<h2>Sender</h2><hr>
<button @click="sendMessage">이벤트 발신</button>
</div>
</template>
<script>
export default {
name : "Sender",
methods: {
sendMessage() {
this.emitter.emit("message", Date.now() + "에 발신된 메시지");
}
},
}
</script>
- Receiver.vue
<template>
<div style="border: solid 1px gray; margin:5px; padding:5px;">
<h2>Receiver</h2>
<hr>
<h2>전송된 텍스트 : {{ textMessage }}</h2>
</div>
</template>
<script>
export default {
name : "Receiver",
created() {
this.emitter.on("message", this.receiveHandler);
},
data() {
return {
textMessage: ""
}
},
methods: {
receiveHandler(msg) {
this.textMessage = msg;
}
},
}
</script>
- App5.vue
<template>
<div>
<Sender />
<hr>
<Receiver />
</div>
</template>
<script>
import Receiver from './components/Receiver.vue'
import Sender from './components/Sender.vue'
export default {
name : "App5",
components : { Sender, Receiver }
}
</script>
- 결과
728x90
'Vue.js' 카테고리의 다른 글
Vue.js 3.0 STEP 7 - TodoList 리팩토링 (0) | 2024.01.25 |
---|---|
Vue.js 3.0 STEP 5 - TodoList (0) | 2024.01.25 |
Vue.js 3.0 STEP 4 - 스타일 적용 (0) | 2024.01.25 |
Vue.js 3.0 STEP 3 - 이벤트 처리 (0) | 2024.01.18 |
Vue.js 3.0 STEP 2 - Vue 인스턴스 (0) | 2024.01.18 |