02.Vue.js 문법
Updated:
1.Instance, lifecycle
<template>
<h1>8</h1>
</template>
<script>
export default {
data() {
return {
count: 2
}
},
beforeCreate() { // 아직 데이터가 정의 되기 전에 실행되는 구조이기 때문에, undefined 출력, 실제로 데이터가 생성되기 전이기때문에 거의 의미가 없음
console.log('Before Create!', this.count)
},
created() { // 작성하는 순서와 관계 없이 created 가 먼저 만들어 지고, count 가 출력됨
console.log('Created!', this.count)
console.log(document.querySelector('h1')) // html 조회 안됨
},
beforeMount() {
console.log('Before Mount!')
console.log(document.querySelector('h1')) // mount 가 되기 전이라 html 의 내용을 검색 할 수 없음
},
mounted() { // mounted 가 실행되는것을 확인 할 수 있음
console.log('Mounted')
console.log(document.querySelector('h1')) // template 의 h1 tag 가 잘 출력됨
}
}
</script>
2.템플릿 문법
// v-once 를 통해 event 가 한번만 실행 될 수 있게 됨 (최초 랜더링 이후에는 v-once 때문에 )
<template>
<h1
v-once
@click="add">
</h1>
</template>
<script>
export default {
data() {
return {
msg: 'Hello world!'
}
},
methods: {
add() {
this.msg += '!'
}
}
}
</script>
// v-html 원시로 html 을 사용하게 directive
<template>
<h1 v-html="msg"></h1>
</template>
<script>
export default {
data() {
return {
msg: '<div style="color: red;">Hello !! </div>'
}
},
methods: {
add() {
this.msg += '!'
}
}
}
</script>
// v-bind 를 통해 문자데이터를 삽입해서 사용함. 약어를 통해서 ESlint 를 통해서 생략되서 사용됨
<template>
<h1 :class="msg">
</h1>
</template>
<script>
export default {
data() {
return {
msg: 'active'
}
},
methods: {
add() {
this.msg += '!'
}
}
}
</script>
<style lang="scss">
.active {
color: royalblue;
font-size: 100px;
}
</style>
- attribute
// attr 이 실제 msg 의 class 가 되는것
<template>
<h1
:[attr]="'active'" // attribute 를 받아서 class에 active 가 될 수 있게 함. 대신 클릭시 에 ! 가 하나씩 늘어나는 함수 때문에 스타일이 적용이 안됨 -> "''" 이중으로 사용해서
@[event]="add">
</h1>
</template>
<script>
export default {
data() {
return {
msg: 'active',
attr: 'class', // 동적 전달인자
event: 'click'
}
},
methods: {
add() {
this.msg += '!'
}
}
}
</script>
<style lang="scss">
.active {
color: royalblue;
font-size: 100px;
}
</style>
3.Computed
- 계산된 데이터는 연산을 통해서 정의를 한 다음에 정의된 값을 반환되는 값을 사용하는 방법 -> computed
// in Fruits.vue (root/src/components)
<template>
<section v-if="hasFruit">
<h1>Fruits</h1>
<ul>
<li
v-for="fruit in fruits"
:key="fruit">
</li>
</ul>
</section>
<section>
<h1>Reverse Fruits</h1>
<ul>
<li
v-for="fruit in reverseFruits"
:key="fruit">
</li>
</ul>
</section>
</template>
<script>
export default {
data() {
return {
fruits: [
"Apple", "Banana", "Cheery"
]
}
},
computed: { // 계산된 데이터는 연산을 통해서 정의를 한 다음에 정의된 값을 반환되는 값을 사용하는 방법
hasFruit() { // fruits 길이가 0 보다 클때 의 조건을 return
return this.fruits.length > 0
},
reverseFruits() { // reverse() 함수 생성
return this.fruits.map(fruit => { // map 으로 새로운 arr 생성을 split으로 '' 기준으로 쪼개고, reverse() 시킨담에, join('') 으로 다시 합쳐줌
return fruit.split('').reverse().join('')
}) // 예): 'Apple' => ['A','p','p','l','e']
// => ['e','l','p','p','A'] => 'elppA'
}
}
}
</script>
// Fruits.vue 를 import 해서 실제 data 값으로 출력
<template>
<Fruits />
</template>
<script>
import Fruits from '~/components/Fruits'
export default {
components: {
Fruits
}
}
</script>
4.Computed Cashing
- 특정한 데이터들을 일단 정의를 해 놓고, 원본의 data를 손상 시키지 않고 computed (계산 시켜서) cashing 기능이 있기 때문에, 한번 계산된 data 는 반복적으로 사용해도, 저장된 cashing data 를 사용하기 때문에 메모리 부담없이 연속된 작업을 할때 유용합니다.
<template>
<h1></h1>
<h1></h1>
<h1></h1>
<h1></h1>
</template>
<script>
export default {
data() {
return {
msg: 'Hello Computed!'
}
},
computed: { // computed 에 만들어 놓은 계산된 data 는 cashing 이라는 기능이 있기 때문에 반복적으로 출력할때, 계산하지 않고 cash data 를 가지고 사용함
reversedMessge() { // 저장된 값을 화면으로 바로 출력 한다는 것임
return this.msg.split('').reverse().join('')
}
}
}
</script>
5.Getter, Setter
-
계산된 data 를 (Computed 된) getter 와 setter 부분을로 나누어서 사용할 수 있습니다. (기본적으로 computed 는 readOnly 상태 즉, getter 만 가능합니다).
-
Vuex(Store, 중앙 집중식 저장소)를 사용할때 유용하게 사용할 수 있습니다.
<template>
<button @click="add">
ADD
</button>
<h1></h1>
<h1></h1>
<h1></h1>
<h1></h1>
</template>
<script>
export default {
data() { // data 부분은 값을 읽는거 (Getter), 지정하는 (Setter)것 2개 다 가능함
return {
msg: 'Hello Computed!'
}
},
computed: { // computed data 는 Readonly (읽기 전용 Data 임) -> 값을 얻어내는 형태를 Getter 라고 함
// reversedMessge() {
// return this.msg.split('').reverse().join('')
// }
// 그래서 computerd 에서 setter 를 사용하기 위한 방법
// 나눠서 쓰는
reversedMessge: {
get() { // 기존 computed data 와 마찬 가지로
return this.msg.split('').reverse().join('')
},
set(value) { // 값을 setter 로 사용할 수 있게 됨
this.msg = value // 값을 받아서 return
}
}
},
methods: { // 위에
add() {
this.reversedMessge += '!?'
}
}
}
</script>
6.Watch
-
특정한 데이터가 변경되는것을 감지해서 추가적인 logic 을 실행 할 수 있는 옵션입니다.
-
일반적인 데이터 뿐만 아니라 계산된(computed) 데이터도 watch 할 수 있습니다.
-
감시하고 싶은 어떠한 데이터가 있다면, watch 부분에 하나의 methods 처럼 만들어서 그 logic 을 만들어서 데이터가 변경이 될때, 어떠한 내용을 실행할지 볼수 있음
<template>
<h1 @click="changeMessage">
</h1>
<h1></h1>
</template>
<script>
export default {
data() {
return {
msg: 'Hello?'
}
},
computed: {
reversedMessage() {
return this.msg.split('').reverse().join('')
}
},
watch: { // 특정한 데이터들의 변경사항을 감시하기 위한 옵션
msg(newValue) { // paremeter 매개변수는 아무거나 변경 가능함
console.log('msg: ', newValue)
},
reversedMessage(newValue) {
console.log('reversedMessage:', newValue)
}
},
methods: {
changeMessage() {
this.msg = 'Good!'
}
}
}
</script>
7.Class and Style Bindings
- class 명을 동적으로 data 에서 관리 해서 적용할 수 있습니다.
<template>
<!-- v-bind 사용 생략하면 : 만 사용 -> 즉, class='active'에서 true 이면 isActive 가 실행 되는것-->
<!-- click 을 하면 h1 tag 에 class='active' 가 활성화가 되는것 -->
<h1
:class="{active: isActive}"
@click="activate">
Hello?!()
</h1>
</template>
<script>
export default {
data() {
return {
isActive: false
}
},
methods: {
activate() {
this.isActive = true
}
}
}
</script>
<style lang="scss" scoped>
.active {
color: red;
font-weight: bold;
}
</style>
- 스타일 역시 data 를 통해 동적으로 관리 해줄 수 있습니다.
<template>
<!-- 여러개의 객체 데이터를 받기 위해서 style 에 array 를 사용하면 됨 -->
<h1
:style="[fontStyle, bacgroundStyle]"
@click="changeStyle">
Hello?!
</h1>
</template>
<script>
export default {
data() {
return {
fontStyle: { // fontStyle 의 객체 데이터
color: 'orange',
fontSize: '30px'
},
bacgroundStyle: { // backgroundStyle 관련 객체 데이터
backgroundColor: 'black'
}
}
},
methods: {
changeStyle() {
this.fontStyle.color = 'red'
this.fontStyle.fontSize = '50px'
}
}
}
</script>
8.Conditional Rendering(조건부 랜더링)
v-if, v-else-if, v-else
-
v-if 는 디렉티브 조건에 따라 블록을 랜더링할 때 사용합니다. 블록은 디렉티브의 표현식이 true 값을 반환할 때만 랜더링 됩니다.
-
awesome 값이 true 일때만 값이 return 됩니다
<h1 v-if="awesome">Vue is awesome!</h1>
- v-if, v-else-if, v-else 기본 예제
<template>
<!-- click 하게 되면 handler의 값인 false 가 return 되서 else 값인 good이 return -->
<button @click="handler">
Click me!
</button>
<h1 v-if="isShow">
Hello?!
</h1>
<h1 v-else-if="count > 3">
Count > 3
</h1>
<h1 v-else>
Good~
</h1>
</template>
<script>
export default {
data() {
return {
isShow: true,
count: 0
}
},
methods: {
handler() { // this 를 통한 현재 데이터는 true 인 상태인데 handler 에서는 ! 를 붙여서 반대의 값을 만들어서 isShow 에 값을 할당 하는것
this.isShow = !this.isShow
this.count += 1 // click 할때 마다 count 의 값이 1씩 증가함
}
}
}
</script>
-
특정한 요소들을 그룹으로 묶기 위해선 template tag 로 묶어서 사용하기
- 단, 최상위 위치에는 사용하면 적용이 안됩니다.
<template>
<!-- 특정한 요소들을 그룹으로 묶기 위해선 template tag 로 묶어서 사용하면 됨 (div X) 단! 최상위에다 붙이면 적용이 되지 않음-->
<button @click="handler">
Click me!
</button>
<template v-if="isShow">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
</template>
v-show
-
v-show
: 연결된 data 의 true, false 여부와 상관 없이 구조적으로 landering 하지만 false 시 화면상에 보이지 않음 => DOM 에 남아있음 (v-if
는 false 이기 landering 조차 하지 않음 :v-show
와 다른 부분) -
v-show
는<template>
element 를 지원하지 않으며,v-else
와 함께 쓸수 없습니다.
<template>
<!-- -->
<button @click="handler">
Click me!
</button>
<h1 v-show="isShow">
Hello?!
</h1>
</template>
v-if VS v-show
-
v-if
: runtime 시 조건이 변경되지 않을때 사용하기 유리 (false -> true 가 될때 마다, landering 작업 대신, true 일때만 랜더링 하기 때문에 초기 실행시 유리함) -
v-show
: 무언가를 자주 전환 해야 할때 사용하기 유리 (미리 landering 을 다 해놓음, 대신. 초기 랜더링시 불리함) -
즉, 사용자 입장에서 웹상에서 계속 반복적으로 사용하는 구조라면 되도록이면
v-show
를 사용하는것이 좋은데, 먼저v-if
로 coding 을 하고 전환 비용이 많이 발생 될꺼 같으면 나중에v-show
로 바꾸는 게 좋습니다.
9.List Landering(리스트 랜더링)
-
v-for
라는directive
로 배열 데이터를 출력 할때는, 꼭 각각의 배열의 아이템들을 구분해 줄 수 있는 특정한 속성이 존재를 해야 하고, 그 속성을 통해서 배열 아이템을 구분해서 사용할 수 있습니다. -
$ npm i -D shortid
: 각각의 ID를 고유하게 만들어 줄 수 있는 Package -
Vue 에서 Array change Detectio : 감시중인 배열의 변이 메소드를 래핑하여 뷰 갱신을 트리거 합니다. 래핑된 메소드는 다음과 같습니다.
- 변이 메소드는 호출된 배열을 변경합니다. 단, filter(), concat(), slice() 는 같은 원래 배열을 변경하지는 않지만 항상 새 배열로 return 합니다.
<template>
<button @click="handler">
Click me!
</button>
<ul>
<li
v-for="{id, name} in newFruits"
:key="id">
--
</li>
</ul>
</template>
<script>
import shortid from 'shortid' // shortid package 가져오기
export default {
data() {
return {
fruits: ['Apple', 'Banana', 'Cherry']
}
},
computed: {
newFruits() {
return this.fruits.map(fruit => ({
id: shortid.generate(), // 고유한 ID 값 생성
name: fruit
}))
}
},
methods: {
handler() { // button 을 클릭하면 fruits 에 Orange 추가
this.fruits.push('Orange')
}
}
}
</script>
10.Event Handling (이벤트 핸들링)
v-on
directive 는@
기호로, DOM 이밴트를 듣고 트리거 될 때와 JS 를 실행할 때 사용합니다.
- 이벤트 핸들러 란 : 쉽게 말해서 위의 예시에 @click 클릭을 했을 때, methods 부분에 정의된 함수를 호출해서 사용하는 것을 말함
<template>
<!-- 버튼을 누를때 msg 출력과 event 출력을 동시에 하기 위해서 $event 라고 구분 처리를 해줌 -->
<button @click="handler('Hi', $event)">
Click 1
</button>
<button @click="handler('What?', $event)">
Click 2
</button>
</template>
<script>
export default {
methods: {
handler(msg, event) { // 버튼을 클릭했을때, 하나의 event 객체가 첫번째 인수로 들어 올수 있다는 의미 임
console.log(msg)
console.log(event)
}
}
}
</script>
- 하나의 요소에 evnet 가 발생했을 때, 실행 할 메소드가 여러개라면 그것을 , (쉼표) 로 구분하되 () 소괄호를 사용해야지만 작동 한다는것을 주의 하여야 합니다.
<template>
<!-- 1개의 버튼을 눌렀을때, 동시에 handler 역활을 해주기 -->
<!-- 단! 주의 점은 handlerA() 라고 해야지 () 생략해서 handlerA, handlerB 라고 만 하면 작동하지 않음 -->
<button @click="handlerA(), handlerB()">
Click Me!
</button>
</template>
<script>
export default {
methods: {
handlerA() {
console.log('A')
},
handlerB() {
console.log('B')
}
}
}
</script>
Event Modifier (이벤트 수식어)
<template>
<!-- script 의 preventDefault 를 간략하게 @click.prevent 로 붙이면 html 에서 동작을 안함 -->
<!-- @click.once 는 handler 라는 event 를 단 한번만 동작 시킴 -->
<!-- method chaining 같이 붙여서도 사용이 가능함 prevent 되고 once 가 실행 되는것 -->
<a
href="https://google.com"
target="_blank"
@click.prevent.once="handler">
Google
</a>
</template>
<script>
export default {
methods: {
handler() {
// event.preventDefault() // 기본동작을 방지하는 method => HTML에 있는 기능을 하지 않고 단순하게 methods 만 실행 하겠다고 하는것
console.log('ABC!')
}
}
}
</script>
Event Bubbling
-
특정한 요소를 선택 했을 경우 자식선택자를 선택했을 때, 부모 선택자로 전파되는 되는 현상을 가리킵니다.
-
event bubbling 을 막기 위해서 아래 코드와 같이 실행 합니다.
<template>
<!-- 파란색영역 handlerB 를 클릭하면 B 가 출력되고 A 도 중첩되어 있는 영역이기 때문에 같이 출력되서 나옴 : 이벤트 버블링 이라고 함 -->
<!-- @click.stop 이라는 methods를 추가하면 script 부분에 event.stopPropagation() 의 효과를 HTML 부분에서 똑같이 사용할 수 있게 됨 -->
<div
class="parent"
@click="handlerA">
<div
class="child"
@click.stop="handlerB"></div>
</div>
</template>
<script>
export default {
methods: {
handlerA() {
console.log('A')
},
handlerB() {
// event.stopPropagation() // 이벤트 버블링이 생기지 않게 전파 방지하는것
console.log('B')
}
}
}
</script>
<style lang="scss">
.parent {
width: 200px;
height: 100px;
background-color: royalblue;
margin: 10px;
padding: 10px;
.child {
width: 100px;
height: 100px;
background-color: orange;
}
}
</style>
- event bubbling 이 일어나는 예시
- 위에 코드의
@click.stop
을 적용해서 bubbling 방지
Event Capturing (이벤트 캡처링)
- bubbling 과는 반대 개념으로 부모요소 event 가 자식요소에 영향을 미치는 현상을 말합니다.
<template>
<!-- @click.capture 를 하게되면 부모요소가 먼저 실행이 되고 자식요소가 실행됨 -->
<!-- @click.capture.stop 을 붙이게 되면 capture 부분만 선택이 됨-->
<div
class="parent"
@click.capture.stop="handlerA">
<div
class="child"
@click="handlerB"></div>
</div>
</template>
<script>
export default {
methods: {
handlerA() {
console.log('A')
},
handlerB() {
console.log('B')
}
}
}
</script>
<style lang="scss">
.parent {
width: 200px;
height: 100px;
background-color: royalblue;
margin: 10px;
padding: 10px;
.child {
width: 100px;
height: 100px;
background-color: orange;
}
}
</style>
@click.capture
만 하게 될경우..
@click.capture.stop
하게 될경우..
.self
- self 는 .target 과 .currentTarget 부분이 일치하는 경우에만 실행되는 경우를 말함
<template>
<!-- @click.self 중첩되지 않은 파란색 영역만 - 노출된 부분만 작동함 (오렌지 부분은 작동하지 않음) -->
<!-- self 는 .target 과 .currentTarget 부분이 일치하는 경우에만 실행되는 경우를 말함 -->
<div
class="parent"
@click.self="handlerA">
<div
class="child"></div>
</div>
</template>
<script>
export default {
methods: {
handlerA(event) {
console.log(event.target) // 클릭이 된 그 지점의 요소를 가리킴
console.log(event.currentTarget) // 실제 화면에서 클릭된 요소가 아니고, 실행된 함수가 연결이 되어져 있는 event 에 해당하는 부분을 가리킴
console.log('A')
},
handlerB() {
console.log('B')
}
}
}
</script>
<style lang="scss">
.parent {
width: 200px;
height: 100px;
background-color: royalblue;
margin: 10px;
padding: 10px;
.child {
width: 100px;
height: 100px;
background-color: orange;
}
}
</style>
.wheel.passive
<template>
<!-- @wheel 은 마우스의 휠을 돌릴때 handler method 가 실행되는 event 임 -->
<!-- @wheel.passive 를 사용하게 되면 화면의 스크롤 부분과 console 창의 event 를 완전히 독립을 시켜서 화면의 부하를 줄여줘서 사용자 입장에서는 부드럽게 처리 할 수 있음 -->
<!-- passive를 사용하면 최대 5대 정도의 성능이 향상 됨 -->
<div
class="parent"
@wheel.passive="handler">
<div
class="child"></div>
</div>
</template>
<script>
export default {
methods: {
handler(event) {
for(let i = 0; i < 10000; i++) { // logic 이 많게 부하를 임의로 적용
console.log(event)
}
}
}
}
</script>
<style lang="scss">
.parent {
width: 200px;
height: 100px;
background-color: royalblue;
margin: 10px;
padding: 10px;
overflow: auto;
.child {
width: 100px;
height: 2000px;
background-color: orange;
}
}
</style>
key Modifier (키 수식어)
-
event 안에있는 속성에 key 라는 수식어를 사용해서 쉽게 사용할 수 있습니다.
-
dash-case (kebab-case) 를 사용해서 수식어로 html 부분에 넣어줘서 사용하면 됩니다.
<template>
<!-- scrit 부분의 if 문 key 대신에 @keydown.enter 를 사용하면 쉽게 key modify 할 수 있음 -->
<!-- method chaining 도 가능해서 ctrl.enter 하게 되면 두개 동시에 눌러야지 handler 가 실행되는 method -->
<input
type="text"
@keydown.ctrl.enter="handler" />
</template>
<script>
export default {
methods: {
handler() {
// if (event.key === 'Enter') { // enter 키를 누를경우 console 창에 Enter 라는 메세지 출력됨
console.log('Enter!!')
// }
}
}
}
</script>
11.Form Input Bindings (폼 입력 바인딩)
- 단방향 데이터 바인딩
<template>
<!-- tag 안에서 data를 연결해서 사용할때는 v-bind: 사용해야 되는데 생략해서 : 만 사용하면 됨 -->
<!-- script 부분에서 data를 받아서 사용하는것이기 때문에 단방향 데이터 바인딩 이라고 함 -->
<!-- 그래서 template 에서 value 값을 변경하더라도 script 부분이 변경되지 않음.. (단방향 데이터라서..) -->
<h1></h1>
<input
type="text"
:value="msg" />
</template>
<script>
export default {
data() {
return {
msg: 'Hello world!'
}
}
}
</script>
- 양방향 데이터 바인딩
<template>
<!-- @input 요소는 type 창에 data 를 입력 할때 handler 가 작동하는 방식임 -->
<h1></h1>
<input
type="text"
:value="msg"
@input="handler" />
</template>
<script>
export default {
data() {
return {
msg: 'Hello world!'
}
},
methods: {
handler(event) {
this.msg = event.target.value
}
}
}
</script>
// - 양방향 데이터의 흐름
1. script 의 msg data 가 template 의 h1 과 input tag 에 출력이 됨
2. input 요소를 통해 사용자가 어떠한 value 를 입력 했을 때, input 이라는 event 가 실행되고 그 안의 handler 라는 method 가 실행됨
3. handler 라는 method 가 실행되면서, 객체의 타겟의 value 가 실행되면서 msg 라는 data 에 다시 할당해서
4. msg 가 갱신 됬기 때문에 다시 h1 과 input tag 에 출력하게 됨 (반응성을 통해서 화면이 바뀌게 됨)
-
Vue.js 의 간소화 된 양방향 데이터 바인딩
-
v-model
이라는directive
를 사용해서 간단하게 데이터 양방향을 만들 수 잇습니다.
<template>
<!-- v-model 이라는 directive 를 통해서 msg , checkbox 를 양방향 데이터 바인딩을 사용할 수 있음 -->
<h1></h1>
<input
type="text"
v-model="msg" />
<h1></h1>
<input
type="checkbox"
v-model="checked" />
</template>
<script>
export default {
data() {
return {
msg: 'Hello world!',
checked: false
}
}
}
</script>
-
🔶 주의 할점 : vue.js 에서 v-model 을 사용할 때, 한글을 입력을 할때는 한박자가 늦게 동작을 합니다. (한글자가 다써지고 다음 글자로 넘어 갈때 변경하는게 바뀜)
- 그래서 v-model을 사용하지 않고 원시적인 1차 내용으로 수동으로 만들어서 사용해야지 즉각 반응이 일어납니다.
<template>
<!-- 주의 한글을 사용할때는 v-model 보다 원시적인 1차 내용으로 수동으로 만들어서 사용해야지 즉각 반응이 일어남 -->
<h1></h1>
<input
type="text"
:value="msg"
@input="msg = $event.target.value" />
<h1></h1>
<input
type="checkbox"
v-model="checked" />
</template>
<script>
export default {
data() {
return {
msg: 'Hello world!',
checked: false
}
}
}
</script>
12.V-model argumetnst (v-model 수식어)
-
v-model.lazy
: lazy 라고 수식어를 붙여 있을땐, 바로 변하는것이 아니라 enter, tap 키를 눌렀을때 value 값이 변환됨 -
v-model.number
: value 값이 숫자로 넘어 올때, input 에서 값이 변하면 return 되는게 string 으로 됩니다. 이것을 방지하기 위해서v-model.number
을 사용해서 number 로 return 할 수 있습니다. -
v-model.trim
: value 값 앞쪽에 공백(space) 이 생기면 trim 을 붙이면 앞,뒤에 공백을 없애 줍니다.
🔶 🔷 📌 🔑
Reference
- Vue.js guide v3.0 - https://v3.vuejs.org/guide/introduction.html
Leave a comment