vscode 설치
vscode plugins 설치
Live Server
Vetur
Meterial Icon Theme
node 설치
vue cli 설치
$ npm install -g @vue/cli
기타
# # vue-router
$ npm install vue-router
# # bootstrap
$ npm install vue bootstrap-vue bootstrap
$ vue create modal-proj
- Manually select features
- babel
- version 3
- dedicated config files (yes)
- save as a preset (no)
# # 의존성 설치
$ npm install
# # 개발 실행
$ npm run serve
# # 프로덕션 실행
$ npm run build
vue-proj-name/
node-modules/
public/
index.html
src/
assets/
components/
router/
views/
App.vue
main.js
package.json
< template >
</ template >
< script >
export default {
}
</ script >
< style >
</ style >
< script >
import HomeView from './views/HomeView.vue' ;
export default {
name : 'App' ,
components : {
HomeView,
} ,
props : [
] ,
data ( ) {
return {
appTitle : '앱 제목' ,
}
} ,
methods : {
handleClick ( e ) {
console . log ( 'handleClick' , e . target ) ;
console . log ( this . appTitle ) ;
}
} ,
computed : {
} ,
watch : {
appTitle ( ) {
console . log ( 'watch appTitle' , appTitle ) ;
}
} ,
// Lifecycle Hook
beforeCreate ( ) {
console . log ( 'beforeCreate' ) ;
} ,
created ( ) {
console . log ( 'created' ) ;
} ,
beforeMount ( ) {
console . log ( 'beforeMount' ) ;
} ,
mounted ( ) {
console . log ( 'mounted' ) ;
} ,
beforeUpdate ( ) {
console . log ( 'beforeUpdate' ) ;
} ,
updated ( ) {
console . log ( 'updated' ) ;
} ,
beforeUnmount ( ) {
console . log ( 'beforeUnmount' ) ;
} ,
unmounted ( ) {
console . log ( 'unmounted' ) ;
} ,
errorCaptured ( ) {
console . log ( 'errorCaptured' ) ;
} ,
activated ( ) {
console . log ( 'activated' ) ;
} ,
deactivated ( ) {
console . log ( 'deactivated' ) ;
} ,
renderTracked ( ) {
console . log ( 'renderTracked' ) ;
} ,
renderTriggered ( ) {
console . log ( 'renderTriggered' ) ;
} ,
beforeDestroy ( ) {
console . log ( 'beforeDestroy' ) ;
} ,
destroyed ( ) {
console . log ( 'destroyed' ) ;
} ,
}
</ script >
<!-- vue js script 삽입 -->
< script src ="https://unpkg.com/[email protected] "> </ script >
<!-- app container -->
< div id ="app ">
</ div >
// app 생성
const app = Vue . createApp ( {
data ( ) {
return {
showBooks : true ,
blogs : [
{ title : 'title1' , content : 'content1' , img : 'assets/1.png' , isFav : true } ,
{ title : 'title2' , content : 'content2' , img : 'assets/2.png' , isFav : true } ,
{ title : 'title3' , content : 'content3' , img : 'assets/3.png' , isFav : true } ,
]
}
} ,
methods : {
} ,
computed : {
}
} )
// div 에 마운트
app . mount ( '#app' ) ;
input 에 값을 입력하면 model 의 값이 변경된다
model 값이 변경되면 input 의 내용도 변경된다
< template >
< div > {{ myVar1 }}</ div > <!-- myVar1 바인딩 -->
</ template >
< script >
export default {
data ( ) {
return {
myVar1 : 'hello world' , // myVar1 정의
}
} ,
</ script >
v-bind:attribute
:attribute
< template >
<!-- basic -->
< h4 v-bind:style ="myStyle "> this is heading 1</ h4 > <!-- myStyle 바인딩 -->
<!-- shortcut -->
< h4 :style ="myStyle "> this is heading 2</ h4 > <!-- myStyle 바인딩 -->
</ template >
< script >
export default {
data ( ) {
return {
myStyle : 'color: red;' , // myStyle 정의
}
} ,
}
</ script >
<!-- v-model 을 사용 -->
< template >
< form >
< label > Email:</ label >
< input type ="email " required v-model ="email "> <!-- here -->
</ form >
< p > {{ email }}</ p >
< button @click ="handleClick "> click me</ button >
</ template >
< script >
export default {
data ( ) {
return {
email : '' // here
} ;
} ,
methods : {
handleClick ( ) {
this . email = 'aa' ; // here
}
}
}
</ script >
< template >
<!-- basic -->
< button v-on:click ="handleClick "> click btn 1</ button >
<!-- shortcut -->
< button @click ="handleClick "> click btn 2</ button >
</ template >
< script >
export default {
methods : {
handleClick ( e ) {
console . log ( e . target ) ;
}
} ,
}
</ script >
@click.self
@click.right
마우스 우클릭
@click.shift
shift 키보드 누른채로 클릭
@submit.prevent
e.preventDefault()
<!-- modal 팝업창을 클릭해도 반응하도록 변경 -->
< button @click.self ="closeModal ">
< li :class ="{ fav: blog.isFav } ">
< script >
export default {
computed : {
filterBlogs ( ) {
return this . blogs . filter ( blog => blog . isFav ) ;
}
}
}
</ script >
< li v-for ="blog in blogs " :key ="blog "> {{ blog }}</ li >
< li v-for ="(blog, i) in blogs " :key ="i "> {{ blog }}, {{ i }}</ li >
< template >
< input type ="text " ref ="name "> <!-- here -->
< button @click ="handleClick "> click here</ button >
</ template >
< script >
export default {
methods : {
handleClick ( ) {
console . log ( this . $refs . name ) ; // here
}
}
}
</ script >
<!-- src/components/Modal.vue -->
< template >
< div class ="backdrop ">
< div class ="modal ">
< p > modal content</ p >
</ div >
</ div >
</ template >
<!-- src/App.vue -->
< template >
< Modal /> <!-- here -->
</ template >
< script >
import Modal from './components/Modal.vue' // here
export default {
name : 'App' ,
components : {
Modal // here
}
}
</ script >
자식 컴포넌트에 데이터 전달 (props 사용)
부모 Component
<!-- App.vue -->
< template >
< Modal header ="this is title " message ="this is message " /> <!-- here -->
</ template >
< script >
import Modal from './components/Modal.vue'
export default {
name : 'App' ,
components : {
Modal
}
}
</ script >
자식 Component
<!-- Modal.vue -->
< template >
< div class ="backdrop ">
< div class ="modal ">
< h1 > {{ header }}</ h1 > <!-- here -->
< p > {{ message }}</ p > <!-- here -->
</ div >
</ div >
</ template >
< script >
export default {
props : [ 'header' , 'message' ] // here
}
</ script >
자식 컴포넌트에 데이터 전달 (binding 변수 사용)
부모 Component
<!-- App.vue -->
< template >
< Modal :header ="header " :message ="message " /> <!-- here -->
</ template >
< script >
import Modal from './components/Modal.vue'
export default {
name : 'App' ,
components : {
Modal
} ,
data ( ) {
return {
header : 'title 22' , // here
message : 'content 22' , // here
}
}
}
</ script >
자식 Component
<!-- Modal.vue -->
< template >
< div class ="backdrop ">
< div class ="modal ">
< h1 > {{ header }}</ h1 > <!-- here -->
< p > {{ message }}</ p > <!-- here -->
</ div >
</ div >
</ template >
< script >
export default {
props: ['header', 'message'] // here
}
자식 컴포넌트에 dynamic css class 전달
부모 Component
<!-- App.vue -->
< template >
< Modal :header ="header " :message ="message " theme ="sale " /> <!-- here -->
</ template >
자식 Component
<!-- Modal.vue -->
< template >
< div class ="backdrop ">
< div class ="modal " :class ="{ sale: theme === 'sale' } "> <!-- here -->
< h1 > {{ header }}</ h1 >
< p > {{ message }}</ p >
</ div >
</ div >
</ template >
< script >
export default {
props : [ 'header' , 'message' , 'theme' ] // here
}
</ script >
< style >
/* here */
.sale {
color: orange;
}
</ style >
this.$emit('close');
자식 Component
<!-- Modal.vue -->
< template >
< div class ="backdrop " @click ="closeModal "> <!-- here -->
< div class ="modal ">
< h1 > {{ header }}</ h1 >
< p > {{ message }}</ p >
</ div >
</ div >
</ template >
< script >
export default {
props : [ 'header' , 'message' ] ,
methods : {
closeModal ( ) {
this . $emit ( 'close' ) ; // here, close 이벤트 발생
}
}
}
</ script >
부모 Component
<!-- App.vue -->
< template >
< div v-if ="showModal ">
< Modal @close ="toggleModal " /> <!-- here, close 이벤트 리스닝 -->
</ div >
< button @click ="toggleModal "> show modal</ button >
</ template >
< script >
import Modal from './components/Modal.vue'
export default {
name : 'App' ,
components : {
Modal
} ,
data ( ) {
return {
showModal : false ,
}
} ,
methods : {
toggleModal ( ) { // here
this . showModal = ! this . showModal ;
}
}
}
</ script >
/* src/assets/global.css */
// src/main.js
import './assets/global.css' ;
<!-- App.vue -->
< Modal theme ="sale " @close ="toggleModal ">
<!-- slot 에 전달할 내용 -->
< h1 > title from app</ h1 >
< p > content from app</ p >
<!-- slot 에 전달할 내용 -->
</ Modal >
<!-- Modal.vue -->
< div class ="modal " :class ="{ sale: theme === 'sale' } ">
< slot > </ slot > <!-- slot 태그 블럭은 전달되는 내용으로 대체된다 -->
</ div >
< template v-slot:links >
</ template >
<slot name="links"></slot>
<!-- App.vue -->
< Modal theme ="sale ">
< template v-slot:links > <!-- here -->
< a href ="# "> sign up</ a >
< a href ="# "> more info</ a >
</ template >
</ Modal >
<!-- Modal.vue -->
< div class ="modal ">
< slot name ="links "> </ slot > <!-- here -->
</ div >
<teleport to="#modals" v-if="showModal">
<!-- index.html -->
< div id ="modals "> </ div >
<!-- App.vue -->
< teleport to ="#modals " v-if ="showModal ">
< Modal @close ="toggleModal ">
< h1 > modal2 title</ h1 >
< p > modal2 content</ p >
</ Modal >
</ teleport >
form submit (prevent default)
< template >
< form @submit.prevent ="handleSubmit ">
</ template >
< script >
export default {
methods : {
handleSubmit ( ) {
console . log ( 'handleSubmit' ) ;
}
}
}
</ script >
// src/router/index.js
const routes = [
{
path : '/jobs' ,
name : 'Jobs' ,
component : Jobs
} ,
{
path : '/jobs/:id' ,
name : 'JobDetails' ,
component : JobDetails
}
]
<!-- src/App.vue -->
< router-link :to ="{ name: 'Jobs' } "> Jobs</ router-link >
<!-- src/views/jobs/Jobs.vue -->
< div v-for ="job in jobs " :key ="job.id ">
< router-link :to ="{ name: 'JobDetails', params: {id: job.id} } ">
< h2 > {{ job.title }}</ h2 >
</ router-link >
</ div >
<!-- src/views/jobs/JobDetails.vue -->
< template >
< p > {{ $route.params.id }}</ p >
</ template >
< script >
export default {
data ( ) {
return {
id : this . $route . params . id
}
}
}
</ script >
// src/router/index.js
const routes = [
{
path : '/jobs/:id' ,
name : 'JobDetails' ,
component : JobDetails ,
props : true /* here */
}
]
<!-- src/views/jobs/JobDetails.vue -->
< template >
< p > {{ id }}</ p > <!-- here -->
</ template >
< script >
export default {
props : [ 'id' ] , /* here */
}
</ script >
// src/router/index.js
const routes = [
{
path : '/all-jobs' ,
redirect : '/jobs'
}
]
// src/router/index.js
import NotFound from '../views/NotFound.vue'
const routes = [
{
path : '/:catchAll(.*)' ,
name : 'NotFound' ,
component : NotFound
}
]
<!-- src/views/NotFound.vue -->
< template >
< h2 > 404</ h2 >
< h3 > Page not found</ h3 >
</ template >
router | redirect, back, forward
// redirect
this . $router . push ( { name : 'Home' } ) ;
// back
this . $router . go ( - 1 ) ;
// forward
this . $router . go ( 1 ) ;
# # json-server 설치
$ npm install json-server
# # data/db.json 파일을 rest api 로 서비스
$ json-server --watch data/db.json --port 3000
<!-- src/views/Jobs.vue -->
< template >
< h1 > Jobs</ h1 >
< div v-for ="job in jobs " :key ="job.id "> <!-- here -->
< router-link :to ="{ name: 'JobDetails', params: {id: job.id} } ">
< h2 > {{ job.title }}</ h2 >
</ router-link >
</ div >
</ template >
< script >
export default {
data ( ) {
return {
jobs : [ ] // here
}
} ,
mounted ( ) { // here
fetch ( 'http://localhost:3000/jobs' )
. then ( res => res . json ( ) )
. then ( data => this . jobs = data )
. catch ( err => console . error ( err . message ) )
}
}
</ script >
<!-- src/views/jobs/JobDetails.vue -->
< template >
< h1 > JobDetails</ h1 >
<!-- <p>{{ $route.params.id }}</p> -->
<!-- <p>{{ id }}</p> -->
< p > {{ job && job.id }}</ p >
< p > {{ job && job.title }}</ p >
< p > {{ job && job.content }}</ p >
</ template >
< script >
export default {
props : [ 'id' ] ,
data ( ) {
return {
id : this . $route . params . id ,
job : null
}
} ,
mounted ( ) {
fetch ( 'http://localhost:3000/jobs/' + this . id )
. then ( res => res . json ( ) )
. then ( data => this . job = data )
. catch ( err => console . error ( err . message ) )
}
}
</ script >
setup()
메소드 하나에서 모두 사용
setup()
메소드에서 2-way binding 을 사용하려면
ref
나 reactive
를 사용하면 되는데
reactive
는 primitive 값이 변경되지 안된다
ref
를 사용
import { ref , reactive } from '@vue/reactivity'
export default {
setup ( ) {
// data
// methods
// computed
// lifecycle hooks
}
}