어플리케이션 & 컴포넌트 인스턴스, 라이프사이클, 클래스바인딩, 조건부렌더링, 상태유지
배열변경감지(무시), 이벤트 핸들링, 이벤트 수식어, 키수식어
Vue.createApp(App).mount('#app')
최상위 컴포넌트 App은 생명주기(라이프사이클)을 가집니다.
각 컴포넌트는 생성될 때 일련의 초기화 단계를 거칩니다. 예를들어 데이터 관찰, 템플릿 컴파일, 인스턴스를 DOM에 마운트, 데이터 변경 시 DOM을 업데이트해야합니다. 그 과정에서 라이프사이클 훅이라 불리우는 함수도 실행하여, 사용자가 특정 단계에서 자신의 코드를 추가할 수 있는 기회를 제공합니다.
created: 컴포넌트가 생성되고 연결이 되기 전
moutend: 컴포넌트가 연결된 직후
<template>
<input
v-model="title"
@keydown.enter="searchMovies(true)" />
<button @click="searchMovies(true)">
Search!
</button>
<ul>
<li
v-for="movie in customMovies"
:key="movie.id">
<img
:src="movie.poster"
alt=""
height="40" />
{{ movie.title }}
</li>
</ul>
<div>Loading...</div>
</template>
<script>
export default {
data() {
return {
title:'',
page:1,
movies:[],
msg: 'HEROPY?!'
}
},
methods: {
async searchMovies(isFirst) {
console.log(isFirst)
if(isFirst) {
//밑에서 push로 밀어넣기 위해서 빈배열로 초기화
this.movies = []
this.page=1
}
let res=await fetch(`https://www.omdbapi.com?apikey=7035c60c&s=${this.title}&page=${this.page}`)
res = await res.json()
const { Search, totalResults } = res
this.movies.push(...Search)
this.page += 1
},
}
}
</script>
vue.js의 경우 작성된 html의 코드를 가져와 내부에서 다시 코드를 만듭니다. 따라서 어디에 어떤 요소가 있는지 알고 있기 때문에 document.querySelector
을 사용할 일이 거의 없습니다.
대신 ref
속성을 사용합니다. 사용한 ref속성은 this.$refs.[설정한 ref속성이름]
으로 사용을 할 수 있습니다.
<div ref="observer">
Loading...
</div>
created() {
const io = new IntersectionObserver(entries => {
entries.forEach(entry => {
if(entry.isIntersecting) {
console.log('교차되었음')
}
})
})
io.observe(this.$refs.observer)
},
그런데 위와 같이 에러가 발생합니다. created는 요소에 연결되기 전이기 떄문에 발생하는 문제로 created대신 mounted에 연결해줘야합니다.
mounted() {
const io = new IntersectionObserver(entries => {
entries.forEach(entry => {
if(entry.isIntersecting) {
console.log('교차되었음')
}
})
})
io.observe(this.$refs.observer)
},
이렇게 cretaed를 먼저 고려한 후 created가 안되는 경우 mounted를 고려해보면 됩니다.
<div
v-if="hasTheRest"
ref="observer">
Loading...
</div>
data() {
return {
total:0,✅
}
},
computed: {
hasTheRest() {
return this.total > this.movies.length✅
}
},
methods: {
async searchMovies(isFirst) {
console.log(isFirst)
if(isFirst) {
//밑에서 push로 밀어넣기 위해서 빈배열로 초기화
this.movies = []
this.page=1
}
let res=await fetch(`https://www.omdbapi.com?apikey=7035c60c&s=${this.title}&page=${this.page}`)
res = await res.json()
const { Search, totalResults } = res
this.movies.push(...Search)
this.page += 1
//totalResults의 경우 문자이기 때문에 숫자로 변환해줘야합니다.
this.total=Number(totalResults)✅
},
}
최초 영화검색이 시작되기 전의 경우 v-if="hasTheRest"로 인해 화면에 요소의 렌더링 자체가 진행되지 않기 떄문에 다음과 같은 에러가 발생합니다.
이 때 사용을 할 수 있는 것이 바로 v-show입니다.
그런데 화면이 더 커 화면과 요소가 교차하지 않는 경우에는 버그가 생기게 됩니다.
이 문제를 해결하기 위해 searchMovies가 한번 동작을 하면 일시적으로 깜박이게 만들어줍니다.
<div
v-show="hasTheRest && showObserver"
ref="observer">
Loading...
</div>
data() {
return {
showObserver: true
}
methods: {
async searchMovies(isFirst) {
console.log(isFirst)
if(isFirst) {
//밑에서 push로 밀어넣기 위해서 빈배열로 초기화
this.movies = []
this.page=1
}
let res=await fetch(`https://www.omdbapi.com?apikey=7035c60c&s=${this.title}&page=${this.page}`)
res = await res.json()
const { Search, totalResults } = res
this.movies.push(...Search)
this.page += 1
//totalResults의 경우 문자이기 때문에 숫자로 변환해줘야합니다.
this.total=Number(totalResults)
this.sbowObserver=false✅
this.sbowObserver=true✅
},
}
그런데 위와같이 작성을 하는 경우 작동을 하지 않습니다.
기본적으로 vue.js가 searchMovies라는 함수를 평가한 후에 그거에 맞게 화면에 반영을 하기 때문입니다. 이문제를 해결하기 위해 setTimeOut을 사용할 수 있습니다.
this.sbowObserver=false
setTimeout(() => {
this.sbowObserver=true
})
vue.js에서 제공하는 nextTick을 이용해 setTimeout과 비슷하게 작동을 시킬 수 있습니다.
nextTick은 화면이 갱신되고 난 후에 로직을 수행할 수 있도록 합니다.
this.sbowObserver=false
this.$nextTick(() => {
this.sbowObserver=true
})
<button
:class="{active: movies.length}"
@click="searchMovies(true)">
Search!
</button>
movies라는 배열 데이터가 있는 경우 active라는 클래스를 붙이겠다.