Vue.js
|
Vue(/vjuː/ 로 발음, view 와 발음이 같습니다.)는 사용자 인터페이스를 만들기 위한 진보적인 프레임워크 입니다. 다른 단일형 프레임워크와 달리 Vue는 점진적으로 채택할 수 있도록 설계하였습니다. 핵심 라이브러리는 뷰 레이어만 초점을 맞추어 다른 라이브러리나 기존 프로젝트와의 통합이 매우 쉽습니다. 그리고 Vue는 현대적 도구 및 지원하는 라이브러리와 함께 사용한다면 정교한 단일 페이지 응용프로그램을 완벽하게 지원할 수 있습니다.
Categories
- Vue Cheat sheet - 그냥 직접 페이지 참조.
- Vue:MultipleApps - One Host, Multiple Vue Apps
- Vue:API
- 설치방법
- 시작하기
- Vue 인스턴스
- Vue:Lifecycle
- Vue:Template - 템플릿 문법
- v-bind (축약어
:
) - v-on (축약어
@
) - 머스태쉬 (Mustache) (
{{
와}}
)을 사용한 텍스트 보간.
- v-bind (축약어
- v-bind (축약어
:
) 사용방법.
-
$.computed
,$.computed.get
,$.computed.set
정의 방법.
- 컴포넌트의
$on(eventName)
,$emit(eventName)
,v-on
이벤트에 대한 내용.
- this.$watch 문법
- v-on (축약어
@
)에 대한 내용,$event
변수.
- ref속성과, this.$refs를 사용한 자식 컴포넌트의 인스턴스 및 요소에 접근.
-
$nextTick
사용 방법.
Libraries
- Vue.js 애플리케이션에 대한 상태 관리 패턴 + 라이브러리 입니다.
- this.$store를 사용한 상태 저장소.
- Vue Single File Components -
*.vue
파일. - Scoped CSS -
<style scoped>
같은거.
- vue.config.js - Vue CLI의 설정파일
vue.config.js
에 대한 설명. - VueCLI:Plugins
- VueCLI:Service
Chart libraries
- Top Vue.js Chart Components
- JSCharting
- Vue FusionCharts
- Syncfusion Vue Charts
- Vue Trend
- Highcharts
- ZingCharts
- Vue ECharts
- Frappe Charts
- Chart.js
Project Template
Vue CLI 5.x 부터 babel 과 eslint 만 딱 설치되고 마는듯...
- vue2, babel, eslint, typescript, vue-router, vuex
- vue2, babel, eslint, typescript, vue-router, vuex, sass
- vue2, babel, eslint, typescript, vue-router, vuex, sass, jest
- vue2, babel, eslint, typescript, vue-router, vuex, sass, jest, cypress
- vue2, babel, eslint, typescript, vue-router, vuex, sass, jest, cypress, pwa
VSCode Plugin
Examples
- Vue:Components:App - 기본 App 컴포넌트.
- Baserow:Scrollbars - Baserow에서 사용하는 Scrollbars 컴포넌트의 TypeScript 변환 버전.
- Vue Router#HTML5 히스토리 모드 - vue-router를 frontend로, backend는 aiohttp로 사용할 경우 배포시 주의사항.
E.T.C
- ESLint
- NativeScript - Native app.
Hello world
index.html
파일은 아래와 같다.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Vue</title>
<!-- CSS -->
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
</head>
<body>
<div id="app">
{{ message }}
</div>
<!-- JS -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="app.js"></script>
</body>
</html>
app.js
파일은 아래와 같다:
TypeScript 코딩 준비
vue/valid-v-slot error
다음과 같이 코딩하면,
<template>
<!-- ... -->
<template v-slot:item.enable="{item}"> <!-- WARNING !! -->
<v-btn icon @click="click"></v-btn>
</template>
<!-- ... -->
</template>
해결 방법은 v-slot:item.enable="{item}"
를 v-slot:item[`enable`]="{item}"
로 바꿔주면 된다.
또는 ESLint에 다음 룰을 추가한다:
Unrecognized slot name
<template v-slot:item.server_running="{item}">
<v-icon v-show="item.server_running" small disabled>
{{ icons.mdiCheck }}
</v-icon>
</template>
IntelliJ계열 사용시 item.server_running
부분에서 "Unrecognized slot name" 경고가 출력된다.
자식 요소 접근하기
- 예외적인 상황들 — Vue.js - "자식 컴포넌트의 인스턴스 및 요소에 접근하기" 항목 참조.
- Vue.js의 $refs를 통해 dom에 접근하기
물론 props와 events가 존재하지만, 가끔 JavaScript에서 자식 요소에 직접 접근해야 하는 경우가 있습니다. 이 경우, ref
속성을 이용해 자식 요소에 레퍼런스 ID를 할당하여 해결할 수 있습니다. 예를 들어:
이제 ref
를 이용해 ID를 정의한 컴포넌트 안에서 아래와 같이 작성하면:
<base-input> 인스턴스에 접근할 수 있습니다.
Example
html file:
<div id="app">
<h1>{{ message }}</h1>
<button ref="myButton" @click="clickedButton">Click Me!</button>
</div>
javascript file:
var vm = new Vue({
el: '#app',
data: {
message: 'Hello World!'
},
methods: {
clickedButton: function() {
console.log(this.$refs);
this.$refs.myButton.innerText = this.message;
}
}
});
env 파일 접근
vue2의 경우 웹팩 설정에 다음 내용을 추가:
// webpack.config.js
const webpack = require('webpack');
const dotenv = require('dotenv');
const env = dotenv.config().parsed;
plugins: [
new webpack.DefinePlugin({
VUE_APP_LOCAL_URI: JSON.stringify(env.VUE_APP_LOCAL_URI),
}),
],
vue3의 경우 변수명을 항상 VUE_APP_
으로 지정하면 알아서 로드된다.
엘리먼트 접근하기
$el
를 사용하면 된다.
re-rendering 조건에 대하여
data
에 할당된 속성만 재정의 되므로 하위 속성을 수정하면 re-rendering 안된다.
또한 re-rendering 시키고 싶다면 key에 할당된 속성을 변경하면 된다.
Lifecycle
Vue:Lifecycle 항목 참조. 다이어그램은 다음과 같다:
Vue-lifecycle.png
Vue 인스턴스 가이드
인스턴스에서 사용할 수 있는 속성과 API는 다음과 같다.
- el - 인스턴스가 그려지는 화면의 시작점 (특정 HTML 태그)
- template - 화면에 표시할 요소 (HTML, CSS 등)
- data - 뷰의 반응성(Reactivity)이 반영된 데이터 속성
- methods - 화면의 동작과 이벤트 로직을 제어하는 메서드
- created - 뷰의 라이프 사이클과 관련된 속성
- watch - data에서 정의한 속성이 변화했을 때 추가 동작을 수행할 수 있게 정의하는 속성
Reactivity in Depth
변경 내용을 추적하는 방법
Vue 인스턴스에 JavaScript 객체를 data
옵션으로 전달하면 Vue는 모든 속성에 Object.defineProperty를 사용하여 getter/setters로 변환합니다. 이는 Vue가 ES5를 사용할 수 없는 IE8 이하를 지원하지 않는 이유입니다.
getter / setter 는 사용자에게는 보이지 않으나 속성에 액세스 하거나 수정할 때 Vue가 종속성 추적 및 변경 알림을 수행할 수 있습니다. 한가지 주의 사항은 변환된 데이터 객체가 기록될 때 브라우저가 getter / setter 형식을 다르게 처리하므로 친숙한 인터페이스를 사용하기 위해 vue-devtools를 설치하는 것이 좋습니다.
모든 컴포넌트 인스턴스에는 해당 watcher 인스턴스가 있으며, 이 인스턴스는 컴포넌트가 종속적으로 렌더링되는 동안 "수정"된 모든 속성을 기록합니다. 나중에 종속적인 setter가 트리거 되면 watcher에 알리고 컴포넌트가 다시 렌더링 됩니다.
Vue_-_how_to_track_changes.png
비동기 갱신 큐
눈치채셨는지 모르겠지만, Vue는 DOM 업데이트를 비동기로 합니다. 데이터 변경이 발견 될 때마다 큐를 열고 같은 이벤트 루프에서 발생하는 모든 데이터 변경을 버퍼에 담습니다. 같은 Watcher가 여러 번 발생하면 대기열에서 한 번만 푸시됩니다. 이 버퍼링된 중복의 제거는 불필요한 계산과 DOM 조작을 피하는 데 있어 중요합니다. 그 다음, 이벤트 루프 “tick”에서 Vue는 대기열을 비우고 실제 (이미 중복 제거 된) 작업을 수행합니다. 내부적으로 Vue는 비동기 큐를 위해 네이티브 Promise.then
와 MessageChannel
를 시도하고 setTimeout(fn, 0)
으로 돌아갑니다.
예를 들어, vm.someData = 'new value'
를 설정하면, 컴포넌트는 즉시 재 렌더링되지 않습니다. 큐가 플러시 될 때 다음 “tick” 에서 업데이트됩니다. 대개의 경우 이 작업을 신경 쓸 필요는 없지만 업데이트 후 DOM 상태에 의존하는 작업을 수행하려는 경우 까다로울 수 있습니다. Vue.js는 일반적으로 개발자가 “데이터 중심”방식으로 생각하고 DOM을 직접 만지지 않도록 권장하지만 때로는 건드려야 할 수도 있습니다. Vue.js가 데이터 변경 후 DOM 업데이트를 마칠 때까지 기다리려면 데이터가 변경된 직후에 Vue.nextTick(콜백)
을 사용할 수 있습니다. 콜백은 DOM이 업데이트 된 후에 호출됩니다.
HTML:
JavaScript:
var vm = new Vue({
el: '#example',
data: {
message: '123'
}
})
vm.message = 'new message' // 데이터 변경
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
vm.$el.textContent === 'new message' // true
})
또한 vm.$nextTick()
인스턴스 메소드가 있습니다. 이는 내부 컴포넌트들에 특히 유용합니다. 왜냐하면 전역 Vue
가 필요없고 콜백의 this
컨텍스트가 자동으로 현재 Vue 인스턴스에 바인드될 것이기 때문입니다
Vue.component('example', {
template: '<span>{{ message }}</span>',
data: function () {
return {
message: '갱신 안됨'
}
},
methods: {
updateMessage: function () {
this.message = '갱신됨'
console.log(this.$el.textContent) // => '갱신 안됨'
this.$nextTick(function () {
console.log(this.$el.textContent) // => '갱신됨'
})
}
}
})
$nextTick()
은 promise를 반환하므로, ES2017 async/await 문법을 사용하여 똑같은 동작을 수행할 수 있습니다.
methods: {
updateMessage: async function () {
this.message = '갱신됨'
console.log(this.$el.textContent) // => '갱신 안됨'
await this.$nextTick()
console.log(this.$el.textContent) // => '갱신됨'
}
}
몇 가지 주의 사항
-
<template v-slot:...> ...
</template>과 같은 템플릿 슬롯 안의 내용은 "반응형"이 적용되지 않을 때가 있더라... - v-model로 전달되는 변수가
Object
일 경우 Object의 멤버를 수정하면 정상적으로 "반응"하지 않을 수 있다. 이 경우 "반응"하게 하고 싶다면,Vue.set(object, key, value)
를 사용하는 방법이 있다. - v-model로 전달되는 변수가
Object
일 경우 Object의 모든 멤버가 수정될 경우 수동으로input
이벤트를$emit
해야 한다. - v-model로 전달되는 변수가
Object
이고, Object의 특정 멤버가Array
일 경우 이 Array를 수정했을 때 "반응"하게 하고 싶다면v-if="!value.arr || value.arr.length === 0"
와 같이 사용하면 된다.
윈도우크기(window.innerHeight) 변화 감지
I am sure there are better ways to do this, but this one will work for you until I come up with something:
Basically you will need just one data prop and one watcher to do this.
new Vue({
el: '#app',
data() {
return {
windowHeight: window.innerHeight,
txt: ''
}
},
watch: {
windowHeight(newHeight, oldHeight) {
this.txt = `it changed to ${newHeight} from ${oldHeight}`;
}
},
mounted() {
this.$nextTick(() => {
window.addEventListener('resize', this.onResize);
})
},
beforeDestroy() {
window.removeEventListener('resize', this.onResize);
},
methods: {
onResize() {
this.windowHeight = window.innerHeight
}
}
});
And this would output the changes
Style guide
devServer.watchOptions
webpack-dev-server#devServer.watchFiles 항목 참조. 간단히:
module.exports = {
configureWebpack: {
devServer: {
watchOptions: {
ignored: [/node_modules/, /public/],
}
}
}
}
Tutorials (by. LCW)
- Github - webpack-simple (시작 프로젝트로 사용하기 좋다)
- [추천]
Vue 프로젝트를 통해서 Webpack을 알아보자.(vue simple webpack project)
Webpack과 함께 시작.
Custom shims
shims-recc.d.ts
와 같이 직접 커스텀 shims를 추가하고 싶다면
가 아닌,
의 형태로 해야 WebStorm계열에서 정상적으로 symbol complition 이 적용되더라 ...
최종 형태는 다음과 같다:
Troubleshooting
You may need an additional loader to handle the result of these loaders
필요한 부분에 추가 로더를 설치/설정 하면 된다.
this.$refs.
<refField>.value
does not exist
You can do this:
class YourComponent extends Vue {
$refs!: {
checkboxElement: HTMLFormElement
}
someMethod () {
this.$refs.checkboxElement.checked
}
}
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders
다음과 같이, $refs
를 사용하여 직접 컴포넌트 속성을 변경하면...
Prop을 직접 변경하지 말라는 다음과 같은 에러가 발생된다.
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "loading"
번역하면:
상위 구성 요소가 다시 렌더링될 때마다 값을 덮어쓰므로 소품을 직접 변경하지 마십시오.
대신 prop의 값을 기반으로 데이터 또는 계산된 속성을 사용하십시오.
Prop 변경 중: "loading"
한마디로, ref
를 써서 직접 변경하지 말라. v-bind 사용해라.
Prop 으로 지정하지 않은, Data member 인데 위의 경고가 출력될 경우
Declaring Reactive Properties
Vue는 루트 수준 반응 속성을 동적으로 추가하는 것을 허용하지 않으므로 빈 값이 있더라도 모든 루트 수준 반응 데이터 속성을 미리 선언하여 Vue 인스턴스를 초기화해야 합니다.
var vm = new Vue({
data: {
// declare message with an empty value
message: ''
},
template: '<div>{{ message }}</div>'
})
// set `message` later
vm.message = 'Hello!'
data 옵션에서 message를 선언하지 않으면 Vue는 render 함수가 존재하지 않는 속성에 접근하려고 한다고 경고할 것입니다.
이 제한 뒤에는 기술적인 이유가 있습니다. 종속성 추적 시스템에서 극단적인 경우 클래스를 제거하고 Vue 인스턴스가 유형 검사 시스템에서 더 잘 작동하도록 합니다. 그러나 코드 유지 관리 측면에서도 중요한 고려 사항이 있습니다. 데이터 개체는 구성 요소 상태에 대한 스키마와 같습니다. 모든 반응 속성을 미리 선언하면 나중에 다시 방문하거나 다른 개발자가 읽을 때 구성 요소 코드를 더 쉽게 이해할 수 있습니다.
Error /sockjs-node/info
- Stackoverflow - Errors in browser console, requests to /sockjs-node/info?t=1555629946494
- vue proxy 사용하기
브라우저 콘솔에 다음과 같은 에러가 출력될 수 있다.
Props with type Object/Array must use a factory function to return the default value
Props를 Object 또는 Array 타입으로 설정할 경우 기본 값 설정 방법. 간단히, 함수의 반환값으로 기본값을 설정해야 한다.{ type: () => { return []; } }
Vue:Props#Props with type Object/Array must use a factory function to return the default value 항목 참조.
Error in callback for watcher "value": "TypeError: Cannot read property 'call' of undefined"
부모 컴포넌트에서 다음과 같이 this.current
속성을 created
같은 곳이 아닌, 다른 위치 (e.g. 비동기 실행 영역)에서 수정되도록 한다.
템플릿에 해당 객체를 넘긴다.
current.username
속성을 자식(form-user
)컴포넌트에서 value.username
으로 사용하면,
다음과 같은 에러가 발생된다.
[Vue warn]: Error in callback for watcher "value": "TypeError: Cannot read property 'call' of undefined"
추정이지만..
컴포넌트의 watcher 인스턴스가, 렌더링되는 동안 "수정"된 모든 속성을 기록하는데, Object.defineProperty 으로 Vue의 data
로 전달된 모든 속성을 재정의 하기 때문으로 추정된다. (자세한 내용은 반응형에 대해 깊이 알아보기 페이지 참조) 위의 에러는 username
을 직접 참조하는데, this.current
인스턴스가 Object.defineProperty 으로 재정의된 이후, 다시 객체 자체가 수정되어 발생된 이슈로 추정된다. <- 하위 컴포넌트는 value.username
에 직접 참조해서, 이 부분이 바뀐 것으로 추정됨.
따라서 부모컴포넌트에서 this.current
객체 수정시 다음과 같이 프로퍼티 각각을 직접 수정해야 한다.
컴포넌트의 v-model을 상용, 하위 컴포넌트의 v-model로 다시 전달할 경우 작동하지 않는 현상
아래의 예제를 보자.
<template>
<v-card>
<v-text-field v-model="infoValue"></v-text-field>
</v-card>
</template>
<script lang="ts">
import {Component, Prop, Emit, Watch} from 'vue-property-decorator';
import VueBase from '@/base/VueBase';
@Component
export default class CardInfoNew extends VueBase {
@Prop({type: String, default: ''})
readonly initKey!: string;
infoKey = '';
mounted() {
this.infoKey = this.initKey;
}
}
</script>
Prop
으로 설정한 initKey
는 readonly
라서 v-text-field
의 v-model로 설정할 수 없다.
따라서 infoKey
를 별도로 만들게 되는데, 이 때 상위 컴포넌트에서 initKey
를 변경하면 반영되지 않는다.
아래와 같이 Watch
로 확인해야 정상적으로 작동한다.
Maximum call stack size exceeded
순환 참조를 했는지 확인해보자.
- 컴포넌트 반복 호출
- 함수 재귀
v-slot directive doesn't support any modifier
다음과 같이 된 코드를:
다음과 같이 변경:
Cannot GET /...
- Cannot get /... #1254
- Npm run dev “Cannot GET /config” - vue router history mode
- historyApiFallback이란? (webpack.config.js)
SPA에서는 /bla/bla/bla
같은 경로를 접근해도 루트의 /index.html
파일로 읽어야 할 수 있다. 페이지 링크를 통해 이동하면 프레임워크의 의도대로 이동되지만, 만약 직접 주소를 쳐서 이동하면 해당 위치의 리소스(e.g. /bla/bla/bla/index.html
)가 존재하지 않아 라우팅에 실패한다.
historyApiFallback
는 HTML5의 History API를 사용하는 경우에, 설정해놓은 url 을 포함하는 url에 접근했을때 404 responses를 받게 되는데 이때도 /index.html
을 서빙해주는 효과가 있다. 마치 http://localhost:3080 로 일단 갔다가, /bla/bla/bla
라는 라우터로 이동하는 것처럼 보인다.
vue.config.js파일에 다음과 같이 설정하면 된다:
const {defineConfig} = require('@vue/cli-service');
module.exports = defineConfig({
...
devServer: {
historyApiFallback: true,
},
});
앱 배포 후 브라우저 캐시를 클리어하지 않으면 최신버전으로 갱신되지 않는 현상
Cache Busting 항목 참조.
See also
Favorite site
- 시작하기 - Vue.js
- [추천] Github - awesome-vue
- [추천] Vue.js Examples - 많은 양의 샘플 프로젝트/라이브러리
- 다른 프레임워크와의 비교 - Vue.js
- [추천] 왜 43%의 프론트엔드 개발자들은 Vue.js를 배우고 싶어하나?
- Haruair - 다른 프레임워크와 vue.js 비교
- Meetup : TOAST Cloud - 자바스크립트 프레임워크 소개 3 - Vue.js 1
- Building Your First App With Vue.js
- 난 React와 Vue에서 완전히 똑같은 앱(todo)을 만들었다. 여기 그 차이점이 있다.(번역글)
Tutorials
Guide
- express와 vue를 이용한 개발 환경 구성 살펴보기 3 (express, vue)
- 0.7KB로 Vue와 같은 반응형 시스템 만들기 - TOAST UI :: Make Your Web Delicious!
- Vue.js의 3가지 핵심구조
- [추천] Vue.js 2.3 Complete API Cheat Sheet
- [추천] Vue 인스턴스의 속성과 메소드 ($attrs, $set, $delete, $nextTick, $emit, $forceUpdate...)
- Vue 2 에서 Svelte 로 이관하기 | GeekNews
Lint
- Vue.js 공식 ESLint 플러그인 적용하기 (Vue.js, ESLint)
- ESLint와 Prettier 설정 하기 (Vue.js, ESLint, Prettier)
- Vue.js 공식 ESLint 플러그인 적용하기 – Vue.js 한국 사용자 모임 (Vue.js, ESLint)
Examples
- Github - Whiteboard - (Vue.js Paper.js drawing proj)
- VueJS + PaperJS: A Canvas Story. VueJS is a powerful, progressive web… | by Noah Kreiger | Medium (Vue.js, Paper.js)