List of 300 VueJS Interview Questions
- đź“‹ 본 문서는 sudheerjěť vuejs-interview-questionsěť ë˛ě—본입ë‹ë‹¤.
- â ěť´ í”„ëˇśě ťíŠ¸ę°€ ë§ěťŚě— 드셨다면 STAR를 ëŚëź¬ěŁĽě„¸ěš”.
- ✔️ í’€ 리í€ěŠ¤íŠ¸ëŠ” ě–¸ě śë“ í™ěěž…ë‹ë‹¤.
-
Vue.js는 ě‚¬ěš©ěž ěť¸í„°íŽěť´ěŠ¤ëĄĽ 만들기 위한 ě§„ëł´ě 인 프ë 임워í¬ěž…ë‹ë‹¤. 핵심 라이브러리는
ë·° ë ěť´ě–´
ë§Ś ě´ě ěť„ 맞추어, 다른 ëťĽěť´ë¸Śëź¬ë¦¬ë‚ ę¸°ěˇ´ í”„ëˇśě ťíŠ¸ě™€ěť í†µí•©ěť´ 쉽습ë‹ë‹¤. -
ě•„ëžěť í•목들은 VueJSěť ěŁĽěš” 특징들입ë‹ë‹¤.
- ę°€ě DOM(Virtual DOM): VueJSě—서는 ReactJS, Ember 프ë 임워í¬ě™€ ěś ě‚¬í•게 ę°€ě DOMěť„ 사용합ë‹ë‹¤. ę°€ě DOM은 ě›ëł¸ HTML DOMěť„ 표í„í•는 메모리 ěěť ę°€ë˛Ľěš´ DOM 트리로, ě›ëł¸ DOMě— ěí–Ąěť„ 미ěąě§€ ě•Šęł ě—…ëŤ°ěť´íŠ¸ëĄĽ í• ě ěžěеë‹ë‹¤.
- 컴포넌트(Components): VueJS 어플리케이ě…ě—서 ěž¬ě‚¬ěš©í• ě ěžëŠ” ě—리먼트들을 만들 ě ěžěеë‹ë‹¤.
- 템플릿(Templates): VueJS는 Vue 인스턴스 데이터와 DOMě— ě ‘ę·Ľí• ě ěžëŠ” HTML 기ë°ěť 템플릿을 ě śęłµí•©ë‹ë‹¤.
- 라우팅(Routing): íŽěť´ě§€ěť ě „í™ěť€ vue-router를 이용합ë‹ë‹¤.
- ě €ěš©ëź‰(Light weight): VueJS는 다른 프ë 임워í¬ě™€ ëą„ęµí•´ ě €ěš©ëź‰ěž…ë‹ë‹¤.
-
ëťĽěť´í”„ě‚¬ěť´í´ í›…(Lifecycle hook)은 사용중인 라이브러리가 ě–´ë–¤ ěśě„śëˇś 동작í•는지를 ě•Śë ¤ěŁĽëŠ” ě—í• ěť„ í•©ë‹ë‹¤. í›…ěť„ 이용해 컴포넌트가 ě–¸ě ś ěťě„±ëęł , ě–¸ě ś DOMě— ě¶”ę°€ëë©°, ě–¸ě ś 업데이트ëęł ě–¸ě ś 사라지는지 알 ě ěžěеë‹ë‹¤. ě•„ëžěť 다이어그램을 통해 VueJSěť ě „ë°ě 인 라이프사이í´ěť„ í™•ěť¸í• ě ěžěеë‹ë‹¤.
-
Creation(ě´ę¸°í™”): Create 훅은 컴포넌트가 DOMě— ě¶”ę°€ë기 ě „ě— ě‹¤í–‰ë는 단계입ë‹ë‹¤. í´ëťĽěť´ě–¸íŠ¸ě™€ 서버가 ë ŚëŤ”ë§ ë‹¨ęł„ ě „ě— ě»´íŹ¬ë„ŚíŠ¸ě— ě„¤ě •í•´ě•Ľ í• ę˛ë“¤ěť´ ěžěť„ 때 사용í•는 단계입ë‹ë‹¤. 다른 훅과는 다르게, Create 훅은 서버 사이드 ë ŚëŤ”ë§ě—서도 ě§€ě›ë는 í›…ěž…ë‹ë‹¤.
- beforeCreate:
beforeCreate
훅은 컴포넌트 ě´ę¸°í™” 단계 중 가장 ě˛ěťŚěśĽëˇś 실행ë©ë‹ë‹¤. ěť´ í›…ě—서는 ě»´íŹ¬ë„ŚíŠ¸ěť data를 관찰í•ęł , 이벤트를 ě´ę¸°í™”í•©ë‹ë‹¤. ěť´ 단계ě—서 data는 ě•„ě§ęąŚě§€ ë°ěť‘ě ěť´ě§€ 않으며, ě»´íŹ¬ë„ŚíŠ¸ěť ëťĽěť´í”„ě‚¬ěť´í´ě—서 ë°śěťí•는 이벤트 ě—시 ě„¤ě •ëě§€ 않은 ěíśěž…ë‹ë‹¤.
new Vue({ data: { count: 10 }, beforeCreate: function () { console.log('Nothing gets called at this moment') // `this` points to the view model instance console.log('count is ' + this.count); } }) // count is undefined
- created:
created
훅은 Vue 인스턴스가 이벤트를 ě„¤ě •í•ęł data를 ę´€ě°°í• ë•Ś ë°śěťí•©ë‹ë‹¤. ěť´ 단계ě—서 템플릿은 ě•„ě§ ë§ěš´íЏëę±°ë‚ ë ŚëŤ”ë§ëě§€ 않ě•지만, 이벤트들이 활성화ëë©° dataě— ë°ěť‘ě 으로 ě ‘ę·Ľí•는 ę˛ěť´ 가능합ë‹ë‹¤.
new Vue({ data: { count: 10 }, created: function () { // `this` points to the view model instance console.log('count is: ' + this.count) } }) // count is: 10
주ěť: Create í›…ě—서는 DOMě— ě§ě ‘ ě ‘ę·Ľí•ę±°ë‚ ë§ěš´íŠ¸í• ě—리먼트(
this.$el
)ě— ě§ě ‘ ě ‘ę·Ľí• ě 없다는 ě ěť„ 기억í•세요. - beforeCreate:
-
Mounting(DOM 추가): Mount 훅은 가장 ë§Žěť´ 사용ë는 단계로, 컴포넌트가 ë ŚëŤ”ë§ë기 ě§ě „ěť´ë‚ ě§í›„ě— ě»´íŹ¬ë„ŚíŠ¸ě— ě ‘ę·Ľí• ě ěžëŠ” 단계입ë‹ë‹¤.
- beforeMount:
beforeMount
훅은 컴포넌트가 DOMě— ě¶”ę°€ë기 ě§ě „ě— ě‹¤í–‰ë는 í›…ěž…ë‹ë‹¤.
new Vue({ beforeMount: function () { // `this` points to the view model instance console.log(`this.$el is yet to be created`); } })
- mounted:
mounted
훅은 ë°ěť‘ě 인 data, 템플릿, ë ŚëŤ”ë§ëś DOM(this.$el
) 모ë‘ě— ě ‘ę·Ľí• ě ěžě–´ě„ś 가장 ë§Žěť´ 사용ë는 í›…ěž…ë‹ë‹¤. íť”íž ě»´íŹ¬ë„ŚíŠ¸ě—서 필요한 데이터를 외부ě—서 ę°€ě ¸ě¤ëŠ”(fetch) 용도로 ë§Žěť´ 사용ë©ë‹ë‹¤.
<div id="app"> <p>I’m text inside the component.</p> </div> new Vue({ el: '#app', mounted: function() { console.log(this.$el.textContent); // I'm text inside the component. } })
- beforeMount:
-
Updating (재 ë ŚëŤ”ë§): Update 훅은 컴포넌트 ë‚´ë¶€ěť ë°ěť‘ě 인 속성이 변í–ę±°ë‚, ę·¸ ě™¸ěť ę˛ë“¤ěť´ 재 ë ŚëŤ”ë§ěť„ 일으킬 때 실행ë는 단계입ë‹ë‹¤.
- beforeUpdate:
beforeUpdate
훅은 ě»´íŹ¬ë„ŚíŠ¸ěť dataę°€ 변경ëě–´ 업데이트 사이í´ěť´ 시작ë 때 실행ë©ë‹ë‹¤.
<div id="app"> <p>{{counter}}</p> </div> ...// rest of the code new Vue({ el: '#app', data() { return { counter: 0 } }, created: function() { setInterval(() => { this.counter++ }, 1000) }, beforeUpdate: function() { console.log(this.counter) // Logs the counter value every second, before the DOM updates. } })
- updated:
updated
훅은 ě»´íŹ¬ë„ŚíŠ¸ěť dataę°€ 변í•ě—¬ 재 ë ŚëŤ”ë§ěť´ 일어난 í›„ě— ě‹¤í–‰ë©ë‹ë‹¤.
<div id="app"> <p ref="dom">{{counter}}</p> </div> ...// new Vue({ el: '#app', data() { return { counter: 0 } }, created: function() { setInterval(() => { this.counter++ }, 1000) }, updated: function() { console.log(+this.$refs['dom'].textContent === this.counter) // Logs true every second } })
- beforeUpdate:
-
Destruction(해체): Destruction 훅은 컴포넌트를 더 ěť´ě 사용í•ě§€ 않을 때 사용í•는 단계입ë‹ë‹¤.
- beforeDestroy:
beforeDestroy
훅은 컴포넌트가 해체ë기 ě§ě „ě— ě‹¤í–‰ë©ë‹ë‹¤. ěť´ 훅은 ë°ěť‘ě 인 ěť´ë˛¤íŠ¸ë“¤ěť´ë‚ data들을 해체í•는 훅으로 ě í•©í•©ë‹ë‹¤. ěť´ 단계ě—서 컴포넌트는 ě—¬ě „íž ë¬¸ě śě—†ěť´ ěž ëŹ™ěž‘í•©ë‹ë‹¤.
new Vue ({ data() { return { message: 'Welcome VueJS developers' } }, beforeDestroy: function() { this.message = null delete this.message } })
- destroyed:
destroyed
훅은 컴포넌트가 해체ëęł ë‚ś ě§í›„ě— í¸ě¶śë©ë‹ë‹¤. ëŞ¨ë“ ě§€ě‹śěžë“¤ěť 바인딩이 í•´ě śëě—으며, 이벤트 리스ë„ę°€ ě śę±°ëś ěíśěž…ë‹ë‹¤.
new Vue ({ destroyed: function() { console.log(this) // Nothing to show here } })
- beforeDestroy:
-
-
VueJS는 ěˇ°ę±´ě— ë”°ëťĽ ě—리먼트를 ëł´ě—¬ěŁĽę±°ë‚ ě¨ę¸¸ ě ěžëŠ” 지시ěžë“¤ěť„ ě śęłµí•©ë‹ë‹¤. ě‚¬ěš©í• ě ěžëŠ” 지시ěžë“¤ěť€ v-if, v-else, v-else-if and v-showę°€ ěžěеë‹ë‹¤.
1. v-if: v-if 지시ěžëŠ” ěˇ°ę±´ě— ë”°ëťĽ DOM ě—리먼트를 추가í•ę±°ë‚ ě śę±°í•©ë‹ë‹¤. ě를 들어, ě•„ëžěť 버튼은
isLoggedIn
ěť ę°’ěť´false
라면 ë‚í€ë‚ě§€ 않습ë‹ë‹¤.<button v-if="isLoggedIn">Logout</button>
ě—리먼트들을
<template>
íśę·¸ëˇś ę°ě‹Ľë‹¤ë©´, í•ë‚ěť v-if만으로 여러 ě—ë¦¬ë¨ĽíŠ¸ë“¤ěť ěˇ°ę±´ěť„ ě„¤ě •í• ě ěžěеë‹ë‹¤. ě를 들어, ě•„ëžě™€ 같이label
과button
íśę·¸ëĄĽ í•ë‚ěť ěˇ°ę±´ 지시ěžëˇś ě śě–´í• ě ěžěеë‹ë‹¤.<template v-if="isLoggedIn"> <label> Logout </button> <button> Logout </button> </template>
2. v-else: 프로그ëžë°Ť 언어ě—서
if
ěť ěˇ°ę±´ě— ë§žě§€ 않는 경우else
로 ë„어가는 ę˛ě˛ëźĽ, v-else 지시ěžëŠ” ěť¸ě ‘í•ś v-if ě§€ě‹śěž ë는 v-else-if 지시ěžę°€false
일 때만 ę·¸ 내용이 ë‚í€ë‚©ë‹ë‹¤. ěť´ 지시ěžě—는 조건을 ě§€ě •í• í•„ěš”ę°€ 없습ë‹ë‹¤. ě를 들어, ě•„ëžěť ě시는isLoggedIn
ěť´false
일 때(즉 로그인 ëś ěíśę°€ ě•„ë‹ ë•Ś), v-else를 이용해 로그인 버튼을 보여줍ë‹ë‹¤.<button v-if="isLoggedIn"> Logout </button> <button v-else> Log In </button>
3. v-else-if: v-else-if 지시ěžëŠ” v-if ěť´ě™¸ěť ë‹¤ëĄ¸ 조건을 추가로 확인해야 í• ë•Ś 사용합ë‹ë‹¤. ě를 들어, v-else-if 지시ěžëĄĽ 이용해
ifLoginDisabled
ěť ę°’ěť´true
일 때는 로그인 버튼 ëŚ€ě‹ í…ŤěŠ¤íŠ¸ëĄĽ 보여줄 ě ěžěеë‹ë‹¤.<button v-if="isLoggedIn"> Logout </button> <label v-else-if="isLoginDisabled"> User login disabled </label> <button v-else> Log In </button>
4. v-show: v-show 지시ěžëŠ” v-if 지시ěžě™€ 비슷한 기능을 í•지만, DOMě— ě—리먼트가 ě¶”ę°€ëś ěíśě—서 CSSěť display ę°’ěť„ 이용해 ëł´ě—¬ěŁĽęł ě¨ęą€ěť„ ę˛°ě •í•©ë‹ë‹¤. v-show 지시ěžëŠ” 조건문이 ěžěŁĽ í† ę¸€ë 때 권장ë©ë‹ë‹¤.
<span v-show="user.name">Welcome user,{{user.name}}</span>
-
v-show와 v-ifěť ěŁĽěš” 차이ě 은 다음과 같습ë‹ë‹¤.
- v-if는 조건이 일ěąí•는 ě—리먼트만 DOMě— ë ŚëŤ”ë§í•는 ë°ë©´, v-show는 ëŞ¨ë“ ě—리먼트를 DOMě— ë ŚëŤ”ë§í•ś 후 CSS를 이용해 내용을 ëł´ě—¬ěŁĽę±°ë‚ ě¨ęąë‹ë‹¤.
- v-if와 v-else-ifě—서는 v-else를 ě‚¬ěš©í• ě ěžě§€ë§Ś, v-showě—서는 ě‚¬ěš©í• ě 없습ë‹ë‹¤.
- v-if는 í† ę¸€í• ë•Ś 높은 ë ŚëŤ”ë§ ëą„ěš©ěť´ 들지만, v-show는 ě´ę¸°ěť ë ŚëŤ”ë§ ěž‘ě—…ě—서 높은 비용이 ë“ë‹ë‹¤. 즉, v-show는 요소를 ěžěŁĽ ěĽśęł ë„는 경우 성능 ěěť ěť´ě ěť´ ěžě§€ë§Ś, ě´ę¸° ë ŚëŤ”ë§ ěž‘ě—…ě—서는 v-ifę°€ 더 효율ě ěž…ë‹ë‹¤.
- v-if는
<template>
íśę·¸ě—서 ě‚¬ěš©í• ě ěžě§€ë§Ś v-show는 ě‚¬ěš©í• ě 없습ë‹ë‹¤.
-
v-for 지시ěžëŠ” ë°°ě—´ěť´ë‚ ę°ťě˛´ëĄĽ ěśí™í•면서 ë°ëłµě 인 ë ŚëŤ”ë§ěť„ 가능í•게 í•©ë‹ë‹¤.
- ë°°ě—´ěť ę˛˝ěš°:
<ul id="list"> <li v-for="(item, index) in items"> {{ index }} - {{ item.message }} </li> </ul> var vm = new Vue({ el: '#list', data: { items: [ { message: 'John' }, { message: 'Locke' } ] } })
ěžë°”스í¬ë¦˝íЏ ěśí™ë¬¸ęłĽ ěś ě‚¬í•게,
in
외ě—도of
를 ě‚¬ěš©í• ě ěžěеë‹ë‹¤.- ę°ťě˛´ěť ę˛˝ěš°:
<div id="object"> <div v-for="(value, key, index) in user"> {{ index }}. {{ key }}: {{ value }} </div> </div> var vm = new Vue({ el: '#object', data: { user: { firstName: 'John', lastName: 'Locke', age: 30 } } })
-
ëŞ¨ë“ Vue 어플리케이ě…은 Vue 함ě를 이용해 Vue 인스턴스를 ěťě„±í•면서 동작합ë‹ë‹¤. 일ë°ě 으로
vm
(ViewModelěť ě¶•ě•˝í•)이라는 변ě를 이용해 Vue 인스턴스를 참조합ë‹ë‹¤. ě•„ëžě™€ 같은 방법으로 Vue 인스턴스를 ěťě„±í• ě ěžěеë‹ë‹¤.var vm = new Vue({ // options })
ěś„ěť ě˝”ë“śě—서 볼 ě ěžë“Ż, ěµě…ěť„ ě„¤ě •í•기 위한 객체를 ě „ë‹¬í•´ě•Ľ í•©ë‹ë‹¤. ěť´ ěµě…은 API 문서ě—서 ěžě„¸íž í™•ěť¸í• ě ěžěеë‹ë‹¤.
-
ë ŚëŤ”ë§ě— ěí–Ąěť„ 미ěąě§€ 않는
<template>
íśę·¸ě— v-if 지시ěžëĄĽ ě 용함으로써 여러 ě—리먼트들을 한 ë˛ě— 조건부로 ë‚í€ë‚Ľ ě ěžěеë‹ë‹¤. ě를 들어, ě•„ëžě™€ 같이 ěś íš¨í•ś 사용ěžěť¸ ę˛˝ěš°ě— í•śí•´ě„ś ě‚¬ěš©ěž ě •ëł´ëĄĽ 보여줄 ě ěžěеë‹ë‹¤.<template v-if="condition"> <h1>Name</h1> <p>Address</p> <p>Contact Details</p> </template>
-
Vue는 가능한 한 ě—리먼트를 효율ě 으로 ë ŚëŤ”ë§í•ë ¤ í•©ë‹ë‹¤. ę·¸ëžě„ś ě—리먼트를 ě˛ěťŚë¶€í„° 다시 만들기보다는 재사용í•ë ¤ í•©ë‹ë‹¤. ę·¸ëź¬ë‚ ěť´ëŠ” 몇 가지 ě황ě—서 ë¬¸ě śëĄĽ 일으킬 ě ěžěеë‹ë‹¤. ě를 들어,
input
ě—리먼트를v-if
와v-else
블록 양쪽ě—서 사용í•ë©´,input
ě—리먼트는 ěˇ°ę±´ë¬¸ě— ë”°ëťĽ 바뀌지 ě•Šęł ěµśě´ě— ë ŚëŤ”ë§ ëś ě—ë¦¬ë¨ĽíŠ¸ěť ěíśëĄĽ ěś ě§€í•ęł ěžěеë‹ë‹¤.<template v-if="loginType === 'Admin'"> <label>Admin</label> <input placeholder="Enter your ID"> </template> <template v-else> <label>Guest</label> <input placeholder="Enter your name"> </template>
ěť´ 경우ě—서
input
ě—리먼트는 재사용ë어서는 ě• ë기 때문ě—, key 속성을 이용해 ë‘ ę°śěťinput
ě—리먼트를 ëł„ę°śěť ę˛ěśĽëˇś 취급í•도록 ě„ ě–¸í• ě ěžěеë‹ë‹¤.<template v-if="loginType === 'Admin'"> <label>Admin</label> <input placeholder="Enter your ID" key="admin-id"> </template> <template v-else> <label>Guest</label> <input placeholder="Enter your name" key="user-name"> </template>
ěś„ěť ę˛˝ěš°ëŠ” ë‘ ę°śěť
input
ě—리먼트가 ëł„ę°śěť ę˛ěśĽëˇś 취급ëë©° 서로ě—게 ě–´ë–¤ ě향도 ëĽěąě§€ 않습ë‹ë‹¤. -
v-for 지시ěžëŠ” v-if 보다 더 높은 ěš°ě„ ěśěś„를 ę°–ęł ěžę¸° 때문ě—, 한 ě—리먼트 ë‚´ě—서 v-for와 v-if를 í•¨ę» ě“°ëŠ” ę˛ěť€ 권장ëě§€ 않습ë‹ë‹¤. 일ë°ě 으로 다음과 같은 ěť´ěś ë•Śë¬¸ě— ë‘ ě§€ě‹śěžëĄĽ í•¨ę» ě“°ęł¤ í•©ë‹ë‹¤.
- ë¦¬ěŠ¤íŠ¸ěť ěš”ě†ŚëĄĽ í•„í„°ë§í•기 위해 ě를 들어, v-if 지시ěžëĄĽ 이용해 ë¦¬ěŠ¤íŠ¸ě— ěžëŠ” ě•„ěť´í…śěť„ í•„í„°ë§í•ęł ě‹¶ěť€ 경우입ë‹ë‹¤.
<ul> <li v-for="user in users" v-if="user.isActive" :key="user.id" > {{ user.name }} <li> </ul>
ěť´ 경우는 ě•„ëžě™€ 같이 ě‚¬ě „ě— computed 속성을 이용해 í•„í„°ë§ëś 리스트를 만들어 ě‚¬ěš©í• ě ěžěеë‹ë‹¤.
computed: { activeUsers: function () { return this.users.filter(function (user) { return user.isActive }) } } ...... // ...... // <ul> <li v-for="user in activeUsers" :key="user.id"> {{ user.name }} <li> </ul>
- 리스트 ěžě˛´ę°€ ě¨ę˛¨ě ¸ě•Ľ í• ë•Ś ě를 들어, v-if를 이용해 ë°ëłµë는 리스트를 ě¨ę¸°ęł 싶은 경우입ë‹ë‹¤.
<ul> <li v-for="user in users" v-if="shouldShowUsers" :key="user.id" > {{ user.name }} <li> </ul>
ěť´ 경우는 ě•„ëžě™€ 같이 조건문을 ěěś„ ě—리먼트로 ě®ęą€ěśĽëˇśěŤ¨ í•´ę˛°í• ě ěžěеë‹ë‹¤.
<ul v-if="shouldShowUsers"> <li v-for="user in users" :key="user.id" > {{ user.name }} <li> </ul>
-
Vueě—서 ę°śëł„ DOM 노드들을 ě¶”ě í•ęł ę¸°ěˇ´ ě—ë¦¬ë¨ĽíŠ¸ěť ěž¬ě‚¬ěš©/ěž¬ě •ë ¬ěť„ 위해, v-forěť ěš”ě†Śě— ęł ěś í•ś key 속성을 ě śęłµí•´ě•Ľ í•©ë‹ë‹¤. keyě— ëŚ€í•ś ěť´ěě 인 값은 ę° í•목을 ě‹ťëł„í• ě ěžëŠ” ęł ěś í•ś IDěž…ë‹ë‹¤.
<div v-for="item in items" :key="item.id"> {{item.name}} </div>
ë°ëłµë는 DOM 내용이 단ěśí•ś ę˛˝ěš°ë‚ ěťëŹ„ě 인 성능 í–Ąěěť„ 위해 기본 ëŹ™ěž‘ě— ěťěˇ´í•ě§€ 않는 경우를 ě śě™¸í•ë©´, 가능í•ë©´ ě–¸ě śë‚ v-forě— key를 추가í•는 ę˛ěť´ 좋습ë‹ë‹¤. Note: ę°ťě˛´ë‚ ë°°ě—´ě˛ëźĽ, 기본 í€ěž…(Primitive value)ěť´ 아닌 ę°’ěť„ 키로 사용해서는 ě•ë©ë‹ë‹¤. 문ěžě—´ěť´ë‚ ě«ěžëĄĽ 사용í•세요.
-
이름ě—서 볼 ě ěžë“Ż, ë°°ě—´ěť„ 변화시키는 함ě(mutation methods)는 ě›ëł¸ ë°°ě—´ěť„ 변경시킵ë‹ë‹¤. ě•„ëžěť 함ě는 ë·°(view) 업데이트를 일으킵ë‹ë‹¤.
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
ě를 들어, ě•„ëžě™€ 같이
todos
ë°°ě—´ě—push
함ě를 실행시키면 ë·° 업데이트가 일어납ë‹ë‹¤.vm.todos.push({ message: 'Baz' })
-
ë°°ě—´ěť„ 대체í•는 함ě는 ě›ëł¸ ë°°ě—´ěť„ ěě •í•ě§€ ě•Šęł , í•ě ě로운 ë°°ě—´ěť„ ë°í™í•©ë‹ë‹¤. ě•„ëžěť 함ě는 ë°°ě—´ěť„ 대체í•는 함ěěž…ë‹ë‹¤.
- filter()
- concat()
- slice()
ě를 들어, ě•„ëžě™€ 같이
status
ě†Ťě„±ě— ë”°ëťĽtodos
ë°°ě—´ěť„ í•„í„°ë§í•ś ě로운 ë°°ě—´ěť„ ë°í™ë°›ěť„ ě ěžěеë‹ë‹¤.vm.todos = vm.todos.filter(function (todo) { return todo.status.match(/Completed/) })
Vueę°€ DOMěť„ 효율ě 으로 재사용í•기 때문ě—, ě „ě˛´ 리스트가 ě로 ë ŚëŤ”ë§ë지는 않습ë‹ë‹¤.
-
Vue는 ě•„ëžěť ë‘ ę°€ě§€ ę˛˝ěš°ěť ëł€ę˛˝ 사í•ěť„ ę°ě§€í• ě 없습ë‹ë‹¤.
- 인덱스로 ë°°ě—´ě— ěžëŠ” í•목을 ě§ě ‘ í• ë‹ąí•는 경우
vm.todos[indexOfTodo] = newTodo
- ë°°ě—´ěť ę¸¸ěť´ëĄĽ ěě •í•는 경우
vm.todos.length = todosLength
이는
set
과splice
함ě를 이용해 í•´ę˛°í• ě ěžěеë‹ë‹¤.첫 ë˛ě§¸ 경우
// Vue.set Vue.set(vm.todos, indexOfTodo, newTodoValue)
// Array.prototype.splice vm.todos.splice(indexOfTodo, 1, newTodoValue)
ë‘ ë˛ě§¸ 경우
vm.todos.splice(todosLength)
-
Vue는 추가ëę±°ë‚ ě‚ě śëś ě†Ťě„±ě— ë°ěť‘í•으로 ě ‘ę·Ľí• ě 없습ë‹ë‹¤.
var vm = new Vue({ data: { user: { name: 'John' } } }) // `vm.name` is now reactive vm.email = john@email.com // `vm.email` is NOT reactive
이 경우는
Vue.set(object, key, value)
ë‚Object.assign()
를 이용함으로써 ë°ěť‘í• ě†Ťě„±ěť„ ě¶”ę°€í• ě ěžěеë‹ë‹¤.Vue.set(vm.user, 'email', john@email.com);
vm.user = Object.assign({}, vm.user, { email: john@email.com })
-
v-for
지시ěžě— ě •ě를 사용해 íŠąě • íšźěë§ŚíĽ ë°ëłµí•´ ë ŚëŤ”ë§ í• ě ěžěеë‹ë‹¤.<div> <span v-for="n in 20">{{ n }} </span> </div>
ěť´ 경우 1부터 20까지 ě«ěžę°€ ě¶śë Ąë©ë‹ë‹¤.
-
<template>
ě—서 v-if를 사용한 ę˛ęłĽ ěś ě‚¬í•게,<template>
ě—서 v-for 문법을 ě‚¬ěš©í• ě ěžěеë‹ë‹¤.<ul> <template v-for="todo in todos"> <li>{{ todo.title }}</li> <li class="divider"></li> </template> </ul>
-
VueJSě—서는 ěśě ěžë°”스í¬ë¦˝íŠ¸ě™€ ěś ě‚¬í•게 이벤트 핸들러를 ě‚¬ěš©í• ě ěžěеë‹ë‹¤. 함ěě—서
$event
변ě를 í¸ě¶śí•´ ě‚¬ěš©í• ě ěžěеë‹ë‹¤.<button v-on:click="show('Welcome to VueJS world', $event)"> Submit </button> methods: { show: function (message, event) { // now we have access to the native event if (event) event.preventDefault() console.log(message); } }
-
일ë°ě 으로 ěžë°”스í¬ë¦˝íЏě—서는 이벤트 핸들러 ë‚´ë¶€ě—서
event.preventDefault()
ë는event.stopPropagation()
를 ě śęłµí•©ë‹ë‹¤. Vueěť ë©”ě†Śë“ś ë‚´ë¶€ě—서도 ěť´ 작업을 í• ě ěžě§€ë§Ś, DOMě—서 ë°śěťí•ś 이벤트와 ë©”ě†Śë“śěť ëˇśě§ěť€ 별개로 구분í•는 ę˛ěť´ 좋습ë‹ë‹¤.ěť´ ë¬¸ě śëĄĽ 해결í•기 위해, Vue는
v-on
ěť´ë˛¤íŠ¸ě— ěť´ë˛¤íŠ¸ ě식어를 ě śęłµí•©ë‹ë‹¤. ě식어는 ě 으로 í‘śě‹śëś ě ‘ëŻ¸ě‚¬ ěž…ë‹ë‹¤..stop
.prevent
.capture
.self
.once
.passive
.stop
ě식어를 ě로 ë“¤ě–´ëł´ę˛ ěŠµë‹ë‹¤.<!-- the click event's propagation will be stopped --> <a v-on:click.stop="methodCall"></a>
ě식어는 연속해서 ě‚¬ěš©í• ě ěžěеë‹ë‹¤.
<!-- modifiers can be chained --> <a v-on:click.stop.prevent="doThat"></a>
-
Vue는 키보드 이벤트를 ě śě–´í•기 위해
v-on
지시ěžě— 키 ě식어를 ě śęłµí•©ë‹ë‹¤.<!-- only call `vm.show()` when the `keyCode` is 13 --> <input v-on:keyup.13="show">
ëŞ¨ë“ í‚¤ 코드를 외우는 ę˛ěť€ ě–´ë µę¸° 때문ě—, Vueě—서는 ěžěŁĽ 사용ë는 키들은 ëł„ěąěť„ ě śęłµí•ęł ěžěеë‹ë‹¤.
.enter
.tab
.delete
(“Delete”와 “Backspace” 포함).esc
.space
.up
.down
.left
.right
ěś„ ěě‹śěť í‚¤ 코드는 ě•„ëžě™€ 같이 ëł„ěąěśĽëˇś 다시 쓸 ě ěžěеë‹ë‹¤.
<input v-on:keyup.enter="submit"> // (OR) <!-- with shorthand notation--> <input @keyup.enter="submit">
키 코드 ěť´ë˛¤íŠ¸ěť ě‚¬ěš©ěť€ ěµśě‹ ë¸ŚëťĽěš°ě €ě—서는 ě§€ě›ëě§€ 않을 ě ěžěеë‹ë‹¤.
-
ě „ě—
config.keyCodes
객체를 통해 키 ě식어를 커스터ë§ěť´ě§•í• ě ěžěеë‹ë‹¤. 여기ě—는 몇 가지 규칙들이 ěžěеë‹ë‹¤.- 카멜 케이스(camelCase)를 ëŚ€ě‹ ěŚŤë”°ě´í‘śëˇś ę°ě‹¸ě§„ 케밥 케이스(Kebab-case)를 사용해야 í•©ë‹ë‹¤.
- ë°°ě—´ěť„ 이용해 한 ë˛ě— 여러 값들을 ě •ěťí• ě ěžěеë‹ë‹¤.
Vue.config.keyCodes = { f1: 112, "media-play-pause": 179, down: [40, 87] }
-
Vueě—서는 다음 ě식어를 사용해 해당 ě식어 키가 ëŚëź¬ě§„ 경우ě—ë§Ś ë§ěš°ěФ ë는 키보드 이벤트를 ë°śěťě‹śí‚¬ ě ěžěеë‹ë‹¤.
.ctrl
.alt
.shift
.meta
ě•„ëžëŠ” 컨트롤 키가 ëŚë¦° ěíśě—서 í´ë¦ 이벤트를 활성화 í•는 ě시입ë‹ë‹¤.
<!-- Ctrl + Click --> <div @click.ctrl="doSomething">Do something</div>
-
Vue는 íŠąě •í•ś ë§ěš°ěФ 버튼으로 ë°śěťí•ś 이벤트를 ě śě–´í• ě ěžěеë‹ë‹¤.
.left
.right
.middle
ë§ěš°ěФ 이벤트로
.right
를 이용한 ě시입ë‹ë‹¤.<button v-if="button === 'right'" v-on:mousedown.right="increment" v-on:mousedown.left="decrement" />
-
v-model
지시ěžëĄĽ 이용해input
,textarea
,select
ě—ë¦¬ë¨ĽíŠ¸ěť ëŤ°ěť´í„°ëĄĽ 양방향으로 ě śě–´í• ě ěžěеë‹ë‹¤. ě•„ëžěťinput
ě—리먼트를 살펴보세요.<input v-model="message" placeholder="Enter input here"> <p>The message is: {{ message }}</p>
v-model
은 모ë“form
ě—리먼트ě—서 HTML 속성(attribute)으로 ě„ ě–¸ëśvalue
,checked
그리ęłselected
를 무시합ë‹ë‹¤. ę·¸ ëŚ€ě‹ Vue 인스턴스ě—서v-model
로 바인딩한 ę°’ěť„ 이용합ë‹ë‹¤. 따라서 ě»´íŹ¬ë„ŚíŠ¸ěť dataě—서 ě´ę¸°ę°’ěť„ ě„ ě–¸í•´ě•Ľ í•©ë‹ë‹¤. -
v-model
지시ěžě—는 세 가지 ě식어가 ě§€ě›ë©ë‹ë‹¤.1. lazy: 기본ě 으로,
v-model
은 í•ë‚ěť í‚¤ ěž…ë Ą 이벤트가 ë°śěťí• 때ë§ë‹¤ dataę°€ 업데이트ë©ë‹ë‹¤. 이를 ë°©ě§€í•기 위해서는.lazy
ě식어를 이용합ë‹ë‹¤.<!-- synced after "change" instead of "input" --> <input v-model.lazy="msg" >
2. number:
v-model
ě—.number
ě식어를 붙이면 ěžëŹ™ě 으로 사용ěžěť ěž…ë Ąěť ěžëŁŚí•ěť´Number
로 변í™ë©ë‹ë‹¤. HTMLinput
íśę·¸ěť 속성이type="number"
일지라도 ë°í™ë는 ę°’ěť ěžëŁŚí•은 문ěžě—´ěť´ę¸° 때문ě—, ě«ěž ěžëŁŚí•ěť´ í•„ěš”í•다면.number
ě식어를 사용해야 í•©ë‹ë‹¤.<input v-model.number="age" type="number">
3. trim:
.trim
ě식어를 ě‚¬ěš©ěž ěž…ë Ąě—서 ě˛ěťŚęłĽ ëťě— 들어ěžëŠ” 공백을 ěžëŹ™ěśĽëˇś ě śę±°í•´ě¤Ťë‹ë‹¤.<input v-model.trim="msg">
-
컴포넌트란 재사용 가능í•면서 이름이 ëŞ…ëŞ…ëś Vue 인스턴스입ë‹ë‹¤. 컴포넌트는 Vueě˛ëźĽ data, computed, watch, methods, ëťĽěť´í”„ě‚¬ěť´í´ ěµě…ěť„ ę°–ęł ěžěеë‹ë‹¤. ě•„ëžëŠ” Vueě— ě „ě—으로 컴포넌트를 추가í•는 ě시입ë‹ë‹¤.
// Define a new component called button-counter Vue.component('button-counter', { template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' data: function () { return { count: 0 } }, })
ěť´ 컴포넌트는 ě „ě—으로 ě„ ě–¸ëě—기 ë•Śë¬¸ě— Vue 인스턴스ě—서 ě‚¬ěš©í• ě ěžěеë‹ë‹¤.
<div id="app"> <button-counter></button-counter> </div> var vm = new Vue({ el: '#app' });
-
props는 ěěś„ ě»´íŹ¬ë„ŚíŠ¸ěť ě •ëł´ëĄĽ í•ěś„ 컴포넌트로 ě „ë‹¬í• ě ěžëŠ” ě‚¬ěš©ěž ě§€ě •ěť ě†Ťě„±ěž…ë‹ë‹¤. ěěś„ 컴포넌트ě—서 ě „ë‹¬ë는 props는 í•ěś„ ě»´íŹ¬ë„ŚíŠ¸ěť ě†Ťě„±ěśĽëˇś 여겨지며, í•ěś„ 컴포넌트ě—서는 props ěµě…ěť„ 사용í•ě—¬ ěě‹ í• ę˛ěśĽëˇś ěěë는 props를 명시ě 으로 ě„ ě–¸í•´ě•Ľ í•©ë‹ë‹¤.
Vue.component('todo-item', { props: ['title'], template: '<h2>{{ title }}</h2>' })
í•ěś„ 컴포넌트ě—서 propsę°€ 등록ëęł ë‚ë©´, ěěś„ 컴포넌트ě—서는 ě‚¬ěš©ěž ě§€ě • 속성을 이용해 ę°’ěť„ ě „ë‹¬í• ě ěžěеë‹ë‹¤.
<todo-item title="Learn Vue conceptsnfirst"></todo-item>
-
템플릿이 여러 ę°śěť ě—리먼트들로 구성ëě–´ ěžěť„ 때, ě»´íŹ¬ë„ŚíŠ¸ěť ěµśě단 템플릿은 ë°ë“śě‹ś 단일 ě—리먼트로 ę°ě‹¸ě ¸ ěžě–´ě•Ľ í•©ë‹ë‹¤.
<div class="todo-item"> <h2>{{ title }}</h2> <div v-html="content"></div> </div>
ę·¸ë ‡ě§€ 않다면,
"Component template should contain exactly one root element..."
라는 ě—러를 ë°śěťě‹śí‚µë‹ë‹¤. -
í•ěś„ 컴포넌트ě—서
$emit
객체를 이용해 ěěś„ 컴포넌트로 이벤트를 ë°śěťí‚¬ ě ěžěеë‹ë‹¤.Vue.component('todo-tem', { props: ['todo'], template: ` <div class="todo-item"> <h3>{{ todo.title }}</h3> <button v-on:click="$emit('increment-count', 1)"> Add </button> <div v-html="todo.description"></div> </div> ` })
ěť´ 때 ěěś„ 컴포넌트ě—서는
v-on
지시ěžëĄĽ 이용해 í•ěś„ 컴포넌트ě—서 명명한 이벤트와 ę°’ěť„ ě‚¬ěš©í• ě ěžěеë‹ë‹¤.<ul v-for="todo in todos"> <li> <todo-item v-bind:key="todo.id" v-bind:todo="todo" v-on:increment-count="total += 1"> </todo-item> </li> </ul> <span> Total todos count is {{total}}</span>
-
ě‚¬ěš©ěž ě •ěť input 컴포넌트ě—서도
v-model
ěť„ í™śěš©í• ě ěžěеë‹ë‹¤. 해당 컴포넌트ěťinput
은 ě•„ëž ę·śěą™ë“¤ěť„ 준ě해야 í•©ë‹ë‹¤.input
ěťvalue
를 props를 이용해 바인딩합ë‹ë‹¤.- ě로운 ę°’ěť´ ěž…ë Ąë는
input
이벤트 ë°śěť ě‹ś, 해당 ę°’ěť„emit
í•ě—¬ ěěś„ 컴포넌트로 이벤트를 ě „ë‹¬í•©ë‹ë‹¤.
Vue.component('custom-input', { props: ['value'], template: ` <input v-bind:value="value" v-on:input="$emit('input', $event.target.value)" > ` })
ěť´ 경우 ěěś„ 컴포넌트ě—서
v-model
ěť„ 이용해 ę°’ěť„ ë°”ěť¸ë”©í• ě ěžěеë‹ë‹¤.<custom-input v-model="searchInput"></custom-input>
-
Vueě—서는
<slot>
ěť„ 이용해 ěěś„ 컴포넌트ě—서 í•ěś„ 컴포넌트 ë‚´ë¶€ě— ě‚¬ěš©ěž ě •ěťěť 컨í…ě¸ ëĄĽ ě§‘ě–´ 넣을 ě ěžěеë‹ë‹¤. í•ěś„ 컴포넌트ě—<slot>
ěť„ 이용해 문구를 동ě 으로 넣을 ě ěžëŠ” 컴포넌트를 만들어봅시다.Vue.component('alert', { template: ` <div class="alert-box"> <strong>Error!</strong> <slot></slot> </div> ` })
<alert>
íśę·¸ ě•ě— ë„Łěť€ 값은 컴포넌트 ë‚´ë¶€ěť<slot>
ěť ě»¨í…ě¸ ëˇś 들어가게 ë©ë‹ë‹¤.<alert> There is an issue with in application. </alert>
-
컴포넌트를 ě „ě—으로 등록í•게 ëë©´ ëŞ¨ë“ Vue 인스턴스ě—서 해당 컴포넌트를 ě‚¬ěš©í• ě ěžěеë‹ë‹¤. 컴포넌트는
Vue.component()
함ě를 이용해 ě „ě— ë“±ëˇťí• ě ěžěеë‹ë‹¤.Vue.component('my-component-name', { // ... options ... })
Vue ěť¸ěŠ¤í„´ěŠ¤ě— ě—¬ëź¬ ę°śěť ě»´íŹ¬ë„ŚíŠ¸ëĄĽ ě „ě— ë“±ëˇťí•´ë´…ě‹śë‹¤.
Vue.component('component-a', { /* ... */ }) Vue.component('component-b', { /* ... */ }) Vue.component('component-c', { /* ... */ }) new Vue({ el: '#app' })
ěś„ěť ě»´íŹ¬ë„ŚíŠ¸ë“¤ěť€ Vue 인스턴스 ë‚´ě—서 사용ë ě ěžěеë‹ë‹¤.
<div id="app"> <component-a></component-a> <component-b></component-b> <component-c></component-c> </div>
ě „ě—으로 등록한 컴포넌트들은 í•ěś„ 컴포넌트ě—서도 사용이 가능합ë‹ë‹¤.
-
ě „ě— ë“±ëˇťěśĽëˇś 인해 사용ëě§€ 않는 컴포넌트가 빌드 ě‹śě— ě—¬ě „íž ë‚¨ě•„ěžěť„ ě ěžěеë‹ë‹¤. 이는 ë¶í•„요한 ěžë°”스í¬ë¦˝íŠ¸ëĄĽ ë§Śë“¤ěŁ . 이를 ë°©ě§€í•기 위해, ě•„ëžě™€ 같이 컴포넌트를 ě§€ě— ë“±ëˇťí• ě ěžěеë‹ë‹¤.
- ěš°ě„ ěžë°”스í¬ë¦˝íЏ 객체로 컴포넌트를 ě •ěťí•©ë‹ë‹¤.
var ComponentA = { /* ... */ } var ComponentB = { /* ... */ } var ComponentC = { /* ... */ }
ě§€ě— ë“±ëˇťí•ś 컴포넌트는 다른 ě»´íŹ¬ë„ŚíŠ¸ěť í•ěś„ě—서는 ě‚¬ěš©í• ě 없습ë‹ë‹¤. ěť´ 경우,
components
속성으로 컴포넌트를 추가해 ě‚¬ěš©í• ě ěžěеë‹ë‹¤.var ComponentA = { /* ... */ } var ComponentB = { components: { 'component-a': ComponentA }, // ... }
- Vue 인스턴스ě—서
components
ě†Ťě„±ě— ě‚¬ěš©í• ě»´íŹ¬ë„ŚíŠ¸ë“¤ěť„ ě •ěťí• ě ěžěеë‹ë‹¤.
new Vue({ el: '#app', components: { 'component-a': ComponentA, 'component-b': ComponentB } })
-
ě§€ě— ë“±ëˇťěť ę˛˝ěš°, ę° ě»´íŹ¬ë„ŚíŠ¸ëĄĽ ë””ë ‰í† ë¦¬ě— ěťě„±í•ęł ę°ę°ěť 컴포넌트는 다른 컴포넌트 ě•ě—서
import
í•ě—¬ 사용í•는 ę˛ěť´ 권장ë©ë‹ë‹¤. ë§Śě•˝ 여러분들이 컴포넌트 Cě—서 컴포넌트 A와 B를 사용í•ęł ě‹¶ë‹¤ë©´ ě•„ëžě™€ 같은 ě„¤ě •ěť„ 해야 í•©ë‹ë‹¤.import ComponentA from './ComponentA' import ComponentB from './ComponentC' export default { components: { ComponentA, ComponentB } }
ěś„ěť ę˛˝ěš° 컴포넌트 A와 컴포넌트 B는 컴포넌트 Cěť í…śí”Śë¦żě—서 ě‚¬ěš©í• ě ěžěеë‹ë‹¤.
ě „ě— ë“±ëˇťěť ę˛˝ěš°, 공통ě 으로 사용ë는 컴포넌트를 ę°ę°ěť 파일ě—서
export
해야합ë‹ë‹¤. í•지만webpack
과 같은 ěś ëŞ…í•ś ë˛ë“¤ëź¬ë“¤ěť€require.context
라는 문법을 이용해서 컴포넌트를 쉽게 ě „ě—ě 으로 ë“±ëˇťí• ě ěžę˛Ś 해줍ë‹ë‹¤.import Vue from 'vue' import upperFirst from 'lodash/upperFirst' import camelCase from 'lodash/camelCase' const requireComponent = require.context( // The relative path of the components folder './components', // Whether or not to look in subfolders false, // The regular expression used to match base component filenames /Base[A-Z]\w+\.(vue|js)$/ ) requireComponent.keys().forEach(fileName => { // Get component config const componentConfig = requireComponent(fileName) // Get PascalCase name of component const componentName = upperFirst( camelCase( // Strip the leading `./` and extension from the filename fileName.replace(/^\.\/(.*)\.\w+$/, '$1') ) ) // Register component globally Vue.component( componentName, // Look for the component options on `.default`, which will // exist if the component was exported with `export default`, // otherwise fall back to module's root. componentConfig.default || componentConfig ) })
-
props
ě—는 í€ěž…ěť„ ě§€ě •í• ě도, ě§€ě •í•ě§€ 않을 ě도 ěžěеë‹ë‹¤. í•지만 일ë°ě 으로 í€ěž…ěť„ ě§€ě •í•ë©´ 다른 ę°śë°śěžë“¤ěť´ 해당 코드ě—서 ěžëŞ»ëś í€ěž…ěťprops
를 ë„겨주는 실ě를 줄여주기 때문ě—, 가능í•ë©´ í€ěž…ěť„ ě§€ě •í•´ěŁĽëŠ” ę˛ěť´ 좋습ë‹ë‹¤.props: { name: String, age: Number, isAuthenticated: Boolean, phoneNumbers: Array, address: Object }
props
ę°ťě˛´ěť ě†Ťě„±ęłĽ ę°’ěť„ ě„ ě–¸í•¨ěśĽëˇśě„ś, í€ěž…ěť„ ě„ ě–¸í• ě ěžěеë‹ë‹¤. -
모ë“
props
는 í•ěś„ 속성과 ěěś„ 속성 사이ě—서 단방향 바인딩을 í•성합ë‹ë‹¤. 즉, ěěś„ 속성이 변경ë는 ę˛ěť€ í•ěś„ 속성ě—게 ě „ë‹¬ë지만, ę·¸ ë°ëŚ€ëŠ” ě•ë©ë‹ë‹¤. ě›ěą™ě 으로, í•ěś„ 컴포넌트ě—서는 ěěś„ 컴포넌트ě—서 받은props
ěť„ ěě •í•´ě„śëŠ” ě•ë©ë‹ë‹¤.í•ěś„ 컴포넌트ě—서
props
ěě •ěť í•„ěš”ě„±ěť„ ëŠë‚„ ě ěžëŠ” 몇 가지 경우가 ěžëŠ”ëŤ°, ě•„ëžě™€ 같은 방법으로 í•´ę˛°í• ě ěžěеë‹ë‹¤.- ěěś„ 컴포넌트ěť
props
는 í•ěś„ ě»´íŹ¬ë„ŚíŠ¸ěť ě´ę¸°ę°’ ě„¤ě •ě—ë§Ś 사용ëęł ę·¸ 이후ě—는 로컬 데이터 속성으로 활용ë는 경우:
ěť´ 경우, í•ěś„ 컴포넌트ě—서 ě‚¬ěš©í• ě†Ťě„±ěť„
data
ě— ě„ ě–¸í•ęł , ę·¸ ę°’ěť„props
로 ě´ę¸°í™”í•ë©´ ë©ë‹ë‹¤.props: ['defaultUser'], data: function () { return { username: this.defaultUser } }
- ěěś„ 컴포넌트ě—서
props
로 ě „í•´ěŁĽëŠ” ę°’ěť´ ěě •ë는 경우
ěť´ 경우, í•ěś„ 컴포넌트ě—서
computed
속성을 이용해props
ěť ę°’ěť´ 바뀔 때ë§ë‹¤ ě‹ ę·ś ę°’ěť„ 얻을 ě ěžěеë‹ë‹¤.props: ['environment'], computed: { localEnvironment: function () { return this.environment.trim().toUpperCase() } }
- ěěś„ 컴포넌트ěť
-
props
ę°€ 아닌 속성이란, ě»´íŹ¬ë„ŚíŠ¸ě— ě „ë‹¬ë기는 í•지만 해당props
ę°€ í•ěś„ 컴포넌트ě—서 ě •ěťë지는 않은 속성을 ë§í•©ë‹ë‹¤. ë§Śě•˝data-tooltip
속성을 요구í•는 컴포넌트를 사용í•ęł ěžë‹¤ęł ę°€ě •í•´ë´…ě‹śë‹¤. ěť´ 속성을 컴포넌트 ěť¸ěŠ¤í„´ěŠ¤ě— ë‹¤ěťŚęłĽ 같이 추가 í• ě ěžěеë‹ë‹¤.<custom-input data-tooltip="Enter your input" />
ěěś„ 컴포넌트ě—서부터
props
ę°€ 아닌 속성을 ë„ę˛¨ěŁĽë ¤ 한다면, í•ěś„ 컴포넌트ě—서 같은 이름을 가진 속성은 덮어씌워집ë‹ë‹¤. í•지만class
ë‚style
같은props
는 ě외로, ěť´ 값들은 í•ěś„ 컴포넌트와 í•©ěłě§‘ë‹ë‹¤.//Parent component <custom-input class="custom-class" /> //Child component <input type="date" class="date-control">
-
Vueě—서는 í€ěž…, í•„ě 여부, 디폴트 ę°’ 등
props
ěť ěś íš¨ě„± 검증을 ě śęłµí•ęł ěžěеë‹ë‹¤. ě•„ëžě™€ 같이props
를 검증í•는 규칙이 속성으로 담긴 객체를 ě śęłµí•ë©´ ë©ë‹ë‹¤.Vue.component('user-profile', { props: { // Basic type check (`null` matches any type) age: Number, // Multiple possible types identityNumber: [String, Number], // Required string email: { type: String, required: true }, // Number with a default value minBalance: { type: Number, default: 10000 }, // Object with a default value message: { type: Object, // Object or array defaults must be returned from // a factory function default: function () { return { message: 'Welcome to Vue' } } }, // Custom validator function location: { validator: function (value) { // The value must match one of these strings return ['India', 'Singapore', 'Australia'].indexOf(value) !== -1 } } } })
-
일ë°ě 인 컴포넌트ě—서
v-model
지시ěžëŠ” value를props
로 사용í•ęł inputěť„ 이벤트로 사용í•지만, ě˛´í¬ ë°•ěŠ¤ë‚ ëťĽë””ě¤ ë˛„íŠĽę°™ěť€ 일부 ěž…ë Ą í€ěž…은 다른 목ě 으로value
속성을 ě‚¬ěš©í• ě ěžěеë‹ë‹¤. ěť´ëź° 경우ě—는v-model
ěť„ 커스터ë§ěť´ě§•해서 사용í•는 ę˛ěť´ 좋습ë‹ë‹¤.Vue.component('custom-checkbox', { model: { prop: 'checked', event: 'change' }, props: { checked: Boolean }, template: ` <input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change', $event.target.checked)" > ` })
ěť´ 컴포넌트ě—서
v-model
은 다음과 같이 ě‚¬ěš©í• ě ěžěеë‹ë‹¤.<custom-checkbox v-model="selectFramework"></custom-checkbox>
selectFramework
속성은props
중checked
로 ë„ě–´ę° ę˛ěť´ęł , ě˛´í¬ ë°•ěŠ¤ 컴포넌트ě—서 ę°’ěť´ 변경ëë©´ 이벤트를 ë°śěťě‹śí‚¬ ę˛ěž…ë‹ë‹¤. -
Vueě—서는 í•목들이 DOMě—서 추가, ę°±ě‹ ë는 ě‚ě śë 때, 다양한 방법으로 íŠ¸ëžśě§€ě… íš¨ęłĽëĄĽ ěž…íž ě ěžěеë‹ë‹¤.
- CSS 트랜지ě…과 ě• ë‹ë©”ěť´ě…ěť„ 위한 í´ëžěŠ¤ëĄĽ ěžëŹ™ěśĽëˇś ě ěš©
- Animate.css와 같은 써드파티 CSS ě• ë‹ë©”ěť´ě… ëťĽěť´ë¸Śëź¬ë¦¬ 통합
- íŠ¸ëžśě§€ě… í›… ě¤‘ě— JavaScript를 사용í•ě—¬ DOMěť„ ě§ě ‘ 조작
- Velocity.js와 같은 써드파티 JavaScript ě• ë‹ë©”ěť´ě… ëťĽěť´ë¸Śëź¬ë¦¬ 통합
-
Vue Router는 Vueě—서 동작í•는 공식ě 인 라우팅 라이브러리입ë‹ë‹¤.
- ě¤‘ě˛©ëś ëťĽěš°íŠ¸/ë·° 매핑
- 모ë“í™”ëś, 컴포넌트 기ë°ěť 라우터 ě„¤ě •
- 라우터 파라미터, 쿼리, 와일드카드
- Vueěť íŠ¸ëžśě§€ě… ě‹śěŠ¤í…śěť„ 이용한 íŠ¸ëžśě§€ě… íš¨ęłĽ
- 세밀한 ë„¤ëą„ę˛Śěť´ě… ě»¨íŠ¸ëˇ¤
- active CSS í´ëžěŠ¤ëĄĽ ěžëŹ™ěśĽëˇś 추가해주는 ë§í¬
- HTML5 ížěŠ¤í† ë¦¬ 모드 ë는 해시 모드(IE9ě—서 ěžëŹ™ěśĽëˇś 폴백)
- ě‚¬ěš©ěž ě •ěť ę°€ëŠĄí•ś 스í¬ëˇ¤ 동작
-
Vue를 사용í•ęł ěžë‹¤ë©´, 쉽게 Vue Router를 í†µí•©í• ě ěžěеë‹ë‹¤.
Step 1: ë¨Ľě € 템플릿ě—서
<router-link>
íśę·¸ëĄĽ ě„¤ě •í•©ë‹ë‹¤.<script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> <div id="app"> <h1>Welcome to Vue routing app!</h1> <p> <!-- use router-link component for navigation using `to` prop. It rendered as an `<a>` tag --> <router-link to="/home">Home</router-link> <router-link to="/services">Services</router-link> </p> <!-- route outlet in which component matched by the route will render here --> <router-view></router-view> </div>
Step 2:
main.js
ě—서 Vue와 Vue 라우터를import
í•ęłVue.use()
함ě를 이용해 í¸ě¶śí•©ë‹ë‹¤.import Vue from 'vue'; import VueRouter from 'vue-router'; Vue.use(VueRouter)
Step 3: 라우트 컴포넌트를 ě •ěťí•ę±°ë‚
import
í•©ë‹ë‹¤.const Home = { template: '<div>Home</div>' } const Services = { template: '<div>Services</div>' }
Step 4: 라우트를 ě •ěťí•©ë‹ë‹¤. ę° ëťĽěš°íŠ¸ëŠ” ë°ë“śě‹ś 컴포넌트와 매핑ë어야 í•©ë‹ë‹¤.
const routes = [ { path: '/home', component: Home }, { path: '/services', component: Services } ]
Step 5:
routes
ěµě…과 í•¨ę» router 인스턴스를 ë§Śë“ë‹ë‹¤.const router = new VueRouter({ routes // short for `routes: routes` })
Step 6: 루트 Vue 인스턴스를 만들ęł
mount
í•©ë‹ë‹¤.const app = new Vue({ router }).$mount('#app')
ěť´ě ś Vue 어플리케이ě…ě—서 다른 íŽěť´ě§€(Home, Services)로 네비게이트 í• ě ěžěеë‹ë‹¤.
-
주어진 패턴을 가진 라우트를 동일한 ě»´íŹ¬ë„ŚíŠ¸ě— ë§¤í•‘í•´ě•Ľí•는 경우가 ěžěŁĽ ěžěеë‹ë‹¤. 동ě 세그먼트를 이용해
/user/john/post/123
ë‚/user/jack/post/235
와 같이 ë§¤í•‘ëś URLěť„ 가지는 컴포넌트를 만들어봅시다.const User = { template: '<div>User {{ $route.params.name }}, PostId: {{ route.params.postid }}</div>' } const router = new VueRouter({ routes: [ // dynamic segments start with a colon { path: '/user/:name/post/:postid', component: User } ] })
-
매개 변ě와 í•¨ę» ëťĽěš°íŠ¸ëĄĽ ě‚¬ěš©í• ë•Ś ěŁĽěť í•´ě•Ľí• ě 은 사용ěžę°€
/user/foo
ě—서/user/bar
로 ěť´ëŹ™í• ë•Ś 동일한 컴포넌트 인스턴스가 재사용ëśë‹¤ëŠ” ę˛ěž…ë‹ë‹¤. ë‘ ëťĽěš°íŠ¸ ëŞ¨ë‘ ëŹ™ěťĽí•ś 컴포넌트를 ë ŚëŤ”ë§í•므로 ěť´ě „ 인스턴스를 ě‚ě ś 한 다음 ě 인스턴스를 만드는 ę˛ëł´ë‹¤ 효율ě ěž…ë‹ë‹¤. ę·¸ëź¬ë‚ ěť´ëŠ” ë한 ě»´íŹ¬ë„ŚíŠ¸ěť ëťĽěť´í”„ ě‚¬ěť´í´ í›…ěť´ í¸ě¶śëě§€ 않음을 ěťëŻ¸í•©ë‹ë‹¤.동일한 컴포넌트ěť
params
변경 사í•ě— ë°ěť‘í•ë ¤ë©´$route
객체를 ëł´ë©´ë©ë‹ë‹¤.watch
ě—서$route
관찰í•기:
const User = { template: '<div>User {{ $route.params.name }} </div>', watch: { '$route' (to, from) { // react to route changes... } } }
beforeRouteUpdate
ë„¤ëą„ę˛Śěť´ě… ę°€ë“śëĄĽ 사용í•기:
const User = { template: '<div>User {{ $route.params.name }} </div>', beforeRouteUpdate (to, from, next) { // react to route changes and then call next() } }
beforeRouteEnter
가드ě—서는this
ě— ě ‘ę·Ľí• ę¶Śí•śěť´ 없다는 ę˛ěť„ 기억í•세요. ëŚ€ě‹ ,next
콜백 함ě를 이용해 ěť¸ěŠ¤í„´ěŠ¤ě— ě ‘ę·Ľí• ě ěžěеë‹ë‹¤. -
동일한 URLěť´ 여러 라우트와 일ěąí•는 경우가 ěžěеë‹ë‹¤. ěť´ 경우 일ěąí•는 ěš°ě„ ěśěś„는 라우트 ě •ěťěť ěśě„śě— 따라 ę˛°ě •ë©ë‹ë‹¤. 즉, 경로가 더 ë¨Ľě € ě •ěť ë ě록 ěš°ě„ ěśěś„ę°€ 높아집ë‹ë‹¤.
const router = new VueRouter({ routes: [ // dynamic segments start with a colon { path: '/user/:name', component: User } // This route gets higher priority { path: '/user/:name', component: Admin } { path: '/user/:name', component: Customer } ] })
-
일ë°ě 으로 어플리케이ě…은 여러 ë‹¨ęł„ěť ě¤‘ě˛©ëś ě»´íŹ¬ë„ŚíŠ¸ëˇś ěť´ëŁ¨ě–´ě ¸ ěžěеë‹ë‹¤. URLěť ě„¸ę·¸ë¨ĽíŠ¸ ě—시 ě¤‘ě˛©ëś ě»´íŹ¬ë„ŚíŠ¸ěť íŠąě • 구조와 일ěąí•©ë‹ë‹¤. ě¤‘ě˛©ëś ě•„ě›ë ›ě—서 컴포넌트를 ë ŚëŤ”ë§í•ë ¤ë©´
VueRouter
ěťě„±ěžě—서config
로children
ěť„ ě„¤ě •í•´ě•Ľ í•©ë‹ë‹¤.프로필과 포스트들이 ě대ě 인 경로로 ě„¤ě •ëś ě–´í”Śë¦¬ěĽ€ěť´ě…ěť„ 만들어봅시다. 매ěąë는 í•ěś„ 라우트가 없을 ę˛˝ěš°ě— ë ŚëŤ”ë§ë는 라우트 컴포넌트를 ě„¤ě •í• ě ěžěеë‹ë‹¤.
const router = new VueRouter({ routes: [ { path: '/user/:id', component: User, children: [ { // UserProfile will be rendered inside User's <router-view> when /user/:id/profile is matched path: 'profile', component: UserProfile }, { // UserPosts will be rendered inside User's <router-view> when /user/:id/posts is matched path: 'posts', component: UserPosts }, // UserHome will be rendered inside User's <router-view> when /user/:id is matched { path: '', component: UserHome }, ] } ] })
-
ě•„ë§ í•ë‚ěť íŽěť´ě§€ě—서 HTML, CSS, JavaScriptěť„ 다른 파일로 분리해 관리해본 경í—ěť´ ěžěť„ ę˛ěž…ë‹ë‹¤. í•지만 싱글 파일 컴포넌트ě—서는 템플릿과 스í€ěťĽ, 로ě§ë“¤ěť„ í•ë‚ěť íŚŚěťĽě— ě •ë¦¬í•©ë‹ë‹¤.
<template> <div> <h1>Welcome {{ name }}!</h1> </div> </template> <script> module.exports = { data: function() { return { name: 'John' } } } </script> <style scoped> h1 { color: #34c779; padding: 3px; } </style>
-
주목해야 í• ě¤‘ěš”í•ś ě 은 관심사 분리가 파일 í€ěž… 분리와 같지 않다는 ę˛ěž…ë‹ë‹¤. í„대ě 인 UI ę°śë°śě—서 ě˝”ë“śë˛ ěť´ěŠ¤ëĄĽ 서로 ě–˝í€ěžëŠ” 세 ę°śěť ę±°ëŚ€í•ś ë 이어로 ë‚ë„는 ëŚ€ě‹ , ëŠěЍí•게 결합 ëś ě»´íŹ¬ë„ŚíŠ¸ëˇś ë‚ë„ęł ęµ¬ě„±í•는 ę˛ěť´ 더 중요합ë‹ë‹¤. 컴포넌트 ë‚´ë¶€ě—서 템플릿, ëˇśě§ ë°Ź 스í€ěťĽěť´ 본ě§ě 으로 결합ëě–´ ë°°ěąëë©´ ě»´íŹ¬ë„ŚíŠ¸ěť ěť‘ě§‘ë ĄęłĽ ěś ě§€ ëł´ě성이 í–Ąěë©ë‹ë‹¤.
싱글 파일 ě»´íŹ¬ë„ŚíŠ¸ě— ëŚ€í•ś 아이디어가 ë§ěťŚě— 들지 않더라도 JavaScript와 CSS를 ëł„ëŹ„ěť íŚŚěťĽëˇś 분리í•ě—¬ í•« 리로드 ë°Ź ě‚¬ě „ 컴파일 기능을 í™śěš©í• ě ěžěеë‹ë‹¤.
<template> <div>This section will be pre-compiled and hot reloaded</div> </template> <script src="./my-component.js"></script> <style src="./my-component.css"></style>
-
복잡한 í”„ëˇśě ťíŠ¸ěť ę˛˝ěš° ë는 í”„ëˇ íŠ¸ě—”ë“śę°€ JavaScript 기ë°ěť¸ 경우 단ě ěť´ 분명해집ë‹ë‹¤. 싱글 파일 컴포넌트가 아닌 경우ě—는 ě•„ëžě™€ 같은 ë¬¸ě śě ěť´ ěžěť„ ě ěžěеë‹ë‹¤.
- ě „ě— ě •ěť ëŞ¨ë“ ęµ¬ě„± ěš”ě†Śě— ëŚ€í•´ ęł ěś í•ś 이름을 ě§€ě •í•도록 ę°•ěš”ë©ë‹ë‹¤.
- 문ěžě—´ 템플릿 구문 강조가 약해 여러 줄로 ëś HTMLě— ëł´ę¸° ě•좋은 슬ëžě‹śę°€ ë§Žěť´ 필요합ë‹ë‹¤.
- CSS ě§€ě› ě—†ěťŚ HTML ë°Ź JavaScriptę°€ 컴포넌트로 모ë“í™” ëě–´ ěžěśĽë‚ CSSę°€ ëą ě ¸ ěžëŠ” ę˛ěť„ ë§í•©ë‹ë‹¤.
- 빌드 단계 없음 Pug (ěť´ě „ěť Jade) ë°Ź Babel과 같은 ě „ě˛ë¦¬ę¸°ę°€ 아닌 HTML ë°Ź ES5 JavaScript로 ě śí•śë©ë‹ë‹¤.
싱글 파일 컴포넌트는 JavaScript 기ë°ě—서 ë°śěťí•는 ë¬¸ě śě ěť„ 해결í•기 위해, 별도ěť
.vue
확장ěžěť 파일로 작성합ë‹ë‹¤. -
filter
는 텍스트 í•식화를 위해 사용ë©ë‹ë‹¤. ěť´ 필터들은 ěžë°”스í¬ë¦˝íЏ 표í„ě‹ťě— íŚŚěť´í”„(|
) 기í¸ě™€ í•¨ę» ě¶”ę°€ë어야 í•©ë‹ë‹¤. í¬ę˛Ś ë‘ ę°€ě§€ 경우ě—서 사용ë ě ěžěеë‹ë‹¤.- ě¤‘ę´„í¸ ëł´ę°„ë˛•
v-bind
표í„식
첫 글ěžëĄĽ 대문ěžëˇś 만드는 로컬 필터를 ě •ěťí•´ë´…시다.
filters: { capitalize: function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) } }
ěť´ 필터를 ě¤‘ę´„í¸ ëł´ę°„ë˛• ë는
v-bind
표í„식 í•¨ę» ě‚¬ěš©í• ě ěžěеë‹ë‹¤.<!-- in mustaches --> {{ username | capitalize }} <!-- in v-bind --> <div v-bind:id="username | capitalize"></div>
-
- ě§€ě— í•„í„°(Local filters): ě§€ě— í•„í„°ëŠ” ě»´íŹ¬ë„ŚíŠ¸ěť ěµě…ě—서 ě •ěťí• ě ěžěеë‹ë‹¤. ěť´ 경우, 필터는 해당 컴포넌트ě—서만 사용 가능합ë‹ë‹¤.
filters: { capitalize: function (value) { if (!value) return '' value = value.toString
() return value.charAt(0).toUpperCase() + value.slice(1) } } - ě „ě— í•„í„°(Global filters): Vue 인스턴스를 만들기 ě „ě— ě „ě—ě 으로 필터를 ě •ěťí• ě ěžěеë‹ë‹¤. ěť´ 경우 Vue 인스턴스 ë‚´ěť ëŞ¨ë“ ě»´íŹ¬ë„ŚíŠ¸ě—서 필터를 ě‚¬ěš©í• ě ěžěеë‹ë‹¤.
Vue.filter('capitalize', function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) }) new Vue({ // ... })
-
일ë°ě 으로 ě•„ëžě™€ 같이, 표í„식ě—서 í•„í„° ë’¤ě— ë 다른 필터를 ě‚¬ěš©í• ě ěžěеë‹ë‹¤.
{{ message | filterA | filterB | filterC ... }}
ę°ę°ěť 필터는 파이프(
|
)로 구분ëë©°,message
는filterA
ěť ę˛°ęłĽę°€filterB
ěť ěí–Ąěť„ ë°›ęł , ę·¸ 결과가 다시filterC
ěť ěí–Ąěť„ 받습ë‹ë‹¤.ě를 들어, ë‚ ě§ś í•ě‹ťěť ëŤ°ěť´í„°ëĄĽ 변경한 ë’¤ 대문ěžëˇś 변경í•ęł ě‹¶ë‹¤ë©´ ě•„ëžě™€ 같이 ě‚¬ěš©í• ě ěžěеë‹ë‹¤.
{{ birthday | dateFormat | uppercase }}
-
필터는 기본ě 으로 ěžë°”스í¬ë¦˝íЏ 함ě이기 때문ě—, ě•„ëžě™€ 같이 ë‘ ę°ś ěť´ěěť ěť¸ě를 받을 ě ěžěеë‹ë‹¤.
{{ message | filterA('arg1', arg2) }}
여기서
filterA
는 세 ę°śěť ěť¸ě를 받는 함ě로 ě •ěťëě—습ë‹ë‹¤.message
ěť ę°’ěť€ 첫ë˛ě§¸ 인ě로 ě „ë‹¬ë ę˛ěť´ë©°, ěśě 문ěžě—´ěť¸'arg1'
은 ë‘ë˛ě§¸ 인ě로 ě „ë‹¬ë ę˛ěť´ë©°, ěžë°”스í¬ë¦˝íЏ 표í„식인arg2
는 표í„식이 ě‹¤í–‰ëś ěť´í›„ě— ě„¸ë˛ě§¸ 인ě로 ě „ë‹¬ë ę˛ěž…ë‹ë‹¤.{{ 2 | exponentialStrength(10) }} // prints 2 power 10 = 1024
-
플러그인은 일ë°ě 으로 ě „ě— ě준 기능을 Vue 어플리케이ě…ě— ě¶”ę°€í•©ë‹ë‹¤.
- ě „ě— ë©”ě†Śë“ś ë는 속성 추가(
<vue-custom-element>
) - í•ë‚ ěť´ěěť ę¸€ëˇśë˛Ś ě—ě…‹ 추가(지시ěž, í•„í„°, 트랜지ě…)
- ě „ě— ëŻąěŠ¤ěť¸ěśĽëˇś 컴포넌트 ěµě…(vuex)
Vue.prototype
를 이용해 Vueě— ěť¸ěŠ¤í„´ěŠ¤ 메소드를 추가- ěś„ěť ę¸°ëŠĄęłĽ í•¨ę» ěžě˛´ API를 ě śęłµí•는 라이브러리(vue-router)
- ě „ě— ë©”ě†Śë“ś ë는 속성 추가(
-
플러그인ě—서는
install
메소드를 ě •ěťí•´ě•Ľ í•©ë‹ë‹¤. ěť´ 메소드는 첫 ë˛ě§¸ 인ěžëˇś Vue ěťě„±ěžě™€ 외부ě—서 ě„¤ě • 가능한 ěµě…ěť„ 파라미터로 ě „ë‹¬ë°›ěŠµë‹ë‹¤.MyPlugin.install = function (Vue, options) { // 1. add global method or property Vue.myGlobalMethod = function () { // some logic ... } // 2. add a global asset Vue.directive('my-directive', { bind (el, binding, vnode, oldVnode) { // some logic ... } ... }) // 3. inject some component options Vue.mixin({ created: function () { // some logic ... } ... }) // 4. add an instance method Vue.prototype.$myMethod = function (methodOptions) { // some logic ... } }
-
Vue.use()
ě „ě— ë©”ě†Śë“śëĄĽ í¸ě¶śí•ě—¬ 플러그인을 ě‚¬ěš©í• ě ěžěеë‹ë‹¤. ěť´ 함ě는 ěťě„±ěžnew Vue()
로 Vue 인스턴스를 ěťě„±í•기 ě „ě— í¸ě¶śë어야 í•©ë‹ë‹¤.// calls `MyPlugin.install(Vue, { someOption: true })` Vue.use(MyPlugin) new Vue({ //... options })
-
Mixins는 Vue ě»´íŹ¬ë„ŚíŠ¸ě— ěž¬ě‚¬ěš© 가능한 기능을 배포í•는 ěś ě—°í•ś 방법입ë‹ë‹¤. ëŻąěŠ¤ěť¸ě— ěˇ´ěž¬í•는 기능들은 í¸ě¶śëś ě»´íŹ¬ë„ŚíŠ¸ěť ę¸°ëŠĄë“¤ęłĽ í•©ěłě§‘ë‹ë‹¤.
mixin 객체는 ëŞ¨ë“ ęµ¬ě„± 요소 ěµě…ěť„ íŹ¬í•¨í• ě ěžěеë‹ë‹¤. 다른 컴포넌트ě—서 재사용ë ě ěžëŠ”
created
ëťĽěť´í”„ě‚¬ěť´í´ í›…ěť„ 가진 믹스인을 작성해봅시다.const myMixin = { created(){ console.log("Welcome to Mixins!") } } var app = new Vue({ el: '#root', mixins: [myMixin] })
Note: 여러 믹스인은 ë°°ě—´ěť í•íśëˇś ě‚¬ěš©í• ě ěžěеë‹ë‹¤.
-
Vue 어플리케이ě…ěť ëŞ¨ë“ ě»´íŹ¬ë„ŚíŠ¸ě— ëŹ™ěťĽí•ś ěµě…ěť´ë‚ ę¸°ëŠĄěť„ 확장해 ě‚¬ěš©í• í•„ěš”ę°€ ěžěť„ ě ěžěеë‹ë‹¤. ěť´ 경우, ě „ě— ëŻąěŠ¤ěť¸ěť„ 활용해 Vueěť ëŞ¨ë“ ě»´íŹ¬ë„ŚíŠ¸ě— ěí–Ąěť„ 줄 ě ěžěеë‹ë‹¤.
Vue.mixin({ created(){ console.log("Write global mixins") } }) new Vue({ el: '#app' })
ěś„ěť ě „ě— ëŻąěŠ¤ěť¸ěť€ 해당 Vue 인스턴스ě—서 ę° ě»´íŹ¬ë„ŚíŠ¸ę°€ ěťě„±ë 때ë§ë‹¤
created
í›…ě—서 로그를 ë°śěťě‹śí‚µë‹ë‹¤. 즉 ëŞ¨ë“ ë‹¨ěťĽ Vue ěť¸ěŠ¤í„´ěŠ¤ě— ěí–Ąěť„ 주기 ë•Śë¬¸ě— ě 게 ěť´ěš©í•ęł ě‹ ě¤‘í•게 사용해야 í•©ë‹ë‹¤. -
Vue CLI를 사용한다면, 믹스인은 일ë°ě 으로
/src/mixins
ë””ë ‰í† ë¦¬ě—서.js
파일로 작성합ë‹ë‹¤.export
키워드로 ě™¸ë¶€ě— ë‚´ëł´ë‚¸ë‹¤ëŠ” ę˛ěť„ ě„ ě–¸í•´ě•Ľ í•ë©° ě‚¬ěš©í• Vue 컴포넌트ě—서import
키워드로 ë¶ëź¬ě¬ ě ěžěеë‹ë‹¤. -
믹스인과 컴포넌트ě—서 충돌í•는 ěµě…ěť´ ěžë‹¤ë©´, ěµě…은 몇 가지 방법을 통해 충돌í•는 ěµě…ěť„ 병합합ë‹ë‹¤.
data
는 재귀ě 으로 병합í•ë, 충돌ë는 속성은 ě»´íŹ¬ë„ŚíŠ¸ěť ëŤ°ěť´í„°ę°€ ěš°ě„ ě 으로 병합ë©ë‹ë‹¤.
var mixin = { data: function () { return { message: 'Hello, this is a Mixin' } } } new Vue({ mixins: [mixin], data: function () { return { message: 'Hello, this is a Component' } }, created: function () { console.log(this.$data); // => { message: "Hello, this is a Component'" } } })
- ëťĽěť´í”„ě‚¬ěť´í´ í›… 함ě는 믹스인 함ěę°€ ë¨Ľě € 실행ëęł , ę·¸ ë‹¤ěťŚě— ě»´íŹ¬ë„ŚíŠ¸ěť í•¨ěę°€ 실행ë©ë‹ë‹¤.
const myMixin = { created(){ console.log("Called from Mixin") } } new Vue({ el: '#root', mixins:[myMixin], created(){ console.log("Called from Component") } }) //Called from Mixin //Called from Component
methods
,components
,directives
ě—시 재귀ě 으로 병합í•ë, 이러한 ę°ťě˛´ě— ě¶©ëŹŚí•는 키가 ěžěť„ 경우 ě»´íŹ¬ë„ŚíŠ¸ěť ěµě…ěť´ ěš°ě„ ěśěś„를 갖습ë‹ë‹¤.
var mixin = { methods: { firstName: function () { console.log('John') }, contact: function () { console.log('+65 99898987') } } } var vm = new Vue({ mixins: [mixin], methods: { lastName: function () { console.log('Murray') }, contact: function () { console.log('+91 893839389') } } }) vm.firstName() // "John" vm.lastName() // "Murray" vm.contact() // "+91 893839389"
-
Vueě—서는 ě‚¬ěš©ěž ě§€ě • ěµě…ěť„ ëł‘í•©í• ë•Ś 기본ě 으로 기존 ę°’ěť„ 덮어는 방법을 이용합ë‹ë‹¤. ë§Śě•˝ ě‚¬ěš©ěž ě •ěťěť 로ě§ěť„ 사용해 커스텀 ěµě…ěť„ 병합í•ë ¤ë©´,
Vue.config.optionMergeStrategies
ě— í•¨ě를 ě¶”ę°€í• í•„ěš”ę°€ ěžěеë‹ë‹¤.Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) { // return mergedVal }
더 ęł ę¸‰ ěě śëŠ” Vuexěť 1.x 병합 ě „ëžµě—서 확인í•실 ě ěžěеë‹ë‹¤.
const merge = Vue.config.optionMergeStrategies.computed Vue.config.optionMergeStrategies.vuex = function (toVal, fromVal) { if (!toVal) return fromVal if (!fromVal) return toVal return { getters: merge(toVal.getters, fromVal.getters), state: merge(toVal.state, fromVal.state), actions: merge(toVal.actions, fromVal.actions) } }
-
지시ěžëŠ” DOM ě—ë¦¬ë¨ĽíŠ¸ě— ë¶€ě°©í• ě ěžëŠ” ëŞ…ë ąě–´ěž…ë‹ë‹¤. ěś„ě—서 본 ę˛ě˛ëźĽ
v-
로 시작í•는 문법을 사용해 Vueę°€ ěť´ ëŞ…ë ąě–´ëĄĽ ěť¸ě‹ťí• ě ěžëŹ„ëˇť 해야 í•©ë‹ë‹¤. 일ë°ě 으로 í•ěś„ ěě¤€ěť DOMěť„ ě śě–´í•기 위해 ě§ě ‘ ě ‘ę·Ľí•´ě•Ľ í• í•„ěš”ę°€ ěžěť„ 때 ěś ěš©í•게 사용ë©ë‹ë‹¤.íŽěť´ě§€ę°€ 로드ë 때
input
ě— ěžëŹ™ěśĽëˇś 포커싱ë는 ě‚¬ěš©ěž ě •ěť ě§€ě‹śěžëĄĽ ě „ě—으로 만들어봅시다.// Register a global custom directive called `v-focus` Vue.directive('focus', { // When the bound element is inserted into the DOM... inserted: function (el) { // Focus the element el.focus() } })
ěť´ě ś ěť´ 지시ěžëŠ”
v-focus
라는 문법과 í•¨ę» ě–´ë–¤ 컴포넌트ě—ě„śë“ ě‚¬ěš©ë ě ěžěеë‹ë‹¤.<input v-focus>
-
지시ěžëĄĽ ě§€ě— ë“±ëˇťí•기 위해서는
directives
ěµě…ěť„ 이용합ë‹ë‹¤.directives: { focus: { // directive definition inserted: function (el) { el.focus() } } }
ěť´ 지시ěžëŠ” ě„ ě–¸ëś í•´ë‹ą 컴포넌트ě—서만 사용ë ě ěžěеë‹ë‹¤.
<input v-focus>
-
ě§€ě‹śěž ę°ťě˛´ę°€ 등록ë 때 몇 ę°śěť ëťĽěť´í”„ ě‚¬ěť´í´ í›…ěť„ ě śęłµí•©ë‹ë‹¤.
bind
: 지시ěžę°€ ě˛ěťŚ ě—ë¦¬ë¨ĽíŠ¸ě— ë¶€ě°©ë 때 한 ë˛ í¸ě¶śë©ë‹ë‹¤.inserted
: 지시ěžę°€ ë¶€ě°©ëś ě—리먼트가 DOMě— ě‚˝ěž…ëě—ěť„ 때 í¸ě¶śë©ë‹ë‹¤.update
: 해당 ě—리먼트가 업데이트 ë 때 í¸ě¶śë©ë‹ë‹¤. í•지만 ě•„ě§ í•ěś„ ě—리먼트는 업데이트 ëě§€ 않은 ěíśěž…ë‹ë‹¤.componentUpdated
: í•ěś„ 컴포넌트까지 업데이트 ëś ěíśěťĽ 때 í¸ě¶śë©ë‹ë‹¤.unbind
: 지시ěžę°€ ě—리먼트ě—서부터 ě‚ě śë 때 í¸ě¶śë©ë‹ë‹¤.
Note: ěś„ěť í›…ě—서는 íŠąě •í•ś ě „ë‹¬ěť¸ěž(Argument)를 받는다.
-
ëŞ¨ë“ í›…ě—서는 ě „ë‹¬ěť¸ěžëˇś
el
,binding
와vnode
를 ę°–ęł ěžěеë‹ë‹¤. 그와 함ę», update와 componentUpdatedí›…ě—서는 ě 값과 ěť´ě „ ę°’ěť„ ëą„ęµí•기 위해oldVnode
를 추가ě 으로 ę°–ęł ěžěеë‹ë‹¤.el
: 해당 지시ěžę°€ ë¶€ě°©ëś ě—리먼트로, 이를 이용해 DOMěť„ ěˇ°ěž‘í• ě ěžěеë‹ë‹¤.binding
: ě•„ëžěť 속성을 가진 객체입ë‹ë‹¤.name
: 지시ěžěť 이름으로,v-
ě ‘ë‘사가 ě śę±°ëś ěť´ë¦„ěž…ë‹ë‹¤.value
: 지시ěžě—서 ě „ë‹¬ 받은 ę°’ěž…ë‹ë‹¤. ë§Śě•˝v-my-directive="1 + 1"
라면2
ę°€ ë©ë‹ë‹¤.oldValue
: ěť´ě „ 값으로,update
와componentUpdated
í›…ě—서만 ě‚¬ěš©í• ě ěžěеë‹ë‹¤. 이를 통해 ę°’ěť´ 변경ëě—는지 아닌지를 í™•ěť¸í• ě ěžěеë‹ë‹¤.expression
: 문ěžě—´ëˇś ë°”ěť¸ë”©ëś í‘śí„식입ë‹ë‹¤. ë§Śě•˝v-my-directive="1 + 1"
라면"1 + 1"
ěť´ ë©ë‹ë‹¤..arg
: 지시ěžěť ě „ë‹¬ěť¸ěžěž…ë‹ë‹¤. ë§Śě•˝v-my-directive:foo
라면"foo"
ę°€ ë©ë‹ë‹¤..modifiers
: ě식어가 íŹ¬í•¨ëś ę°ťě˛´ěž…ë‹ë‹¤. ë§Śě•˝v-my-directive.foo.bar
라면{ foo: true, bar: true }
ę°€ ë©ë‹ë‹¤.
vnode
: Vueěť ě»´íŚŚěťĽëź¬ě— ěťí•´ ěťě„±ëś ę°€ě 노드입ë‹ë‹¤.oldVnode
: ěť´ ě „ěť ę°€ě 노드로,update
와componentUpdated
í›…ě—서만 ě‚¬ěš©í• ě ěžěеë‹ë‹¤.
-
지시ěžëŠ” ěś íš¨í•ś ěžë°”스í¬ë¦˝íЏ 표í„식은 ëŞ¨ë‘ ěěš©í• ě ěžěеë‹ë‹¤. 따라서 지사ěžě— 여러 값들을 ě „ë‹¬í•ë ¤ë©´, ěžë°”스í¬ë¦˝íЏ 객체 리터럴을 이용해 ě „ë‹¬í• ě ěžěеë‹ë‹¤.
<div v-avatar="{ width: 500, height: 400, url: 'path/logo', text: 'Iron Man' }"></div>
ěť´ě ś
v-avatar
지시ěžëĄĽ ě „ě—으로 ě„¤ě •í•´ë´…ě‹śë‹¤.Vue.directive('avatar', function (el, binding) { console.log(binding.value.width) // 500 console.log(binding.value.height) // 400 console.log(binding.value.url) // path/logo console.log(binding.value.text) // "Iron Man" })
-
드문 경우지만, 다른 훅과는 ě관없이
bind
ë‚update
í›…ě—서 같은 동작을 í•기 ě›í• ě ěžěеë‹ë‹¤. ěť´ 경우ě—는 함ě 약어를 ě‚¬ěš©í• ě ěžěеë‹ë‹¤.Vue.directive('theme-switcher', function (el, binding) { el.style.backgroundColor = binding.value })
-
일ë°ě 인 경우 Vueěť í…śí”Śë¦żěť„ 이용해 HTMLěť„ 작성í•는 ę˛ěť„ 권장합ë‹ë‹¤. í•지만
input
ěť´ë‚slot
ěť ę°’ěť„ 이용해 동ě 인 컴포넌트를 ěťě„±í•는 ę˛ě˛ëźĽ, 일부 특별한 경우ě—는 JavaScriptę°€ 필요한 경우가 ěžěеë‹ë‹¤. 이때render
함ě를 사용í•ë©°,render
함ě는 JavaScript로 작성í•기 ë•Śë¬¸ě— í”„ëˇśę·¸ëžë°Ť í™ę˛˝ěť„ ě¨ě „íž ěť´ěš©í• ě ěžë‹¤ëŠ” 장ě ěť´ ěžěеë‹ë‹¤. -
render
함ě는createElement
라는 함ě를 첫 ë˛ě§¸ 인ěžëˇś 받아 ę°€ě 노드를 ěťě„±í•는 함ěěž…ë‹ë‹¤. ë‚´ë¶€ě 으로 Vueěť í…śí”Śë¦żěť€ 빌드 í€ěž„ě—서render
함ě를 이용해 컴파일í•ęł ěžěеë‹ë‹¤. 그러므로 템플릿은render
함ě를 문법ě 으로 보기 쉽게 만들어 놓은 ę˛ě— 가깝습ë‹ë‹¤.<div>
íśę·¸ëĄĽ ę°ę° 템플릿과render
함ě를 이용해 작성해봅시다. 템플릿은 ě•„ëžě™€ 같이 ěž‘ě„±í• ě ěžěеë‹ë‹¤.<template> <div :class="{'is-rounded': isRounded}"> <p>Welcome to Vue render functions</p> </div> </template>
ěś„ěť í…śí”Śë¦żěť„
render
함ě로 작성í•ë©´ ě•„ëžě™€ 같습ë‹ë‹¤.render: function (createElement) { return createElement('div', { 'class': { 'is-rounded': this.isRounded } }, [ createElement('p', 'Welcome to Vue render functions') ]); },
-
createElement
함ě는 몇 ę°€ě§€ěť ě•˝ě†Ťëś ě „ë‹¬ěť¸ěžëĄĽ 받는데, 이를 이용해 템플릿ě—서 사용ë는 기능을 JavaScript 코드로 ěž‘ě„±í• ě ěžěеë‹ë‹¤.// @returns {VNode} createElement( // An HTML tag name, component options, or async function resolving to one of these. // Type is {String | Object | Function} // Required. 'div', // A data object corresponding to the attributes you would use in a template. //Type is {Object} // Optional. { // Normal HTML attributes attrs: { id: 'someId' }, // Component props props: { myProp: 'somePropValue' }, // DOM properties domProps: { innerHTML: 'This is some text' }, // Event handlers are nested under `on` on: { click: this.clickHandler }, // Similar to `v-bind:style`, accepting either a string, object, or array of objects. style: { color: 'red', fontSize: '14px' }, //Similar to `v-bind:class`, accepting either a string, object, or array of strings and objects. class: { classsName1: true, classsName2: false }, .... }, // Children VNodes, built using `createElement()`, or using strings to get 'text VNodes'. // Type is {String | Array} // Optional. [ 'Learn about createElement arguments.', createElement('h1', 'Headline as a child virtual node'), createElement(MyComponent, { props: { someProp: 'This is a prop value' } }) ] )
ěžě„¸í•ś 내용은 공식 문서ě—서 í™•ěť¸í• ě ěžěеë‹ë‹¤.
-
컴포넌트 íŠ¸ë¦¬ěť ëŞ¨ë“ ę°€ě 노드(VNodes)는 ęł ěś í•´ě•Ľ í•©ë‹ë‹¤. 즉, ě§ě ‘ ę°€ě 노드를 여러 ë˛ ě‚¬ěš©í• ě는 없습ë‹ë‹¤. ë§Śě•˝ 같은 ě—ë¦¬ë¨ĽíŠ¸ë‚ ě»´íŹ¬ë„ŚíŠ¸ëĄĽ 여러 ë˛ ë°ëłµí•´ě„ś 사용해야 한다면, íŚ©í† ë¦¬ 패턴을 이용해 작성해야 í•©ë‹ë‹¤.
ě•„ëžěť
render
함ě는h1
ě—리먼트를 세 ë˛ ë°ëłµí•ë ¤ í–기 ë•Śë¬¸ě— ěś íš¨í•ě§€ 않습ë‹ë‹¤.render: function (createElement) { var myHeadingVNode = createElement('h1', 'This is a Virtual Node') return createElement('div', [ myHeadingVNode, myHeadingVNode, myHeadingVNode ]) }
íŚ©í† ë¦¬ 패턴을 ěť´ěš©í•ë©´ ë©ë‹ë‹¤.
render: function (createElement) { return createElement('div', Array.apply(null, { length: 3 }).map(function () { return createElement('h1', 'This is a Virtual Node') }) ) }
-
Vueě—서 HTMLěť„ 작성í•는데 사용ë는 템플릿과 render 함ě를 ëą„ęµí•´ë´…시다.
템플릿(Templates) ë ŚëŤ” 함ě(Render function) v-if
와v-for
를 이용해 조건문/ë°ëłµë¬¸ 실행JavaScriptěť if else
문과map
메소드로 조건문/ë°ëłµë¬¸ 실행v-model
로 ě–‘ë°©í–Ą 바인딩바인딩과 이벤트를 ě§ě ‘ ě„¤ě • Capture 이벤트 ě식어는 .passive
,.capture
,.once,
.capture.once
,.once.capture
&, !, ~, ~! 이벤트 ě식어와 키 ě식어: .stop
,.prevent
,.self
, keys(.enter
,.13
) and Modifiers Keys(.ctrl
,.alt
,.shift
,.meta
)JavaScript로 해결, event.stopPropagation()
,event.preventDefault()
,if (event.target !== event.currentTarget) return
,if (event.keyCode !== 13) return
,if (!event.ctrlKey) return
슬롯 속성 활용 ë ŚëŤ” 함ěěť this.$slots
와this.$scopedSlots
활용 -
함ěí• ě»´íŹ¬ë„ŚíŠ¸ëŠ”
context
를 통해 ě „ë‹¬ë°›ěť€ ě •ëł´ëˇśë§Ś ěťě„±ë는 간단한 컴포넌트입ë‹ë‹¤.- ěíś ě—†ěťŚ(Stateless): 즉
data
ę°€ 없습ë‹ë‹¤ - 인스턴스 없음(Instanceless): 즉
this
ę°€ 없습ë‹ë‹¤
이 경우,
functional: true
속성을 이용해 컴포넌트를 함ěí•으로 ěž‘ě„±í• ě ěžěеë‹ë‹¤.Vue.component('my-component', { functional: true, // Props are optional props: { // ... }, // To compensate for the lack of an instance, // we are now provided a 2nd context argument. render: function (createElement, context) { // ... } })
- ěíś ě—†ěťŚ(Stateless): 즉
-
- ë‘ í”„ë ěž„ě›Śí¬ ëŞ¨ë‘ ę°€ě DOM 모델을 사용합ë‹ë‹¤.
- ë°ěť‘ě ěť´ęł ěˇ°í•© 가능한 컴포넌트를 ě śęłµí•©ë‹ë‹¤.
- 코어 라이브러리ě—ë§Ś 집중í•ęł ěžęł , 라우팅 ë°Ź ěíś ę´€ë¦¬ě™€ 같은 라이브러리가 부가ě 으로 ěžěеë‹ë‹¤.
-
특징 Vue React í€ěž… JavaScript MVC 프ë ěž„ě›Śí¬ JavaScript 라이브러리 플랫폼 웹을 ěš°ě„ ě 으로 웹과 네이티브 ëŞ¨ë‘ ëłµěžˇëŹ„ ě대ě 으로 간단 ě대ě 으로 복잡 빌드 ě–´í”Śë¦¬ěĽ€ěť´ě… Vue-cli CRA ( Create-React-App
) -
- ę°€ëłŤęł ëą ë¦…ë‹ë‹¤.
- 템플릿이 ę°śë°ś ęłĽě •ěť„ 쉽게 만들어줍ë‹ë‹¤.
- JSXě— ëą„í•´ 가벼운 JavaScript 문법을 사용합ë‹ë‹¤.
-
- í° ę·śëŞ¨ěť ě–´í”Śë¦¬ěĽ€ěť´ě…ěť„ ěś ě—°í•게 만들 ě ěžěеë‹ë‹¤.
- 테스트가 쉽습ë‹ë‹¤.
- 모바일 앱 ě śěž‘ě—도 ě í•©í•©ë‹ë‹¤.
- ěťíśęł„ę°€ í¬ęł 풍부합ë‹ë‹¤.
-
Vueěť ę°śë°ś ě´ę¸° 단계ě—서 AngularJS를 ě°¸ęł í–기 때문ě—, Vue와 AngularJSěť ë¬¸ë˛•ěť€ ěë‹ąíž ëą„ěŠ·í•©ë‹ë‹¤. í•지만 차이ě ě—시 존재합ë‹ë‹¤.
특징 Vue AngularJS 복잡도 배우기 쉬운 API와 ë””ěžěť¸ 프ë 임워í¬ę°€ 꽤 í¬ęł í€ěž…스í¬ë¦˝íЏ ë“±ěť ě§€ě‹ť í•„ěš” 데이터 바인딩 ě–‘ë°©í–Ą 바인딩 단방향 바인딩 ě´ę¸° ë¦´ë¦¬ě¦ 2014 2016 모델 ę°€ě DOM ę¸°ë° MVC ěž‘ě„±ëś ě–¸ě–´ JavaScript TypeScript -
<component>
íśę·¸ě—서v-bind:is
로 ë°”ěť¸ë”©ëś ě»´íŹ¬ë„ŚíŠ¸ëĄĽ 동ě 으로 ě „í™í• ě ěžěеë‹ë‹¤.new Vue({ el: '#app', data: { currentPage: 'home' }, components: { home: { template: "<p>Home</p>" }, about: { template: "<p>About</p>" }, contact: { template: "<p>Contact</p>" } } })
ěť´ě ś 템플릿ě—서
<component>
íśę·¸ě— 바인딩 ë 컴포넌트를 ě„¤ě •í• ě ěžěеë‹ë‹¤.<div id="app"> <component v-bind:is="currentPage"> <!-- component changes when currentPage changes! --> <!-- output: Home --> </component> </div>
-
<keep-alive>
는 ě»´íŹ¬ë„ŚíŠ¸ěť ěíśëĄĽ 보존해서 재 ë ŚëŤ”ë§ěť„ 막아주는 ě¶”ě 컴포넌트입ë‹ë‹¤. ë§Śě•˝ 동ě 인 컴포넌트를<keep-alive>
íśę·¸ëˇś ę°ě‹Ľë‹¤ë©´, 컴포넌트 인스턴스를 ě—†ě• ě§€ ě•Šęł ë©”ëŞ¨ë¦¬ě— ěś ě§€í•´ 보존합ë‹ë‹¤.<!-- Inactive components will be cached! --> <keep-alive> <component v-bind:is="currentTabComponent"></component> </keep-alive>
ë§Śě•˝ 조건문이 ěžë‹¤ë©´, 해당 ěˇ°ę±´ěť í•ěś„ 컴포넌트만 ë ŚëŤ”ë§ë©ë‹ë‹¤.
<!-- multiple conditional children --> <keep-alive> <comp-a v-if="a > 1"></comp-a> <comp-b v-else></comp-b> </keep-alive>
Note:
<keep-alive>
는 DOMě— ë ŚëŤ”ë§ ëě§€ 않습ë‹ë‹¤. -
대규모 응용 프로그램ě—서는 응용 프로그램을 더 작은 덩어리로 ë‚ë„ęł ě‹¤ě śëˇś í•„ěš”í• ë•Śë§Ś 서버ě—서 컴포넌트를 로드해야 í• ě도 ěžěеë‹ë‹¤. Vue를 사용í•ë©´ 컴포넌트 ě •ěťëĄĽ 비동기식으로 해결í•는 íŚ©í† ë¦¬ 함ě로 컴포넌트를 ě •ěť í• ě ěžěеë‹ë‹¤.
Vue.component('async-webpack-example', function (resolve, reject) { // Webpack automatically split your built code into bundles which are loaded over Ajax requests. require(['./my-async-component'], resolve) })
Vue는 Vue는 컴포넌트가 ë ŚëŤ”ë§ë어야 í• ë•Śë§Ś íŚ©í† ë¦¬ 함ě를 ě‹¤í–‰ě‹śí‚¤ęł , ěť´í›„ěť ë‚ě¤‘ě— ěžěť„ ë¦¬ë ŚëŤ”ë§ěť„ 위해 결과를 ěşě‹śí•©ë‹ë‹¤.
-
비동기 컴포넌트 íŚ©í† ë¦¬ëŠ” 다음 í•íśěť 객체를 ë°í™í• ě ěžěеë‹ë‹¤.
const AsyncComponent = () => ({ // The component to load (should be a Promise) component: import('./MyComponent.vue'), // A component to use while the async component is loading loading: LoadingComponent, // A component to use if the load fails error: ErrorComponent, // Delay before showing the loading component. Default: 200ms. delay: 200, // The error component will be displayed if a timeout is // provided and exceeded. Default: Infinity. timeout: 3000 })
-
í•ěś„ 컴포넌트ě—
inline-template
속성이 ěˇ´ěž¬í• ë•Ś, 컴포넌트는 ë‚´ë¶€ 컨í…ě¸ ëĄĽ 템플릿으로 사용합ë‹ë‹¤. 따라서 보다 ěś ě—°í•ś 템플릿 작성이 가능합ë‹ë‹¤.<my-component inline-template> <div> <h1>Inline templates</p> <p>Treated as component component owne content</p> </div> </my-component>
Note:
inline-template
은 í…śí”Śë¦żěť ë˛”ěś„ëĄĽ ě¶”ëˇ í•기 ě–´ë µę˛Ś ë§Śë“ë‹ë‹¤. 가장 좋은 방법은template
ěµě…ěť„ 사용í•ę±°ë‚.vue
파일ěťtemplate
ě—리먼트를 사용í•ě—¬ 컴포넌트 ë‚´ë¶€ě— í…śí”Śë¦żěť„ ě •ěťí•는 ę˛ěž…ë‹ë‹¤. -
템플릿를 ě •ěťí•는 ë 다른 방법은
text/x-template
ěś í•ěť ěŠ¤í¬ë¦˝íЏ ě—리먼트 ë‚´ë¶€ěť ID로 템플릿을 참조í•는 ę˛ěž…ë‹ë‹¤.<script type="text/x-template" id="script-template"> <p>Welcome to X-Template feature</p> </script>
Vue.component('x-template-example', { template: '#script-template' })
-
컴포넌트는 ěžě‹ ěť í…śí”Śë¦żě—서 ěžę¸° ěžě‹ ěť„ 재귀ě 으로 í¸ě¶śí• ě ěžěеë‹ë‹¤.
Vue.component('recursive-component', { template: `<!--Invoking myself!--> <recursive-component></recursive-component>` });
재귀 컴포넌트는 ë¸”ëˇśę·¸ěť ëŤ§ę¸€ěť´ë‚ ë©”ë‰´ě˛ëźĽ ěěś„ 컴포넌트와 í•ěś„ 컴포넌트가 동등한 기능을 í• ë•Ś ěś ěš©í•©ë‹ë‹¤.
Note: 위와 같은 컴포넌트는 최대 ěŠ¤íť í¬ę¸° ě´ęłĽ ě¤ëĄę°€ ë°śěťí•므로 재귀 í¸ě¶śěť´ 조건부인지 확인해야 í•©ë‹ë‹¤.
-
복잡한 어플리케이ě…ě—서 Vue 컴포넌트가 서로가 서로를 í¸ě¶śí•ęł ěžëŠ” ě황이 ë°śěťí• ě ěžěеë‹ë‹¤. 컴포넌트 A와 컴포넌트 Bę°€ 서로 ěśí™ 참조를 í•ęł ěžëŠ” ě황을 살펴봅시다.
//ComponentA <div> <component-b > </div>
//ComponentB <div> <component-a > </div>
이런 경우는
beforeCreate
라이프 ě‚¬ěť´í´ í›… 시ě 까지 ę¸°ë‹¤ë ¸ë‹¤ę°€ 해당 컴포넌트를 등록í•ę±°ë‚, ě›ąíŚ©ěť ëą„ëŹ™ę¸°import
를 활용합ë‹ë‹¤.Solution1:
beforeCreate: function () { this.$options.components.componentB = require('./component-b.vue').default }
Solution2:
components: { componentB: () => import('./component-b.vue') }
-
Google í¬ëˇ¬ 앱과 같은 일부 í™ę˛˝ě—서는 CSP(컨í…ě¸ ëł´ě• ě •ě±…)를 ě ěš©í•ě—¬ 표í„식을 평가í•는 데
new Function()
ěť„ ě‚¬ěš©í• ě 없습ë‹ë‹¤. ě „ě˛´ 빌드는 ěť´ 기능을 사용í•ě—¬ 템플릿을 컴파일í•므로 이러한 í™ę˛˝ě—서는 ě‚¬ěš©í• ě 없습ë‹ë‹¤.ë°ë©´ ëź°í€ěž„ ě „ěš© 빌드는 CSP와 완벽í•게 í¸í™ë©ë‹ë‹¤. Webpack + vue-loader ë는 Browserify + vueify로 ëź°í€ěž„ ě „ěš© 빌드를 사용í•는 경우 템플릿은 CSP í™ę˛˝ě—서 완벽í•게 작동í•는
render
함ě로 미리 컴파일ë©ë‹ë‹¤. -
1. ě „ě˛´ 빌드(Full): 컴파일러와 ëź°í€ěž„ 빌드를 ëŹ™ě‹śě— íŹ¬í•¨í•©ë‹ë‹¤. 템플릿을 작성한 경우 필요합ë‹ë‹¤.
2. ëź°í€ěž„ 빌드(Runtime): Vue 인스턴스 ěťě„±ęłĽ
render
함ě, ę°€ě 돔을 포함í•ęł ěžě§€ë§Ś 컴파일러 빌드를 포함í•ęł ěžě§€ 않습ë‹ë‹¤. -
í€ěž… UMD CommonJS ES Module (for bundlers) ES Module (for browsers) ě „ě˛´ 빌드 vue.js vue.common.js vue.esm.js vue.esm.browser.js ëź°í€ěž„ 빌드 vue.runtime.js vue.runtime.common.js vue.runtime.esm.js NA ě „ě˛´ 빌드 (배포 모드) vue.min.js NA NA vue.esm.browser.min.js ëź°í€ěž„ 빌드 (배포 모드) vue.runtime.min.js NA NA NA -
alias
를 이용해 Vue를 ě„¤ě •í• ě ěžěеë‹ë‹¤.module.exports = { // ... resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1 } } }
-
Vue는 컴파일러를 이용해 템플릿을
render
함ě로 변í™í•©ë‹ë‹¤.// this requires the compiler new Vue({ template: '<div>{{ message }}</div>' }) // this does not new Vue({ render (h) { return h('div', this.message) } })
-
DevTool은 Vue 어플리케이ě…ěť„ ě‚¬ěš©ěž ěąśí™”ě 인 인터íŽěť´ěŠ¤ëˇś 디버그 í• ě ěžę˛Ś 도와주는 ë¸ŚëťĽěš°ě € 확장 프로그램입ë‹ë‹¤.
Note: Vue íŽěť´ě§€ę°€ 배포 모드일 경우ě—는 DevTool로 ë””ë˛„ę·¸í• ě 없습ë‹ë‹¤.
-
ECMAScript5를 ě§€ě›í•는 ë¸ŚëťĽěš°ě €ě—서 동작 가능합ë‹ë‹¤. IE8 ěť´í•ěť ë¸ŚëťĽěš°ě €ě—서는 ě§€ě›í•ě§€ 않습ë‹ë‹¤.
-
Vue는 jsdelivr, unpkg, cdnjsě—서 ě śęłµí•는 CDNěť„ 이용해서도 사용이 가능합ë‹ë‹¤. 일ë°ě 으로 ě´ę¸° 기획, 학습용으로 ě í•©í•©ë‹ë‹¤.
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.7/dist/vue.js"></script>
<script type="module"> import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.7/dist/vue.esm.browser.js' </script>
Note: ë˛„ě „ ě •ëł´ëĄĽ 지우면 í•ě ěµśě‹ ë˛„ě „ěť„ ę°€ě ¸ěµë‹ë‹¤.
-
매우 드문 경우지만, 데이터가 변경ëě§€ 않ě•음ě—도 재 ë ŚëŤ”ë§ěť„ 위해 ę°•ě śëˇś 업데이트를 ë°śěťě‹śěĽśě•Ľ í• ě도 ěžěеë‹ë‹¤. ěť´ 경우
vm.$forceUpdate()
API 메소드를 ěť´ěš©í• ě ěžěеë‹ë‹¤.Note: ëŞ¨ë“ í•ěś„ 컴포넌트ě—는 ěí–Ąěť´ 미ěąě§€ 않으며, 슬롯 ę·¸ ěžě˛´ę°€ ě‚˝ěž…ëś ěŠ¬ëˇŻ ěžě˛´ ë°Ź í•ěś„ 컴포넌트ě—ë§Ś ěí–Ąěť„ 미칩ë‹ë‹¤.
-
많은 ě–‘ěť ě •ě 컨í…ě¸ ëĄĽ ë ŚëŤ”ë§ í• ë•Ś, 성능 í–Ąěěť„ 위해 ě—리먼트 ë°Ź 컴포넌트를 한ë˛ë§Ś ë ŚëŤ”ë§í•는 용도로 사용합ë‹ë‹¤.
Vue.component('legal-terms', { template: ` <div v-once> <h1>Legal Terms</h1> ... a lot of static content goes here... </div> ` })
Note: ě •ě 컨í…ě¸ ę°€ 많아서 ëŠë ¤ě§€ëŠ” 일이 ë°śěťí•ě§€ 않는 한, 과다í•게 사용í•ě§€ 않는 ę˛ěť´ 좋습ë‹ë‹¤.
-
루트 Vue 인스턴스(
new Vue()
)는$root
속성을 이용해 ě ‘ę·Ľí• ě ěžěеë‹ë‹¤.// The root Vue instance new Vue({ data: { age: 26 }, computed: { fullName: function () { /* ... */ } }, methods: { interest: function () { /* ... */ } } })
루트 ěť¸ěŠ¤í„´ěŠ¤ěť ëŤ°ěť´í„°ě™€ 메소드들을 í•ěś„ 컴포넌트ě—서 ě•„ëžě™ 10000 € 같은 방법으로 ě ‘ę·Ľí• ě ěžěеë‹ë‹¤.
// Get root data this.$root.age // Set root data this.$root.age = 29 // Access root computed properties this.$root.fullName // Call root methods this.$root.interest()
ěíś ę´€ë¦¬ëĄĽ 위한 용도라면 Vuex를 사용í•는 ę˛ěť´ 낫습ë‹ë‹¤.
-
ě•„ëžëŠ” Vue를 사용í•ęł ěžëŠ” ěś ëŞ… 기업들입ë‹ë‹¤.
- Netflix
- Adobe
- Xiaomi
- Alibaba
- WizzAir
- EuroNews
- Laracasts
- GitLab
- Laracasts
-
기본
render
함ěę°€ ë ŚëŤ”ë§ ëŹ„ě¤‘ ě—러가 ë°śěťí•ë©´, 대체ë는 ë ŚëŤ”ë§ ę˛°ęłĽëĄĽ ě śęłµí•©ë‹ë‹¤.renderError
ěť ë‘ ë˛ě§¸ ě „ë‹¬ěť¸ěžëˇś ě—러가 ě „ë‹¬ë©ë‹ë‹¤.new Vue({ render (h) { throw new Error('An error') }, renderError (h, err) { return h('div', { style: { color: 'red' }}, err.stack) } }).$mount('#app')
-
ěěś„ 컴포넌트ě—서는 í•ěś„ 컴포넌트들을
$children
배열로 참조í•ë©°, í•ěś„ 컴포넌트ě—서 ěěś„ 컴포넌트를$parent
속성으로 참조합ë‹ë‹¤. -
Vuex는 Vue.js ě• í”Śë¦¬ěĽ€ěť´ě…ěť„ 위한 ěíś ę´€ë¦¬ 패턴 + 라이브러리(Fluxě—서 ěę°ěť„ 받은 ě• í”Śë¦¬ěĽ€ěť´ě… ě•„í‚¤í…Ťě˛)ěž…ë‹ë‹¤. ě측 가능한 방식으로만 ěíśę°€ 변경ë ě ěžëŹ„ëˇť 보장í•는 규칙을 ę°€ě§€ęł ěžëŠ” ě• í”Śë¦¬ěĽ€ěť´ě…ěť ëŞ¨ë“ ě»´íŹ¬ë„ŚíŠ¸ëĄĽ 위한 중앙 집중식 ě €ěžĄě†Śěž…ë‹ë‹¤.
-
ěíś ę´€ë¦¬ěť ěŁĽěš” 구성요소는 ěíś ë°Ź ë·°, 액ě…ěž…ë‹ë‹¤. 이러한 ęµ¬ě„±ěš”ě†Śě— ë”°ëĄ¸ 패턴을 ě• í”Śë¦¬ěĽ€ěť´ě…ě—서 ěíś ę´€ë¦¬ íŚ¨í„´ěť´ëťĽęł í•©ë‹ë‹¤. ě•„ëžě— ěžě„¸í•ś 구성 요소가 ěžě„¸íž 설명ëě–´ ěžěеë‹ë‹¤.
- ěíśëŠ” 앱을 구동시키는 ě›ě˛śěž…ë‹ë‹¤.
- 뷰는 단지 ěíśěť ě„ ě–¸ě 매핑입ë‹ë‹¤.
- 액ě…은 ë·°ě—서 ě‚¬ěš©ěž ěž…ë Ąě— ë°ěť‘í•ě—¬ ěíśę°€ ëł€í• ě ěžëŹ„ëˇť í•는 방법입ë‹ë‹¤. ěś„ěť 3가지 구성요소와 í•¨ę» ěíś ę´€ë¦¬ 패턴을 따르는 ěą´ěš´í„° ě를 ë“¤ě–´ëł´ę˛ ěŠµë‹ë‹¤.
new Vue({ // state data () { return { count: 0 } }, // view template: ` <div>{{ count }}</div> `, // actions methods: { increment () { this.count++ } } })
-
Vue.js는 props 속성을 통해 단방향 데이터 íťë¦„ 모델을 표í„í•©ë‹ë‹¤. vuexě—서 이와 동일한 ę°śë…은 ě•„ëžě™€ 같이 ë‚í€ë‚Ľ ě ěžěеë‹ë‹¤.
-
Vue loader는 Vue 컴포넌트를 싱글 파일 컴포넌트(SFC, SFCs)ëťĽęł í•는 í•식으로 ěž‘ě„±í• ě ěžëŠ” 웹팩용 로더입ë‹ë‹¤. ě를 들어
HelloWorld
라는 SFC를 작성í•ë©´ ě•„ëžě™€ 같습ë‹ë‹¤.<template> <div class="greeting">{{ message }}</div> </template> <script> export default { data () { return { message: 'Hello world for vueloader!' } } } </script> <style> .greeting { color: blue; } </style>
-
Vue-Loader
ěť ě„¤ě •ěť€ 웹팩 ě„¤ě •ě— Vue Loaderěť í”Śëź¬ę·¸ěť¸ěť„ 추가í•기 ë•Śë¬¸ě— ë‹¤ëĄ¸ 로더와는 약간 다릅ë‹ë‹¤. Vue 로더 플러그인은 ě •ěťëś 다른 ę·śěą™(js ë°Ź css ę·śěą™)ěť„ ëłµě śí•ě—¬.vue
파일ě—서 해당 언어 블록(script
ë°Źstyle
)ě— ě ěš©í•기 위해 필요합ë‹ë‹¤. Vue ëˇśëŤ”ě— ëŚ€í•ś 웹팩 ęµ¬ě„±ěť ę°„ë‹¨í•ś ě는 다음과 같습ë‹ë‹¤.// webpack.config.js const VueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = { mode: 'development', module: { rules: [ { test: /\.vue$/, loader: 'vue-loader' }, // this will apply to both plain `.js` files and `<script>` blocks in `.vue` files { test: /\.js$/, loader: 'babel-loader' }, // this will apply to both plain `.css` files and `<style>` blocks in `.vue` files { test: /\.css$/, use: [ 'vue-style-loader', 'css-loader' ] } ] }, plugins: [ // make sure to include the plugin for cloning and mapping them to respective language blocks new VueLoaderPlugin() ] }
-
- ě 대 경로(Absolute path): ë§Śě•˝ URL 경로가
/images/loader.png
와 같은 ě 대 경로라면 그대로 보존ë©ë‹ë‹¤. - ě대 경로(Relative path): ë§Śě•˝ URL 경로가
./images/loader.png
와 같은 ě 대 경로라면 ě대 ëŞ¨ë“ ěš”ě˛(require(./images/loader.png)
)으로 컴파일ëęł íŚŚěťĽ ě‹śěŠ¤í…śěť íŹ´ëŤ” 구조를 기ë°ěśĽëˇś 해결ë©ë‹ë‹¤. - ~로 시작í•는 경로(URLs starts with ~ symbol): ë§Śě•˝ URL 경로가
./some-node-package/loader.png
와 같은~
로 시작ëśë‹¤ë©´, ëŞ¨ë“ ěš”ě˛ěśĽëˇś 컴파일ë©ë‹ë‹¤. - @로 시작í•는 경로(URLs starts with @ symbol): ë§Śě•˝ URL 경로가
@
로 시작ëśë‹¤ë©´, ëŞ¨ë“ ěš”ě˛ěśĽëˇś 컴파일ë©ë‹ë‹¤. ěť´ę˛ěť€ 웹팩config
ě—@
ě— ëŚ€í•ś ëł„ěąěť´ ěžëŠ” ę˛˝ěš°ě— ěś ěš©í•ë©°, 기본ě 으로vue-cli
ę°€ ë§Śë“ ëŞ¨ë“ í”„ëˇśě ťíŠ¸ě—서/src
를 가리킵ë‹ë‹¤.
- ě 대 경로(Absolute path): ë§Śě•˝ URL 경로가
-
Vue-loader
는 ěž‘ě„±ëś ě–¸ě–´ 블록ěťlang
속성을 이용해 ě ě 한 로더를 ě ěš©í•ęł ě›ąíŚ© ě„¤ě •ě— ě ěš©ëś ę·śěą™ěť„ 따릅ë‹ë‹¤.Vue-loader
ě—서 SASS, LESS, Stylusë‚ PostCSS 같은 ě „ě˛ë¦¬ę¸°ëĄĽ ě‚¬ěš©í• ě ěžěеë‹ë‹¤. -
범위를 가지는 CSS(Scoped CSS)는 Vueě—서 싱글 파일 컴포넌트ě—서 해당 ě»´íŹ¬ë„ŚíŠ¸ě— ěž‘ě„±ëś CSSę°€ 다른 ě»´íŹ¬ë„ŚíŠ¸ě— ěí–Ąěť„ 미ěąě§€ 않도록 ę·¸ ě ěš© 범위를 ě śí•śí•는 ę˛ěť„ ěťëŻ¸í•©ë‹ë‹¤. 즉,
<style>
íśę·¸ę°€scoped
속성을 ę°€ě§€ęł ěžë‹¤ë©´, 해당 CSS는 해당 컴포넌트ě—서만 ěí–Ąěť„ 미칩ë‹ë‹¤.<style scoped> .greeting { color: green; } </style>
<template> <div class="greeting">Let's start Scoped CSS</div> </template>
ěś„ěť ě˝”ë“śëŠ” ě•„ëžëˇś 변í™ë©ë‹ë‹¤.
<style scoped> .greeting[data-v-f3f3eg9] { color: green; } </style>
<template> <div class="greeting" data-v-f3f3eg9>Let's start Scoped CSS</div> </template>
-
범위가 ě§€ě •ëś ěŠ¤í€ěťĽęłĽ 범위가 ě§€ě •ëě§€ 않은 스í€ěťĽěť€ 동일한 ě»´íŹ¬ë„ŚíŠ¸ě— íŹ¬í•¨í• ě ěžěеë‹ë‹¤.
<style> /* global styles */ </style> <style scoped> /* local styles */ </style>
-
범위가 ě§€ě •ëś CSSě—서, ěžě‹ť ě»´íŹ¬ë„ŚíŠ¸ě— ěí–Ąěť„ 미ěąę˛Ś í•는 방법은
>>>
ě—°ě‚°ěžëĄĽ 사용í•ë©´ ë©ë‹ë‹¤.<style scoped> .class1 >>> .class2 { /* ... */ } </style>
ěś„ěť CSS는 ě•„ëžëˇś 컴파일ë©ë‹ë‹¤.
.class1[data-v-f3f3eg9] .class2 { /* ... */ }
Note: SASSě—서는
>>>
ě—°ě‚°ěžę°€ ě śëŚ€ëˇś 작동í•ě§€ 않을 ě ěžěеë‹ë‹¤. 이때는/deep/
ë는::v-deep
ě„ íťěžëĄĽ ëŚ€ě‹ ěť´ěš©í•©ë‹ë‹¤. -
일ë°ě 으로 ěěś„ ě»´íŹ¬ë„ŚíŠ¸ěť ěŠ¤í€ěťĽěť€ í•ěś„ ě»´íŹ¬ë„ŚíŠ¸ě— ěí–Ąěť„ 미ěąě§€ 않습ë‹ë‹¤. í•지만 í•ěś„ ě»´íŹ¬ë„ŚíŠ¸ěť ëŁ¨íŠ¸ 노드는 ěěś„ 컴포넌트와 í•ěś„ ě»´íŹ¬ë„ŚíŠ¸ěť ěŠ¤í€ěťĽě— ëŞ¨ë‘ ěí–Ąěť„ 받습ë‹ë‹¤. 즉, í•ěś„ ě»´íŹ¬ë„ŚíŠ¸ěť ëŁ¨íŠ¸ ë…¸ë“śě— ěěś„ 컴포넌트ě—서 ě‚¬ěš©ëś í´ëžěФ氀 사용ëśë‹¤ë©´, ěěś„ ě»´íŹ¬ë„ŚíŠ¸ěť ěŠ¤í€ěťĽěť´ í•ěś„ 컴포넌트ě—도 ěí–Ąěť„ ëĽěą©ë‹ë‹¤. 이는 ěěś„ 컴포넌트ě—서 ë ěť´ě•„ě›ěť„ 위해 í•ěś„ ě»´íŹ¬ë„ŚíŠ¸ě— ěí–Ąěť„ ëŻ¸ěą ě ěžëŹ„ëˇť ë””ěžěť¸ëś ę˛ěž…ë‹ë‹¤. ě•„ëžěť ě시는 ěěś„ 컴포넌트ěť
background
ę°€ í•ěś„ 컴포넌트ě—까지 ěí–Ąěť„ 미ěąëŠ” ěě śěž…ë‹ë‹¤.// parent.vue <template> <div class="wrapper"> <p>parent</p> <ChildMessageComponent/> </div> </template> <script> import ChildMessageComponent from "./components/child"; export default { name: "App", components: { ChildMessageComponent } }; </script> <style scoped> .wrapper { background: blue; } </style>
//child.vue <template> <div class="wrapper"> <p>child</p> </div> </template> <script> export default { name: "Hello, Scoped CSS", }; </script> <style scoped> .wrapper { background: red; } </style>
í•ěś„ 컴포넌트ěť
wrapper
í´ëžěŠ¤ěť ë°°ę˛˝ě‰ěť€ 빨간ě‰ěť´ ě•„ë‹ëťĽ 파란ě‰ěť´ ë©ë‹ë‹¤. -
범위 CSS는
v-html
지시ěžëˇś 동ě 으로 ěťě„±ëś ë‚´ěš©ě— ěí–Ąěť„ 주지 않습ë‹ë‹¤. ěť´ 경우,deep
ě„ íťěžëĄĽ 통해 ë¬¸ě śëĄĽ í•´ę˛°í• ě ěžěеë‹ë‹¤. -
CSS 모ë“은 CSS를 모ë“í™”í•ęł ęµ¬ě„±í•는데 ë„리 사용ë는 시스템입ë‹ë‹¤.
vue-loader
는 시뮬ë ěť´íŠ¸ëś ë˛”ěś„ CSSěť ëŚ€ě•으로 CSS 모ë“과 í•¨ę» 1급 í´ëžěŠ¤ëˇśěť í†µí•©ěť„ ě śęłµí•©ë‹ë‹¤. -
ě• ë©ë‹ë‹¤. Vueě—서 사용ë는 템플릿은 ě¤ě§
.vue
파일ě—서만 사용ëë©°, 다른 경우라면render
함ěę°€ 필요합ë‹ë‹¤. -
- CSS ëŞ¨ë“ í™śě„±í™”:
webpack.config.js
ěťcss-loader
ě—서modules: true
ěµě…ěť„ 활성화해줍ë‹ë‹¤.
// webpack.config.js { module: { rules: [ // ... other rules omitted { test: /\.css$/, use: [ 'vue-style-loader', { loader: 'css-loader', options: { // enable CSS Modules modules: true, // customize generated class names localIdentName: '[local]_[hash:base64:8]' } } ] } ] } }
- ëŞ¨ë“ ě†Ťě„± 추가:
<style>
íśę·¸ě—module
속성을 추가합ë‹ë‹¤.
<style module> .customStyle { background: blue; } </style>
- CSS ëŞ¨ë“ ěŁĽěž…:
computed
속성인$style
ěť„ 통해 CSS 모ë“ěť„ 객체로 ě ‘ę·Ľí• ě ěžěеë‹ë‹¤.
<template> <div :class="$style.blue"> Background color should be in blue </p> </template>
:class
ěť ę°ťě˛´, ë°°ě—´ 문법ě—도 동작합ë‹ë‹¤. - CSS ëŞ¨ë“ í™śě„±í™”:
-
CSS 모ë“ěť„ ě „ě˛ë¦¬ę¸°ě—서 ě‚¬ěš©í• ě ěžěеë‹ë‹¤. ě를 들어, Sass ě „ě˛ë¦¬ę¸°ëŠ” 웹팩ě—서 ě•„ëžě™€ 같이 ě„¤ě •í• ě ěžěеë‹ë‹¤.
// webpack.config.js -> module.rules { test: /\.scss$/, use: [ 'vue-style-loader', { loader: 'css-loader', options: { modules: true } }, 'sass-loader' ] }
-
ěŁĽěž…ëś CSS 모ë“ě—
module
속성을 부여해서, 모ë“ěť ěť´ë¦„ěť„ 커스터ë§ěť´ě§•í• ě ěžěеë‹ë‹¤.*.vue
파일ě—서 ë‘ ěť´ěěť<style>
íśę·¸ę°€ ěˇ´ěž¬í• ë•Ś, 스í€ěťĽěť´ 서로 덮어쓰지 않게 í•는 데 ěś ěš©í•©ë‹ë‹¤. ě를 들어, ě•„ëžě™€ 같이 속성을 줄 ě ěžěеë‹ë‹¤.<style module="a"> /* identifiers injected as a */ </style> <style module="b"> /* identifiers injected as b */ </style>
-
핫 리로드는
*.vue
파일을 íŽ¸ě§‘í• ë•Ś 단ěśíž íŽěť´ě§€ëĄĽ 다시 로드í•는 ę˛ěť´ ě•„ë‹™ë‹ë‹¤. í•« 리로드 기능을 사용í•ë©´*.vue
파일을 íŽ¸ě§‘í• ë•Ś 해당 ě»´íŹ¬ë„ŚíŠ¸ěť ëŞ¨ë“ ěť¸ěŠ¤í„´ěŠ¤ę°€ íŽěť´ě§€ëĄĽ 리로딩í•ě§€ ě•Šęł ëł€ę˛˝ë©ë‹ë‹¤. 심지어 ě•±ěť í„재 ěíśě™€ ëł€ę˛˝ëś ě»´íŹ¬ë„ŚíŠ¸ëĄĽ 보존합ë‹ë‹¤. ěť´ę˛ěť€ 템플릿 ë는 ě»´íŹ¬ë„ŚíŠ¸ěť ěŠ¤í€ěťĽěť„ ěě •í• ë•Ś ę°śë°ś í™ę˛˝ěť´ í¬ę˛Ś ę°śě„ ë©ë‹ë‹¤. -
í•« 리로드는 ě•„ëžěť ě황ě—서는 비활성화 ëě–´ěžěеë‹ë‹¤.
- 웹팩ěť
target
ěť´node
일 때 (SSR) - 웹팩이 코드를 minifyí• ë•Ś
process.env.NODE_ENV === 'production'
일 때
- 웹팩ěť
-
hotReload: false
ěµě…ěť„ 웹팩 로더ě—서 ě„¤ě •í•ë©´ ë©ë‹ë‹¤.module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { hotReload: false // disables Hot Reload } } ] }
-
vue-loader
플러그인은 ë‚´ë¶€ě 으로 í•« 리로드를 사용í•ęł ěžěеë‹ë‹¤. ë§Śě•˝vue-cli
를 이용해 í”„ëˇśě ťíŠ¸ëĄĽ 시작í–다면, í•« 리로드를 바로 ě‚¬ěš©í• ě ěžěеë‹ë‹¤. ë§Śě•˝ í”„ëˇśě ťíŠ¸ëĄĽ ě§ě ‘ 세팅í–다면,webpack-dev-server --hot
ěµě…으로 í”„ëˇśě ťíŠ¸ëĄĽ 시작해 활성화 í• ě ěžěеë‹ë‹¤. -
- 컴포넌트ěť
<template>
ěť„ ěě •í• ë•Ś, ěě •ëś ě»´íŹ¬ë„ŚíŠ¸ëŠ” ëŞ¨ë“ ěíśëĄĽ 보존한 채로 다시 ë ŚëŤ”ë§ë©ë‹ë‹¤. - 컴포넌트ěť
<script>
를 ěě •í• ë•Ś, ěě •ëś ě»´íŹ¬ë„ŚíŠ¸ëŠ” 해체(destroy) ëś í›„ 다시 ěťě„±(re-create)ë©ë‹ë‹¤. - 컴포넌트ěť
<style>
ěť„ ěě •í• ë•Ś, í•« 리로드는vue-style-loader
ě— ěťí•´ 실행ëë©° ěíśě— ěí–Ąěť„ ëĽěąě§€ 않습ë‹ë‹¤.
- 컴포넌트ěť
-
functional
속성을 í…śí”Śë¦żě— ě¶”ę°€í•´ 함ěí• ě»´íŹ¬ë„ŚíŠ¸ëĄĽ ěťě„±í• ě ěžěеë‹ë‹¤.<template functional> <div>{{ props.msg }}</div> </template>
-
Vue.prototype
ě— ě „ě—으로 ě •ěťëś ě†Ťě„±ě— ě ‘ę·Ľí•´ě•Ľ 한다면,parent
속성을 이용해 ě ‘ę·Ľí• ě ěžěеë‹ë‹¤.<template functional> <div>{{ parent.$someProperty }}</div> </template>
-
- vue-cli: ěś ë‹› 테스트와 e2e 테스트 í™ę˛˝ěť´ 미리 ě„¤ě •ëě–´ ě śęłµë©ë‹ë‹¤.
- ě§ě ‘ 세팅:
@vue/test-utils
ě—서mocha-webpack
ěť´ë‚jest
를*.vue
파일을 대ě으로 ě§ě ‘ ě„¤ě •í•©ë‹ë‹¤.
-
Stylelint를 이용해 Vueěť ě‹±ę¸€ 파일 ě»´íŹ¬ë„ŚíŠ¸ěť ěŠ¤í€ěťĽ ë¶€ë¶„ěť Lint를 ě„¤ě •í• ě ěžěеë‹ë‹¤. íŠąě •
.vue
íŚŚěťĽěť Lint는 ě•„ëžě™€ 같이 실행합ë‹ë‹¤.stylelint MyComponent.vue
다른 방법은 웹팩ě—서
stylelint-webpack-plugin
를dev-dependency
로 설ěąí•ęł , 웹팩 ě„¤ě •ě—서 ě•„ëžě™€ 같이 ě„¤ě •í•는 방법입ë‹ë‹¤.// webpack.config.js const StyleLintPlugin = require('stylelint-webpack-plugin'); module.exports = { // ... other options plugins: [ new StyleLintPlugin({ files: ['**/*.{vue,htm,html,css,sss,less,scss,sass}'], }) ] }
-
공식
eslint-plugin-vue
플러그인은 Vueěť ě‹±ę¸€ 파일 ě»´íŹ¬ë„ŚíŠ¸ěť í…śí”Śë¦żęłĽ 스í¬ë¦˝íЏ ë¶€ë¶„ě— ëŚ€í•´ Lint를 ě śęłµí•©ë‹ë‹¤.// .eslintrc.js module.exports = { extends: [ "plugin:vue/essential" ] }
íŠąě • íŚŚěťĽě— ëŚ€í•ś Lint는 ě•„ëžě™€ 같이 ě‹¤í–‰í• ě ěžěеë‹ë‹¤.
eslint --ext js,vue MyComponent.vue
-
eslint-loader
를 ěť´ěš©í•ë©´ ę°śë°ś ëŹ„ě¤‘ě— ěžëŹ™ěśĽëˇś*.vue
íŚŚěťĽë“¤ě— ëŚ€í•´ Lint를 ě 용시킬 ě ěžěеë‹ë‹¤.ěš°ě„ ě•„ëžě™€ 같이 NPM 모ë“ěť„ 설ěąí•´ě•Ľ í•©ë‹ë‹¤.
npm install -D eslint eslint-loader
ę·¸ 후 ě›ąíŚ©ěť ě„¤ě •ě— ě¶”ę°€í•´ě•Ľ í•©ë‹ë‹¤.
// webpack.config.js module.exports = { // ... other options module: { rules: [ { enforce: 'pre', test: /\.(js|vue)$/, loader: 'eslint-loader', exclude: /node_modules/ } ] } }
-
CSS 단일 파일 추출(CSS Extraction)은 ëŞ¨ë“ Vue 컴포넌트ě—서 ě‚¬ěš©ëś CSS를 단일 CSS 파일로 추출í•는 ę˛ěť„ ěťëŻ¸í•©ë‹ë‹¤.
npm install -D mini-css-extract-plugin
ę·¸ 후 ě›ąíŚ©ěť ě„¤ě •ě— ě¶”ę°€í•´ě•Ľ í•©ë‹ë‹¤.
// webpack.config.js var MiniCssExtractPlugin = require('mini-css-extract-plugin') module.exports = { // other options... module: { rules: [ // ... other rules omitted { test: /\.css$/, use: [ process.env.NODE_ENV !== 'production' ? 'vue-style-loader' : MiniCssExtractPlugin.loader, 'css-loader' ] } ] }, plugins: [ // ... Vue Loader plugin omitted new MiniCssExtractPlugin({ filename: 'style.css' }) ] }
-
ě‚¬ěš©ěž ě •ěť ë¸”ëˇť(Custom block)은
*.vue
파일ě—서 ě‚¬ěš©í• ě ěžëŠ”<template>
,<script>
,<style>
íśę·¸ 블록 ěť´ě™¸ěť ë¸”ëˇťěť„ ě •ěťí•는 ę˛ěť„ ë§í•©ë‹ë‹¤.lang
속성, íśę·¸ 이름, 웹팩 ě„¤ě •ěťresourceQuery
ě†Ťě„±ě— ěťí•´ ě •ěťí• ě ěžěеë‹ë‹¤. ě•„ëž ě시는*.vue
파일ě—서<message>
로 ě •ěťëś íśę·¸ëĄĽ 찾는 방법입ë‹ë‹¤.{ module: { rules: [ { resourceQuery: /blockType=message/, loader: 'loader-to-use' } ] } }
-
Below are the list of major stylelint features
- It has more than 160 built-in rules to catch errors, apply limits and enforce stylistic conventions
- Understands latest CSS syntax including custom properties and level 4 selectors
- It extracts embedded styles from HTML, markdown and CSS-in-JS object & template literals
- Parses CSS-like syntaxes like SCSS, Sass, Less and SugarSS
- Supports Plugins for reusing community plugins and creating own plugins
-
Vuex enforces below rules to structure any application.
- Application-level state is centralized in the store.
- The only way to mutate the state is by committing mutations, which are synchronous transactions.
- Asynchronous logic should be encapsulated in, and can be composed with actions. The project structure for any non-trivial application would be as below,
-
Yes, Vuex supports hot-reloading for mutations, modules, actions and getters during development. You need to use either webpack's hot module replacement API or browserify's hot module replacement plugin.
-
The store.hotUpdate() API method is used for mutations and modules. For example, you need to configure vuex store as below,
// store.js import Vue from 'vue' import Vuex from 'vuex' import mutations from './mutations' import myModule from './modules/myModule' Vue.use(Vuex) const state = { message: "Welcome to hot reloading" } const store = new Vuex.Store({ state, mutations, modules: { moduleA: myModule } }) if (module.hot) { // accept actions and mutations as hot modules module.hot.accept(['./mutations', './modules/newMyModule'], () => { // Get the updated modules const newMutations = require('./mutations').default const newMyModule = require('./modules/myModule').default //swap in the new modules and mutations store.hotUpdate({ mutations: newMutations, modules: { moduleA: newMyModule } }) }) }
-
Since mutations are just functions that completely rely on their arguments it will be easier to test. You need to keep mutations inside your store.js file and should also export the mutations as a named export apart from default export. Let's take an example of increment mutations,
// mutations.js export const mutations = { increment: state => state.counter++ }
And test them using mocha and chai as below,
// mutations.spec.js import { expect } from 'chai' import { mutations } from './store' // destructure assign `mutations` const { increment } = mutations describe('mutations', () => { it('INCREMENT', () => { // mock state const state = { counter: 10 } // apply mutation increment(state) // assert result expect(state.counter).to.equal(11) }) })
-
It is easier to test getters similar to mutations. It is recommended to test these getters if they have complicated computation. Let's take a simple todo filter as a getter
// getters.js export const getters = { filterTodos (state, status) { return state.todos.filter(todo => { return todo.status === status }) } }
And the test case for above getter as follows,
// getters.spec.js import { expect } from 'chai' import { getters } from './getters' describe('getters', () => { it('filteredTodos', () => { // mock state const state = { todos: [ { id: 1, title: 'design', status: 'Completed' }, { id: 2, title: 'testing', status: 'InProgress' }, { id: 3, title: 'development', status: 'Completed' } ] } // mock getter const filterStatus = 'Completed' // get the result from the getter const result = getters.filterTodos(state, filterStatus) // assert the result expect(result).to.deep.equal([ { id: 1, title: 'design', status: 'Completed' }, { id: 2, title: 'development', status: 'Completed' } ]) }) })
-
By proper mocking, you can bundle tests with webpack and run them on node without having depenceny on Browser API. It involves 2 steps,
- Create webpack config: Create webpack config with proper .babelrc
// webpack.config.js module.exports = { entry: './test.js', output: { path: __dirname, filename: 'test-bundle.js' }, module: { loaders: [ { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ } ] } }
- ** Run testcases:** First you need to bundle and then run them using mocha as below,
webpack mocha test-bundle.js
-
Below are the steps to run tests in real browser,
- Install
mocha-loader
. - Configure webpack config entry point to 'mocha-loader!babel-loader!./test.js'.
- Start webpack-dev-server using the config.
- Go to localhost:8080/webpack-dev-server/test-bundle to see the test result
- Install
-
In strict mode, whenever Vuex state is mutated outside of mutation handlers, an error will be thrown. It make sure that all state mutations can be explicitly tracked by debugging tools. You can just enable this by passing
strict: true
while creating the vuex store.const store = new Vuex.Store({ // ... strict: true })
-
No, it is not recommended to use strict mode in production environment. Strict mode runs a synchronous deep watcher on the state tree for detecting inappropriate mutations and it can be quite expensive when you perform large amount of mutations. i.e, It can impact performance if you enable in production mode. Hence it should be handled through build tools,
const store = new Vuex.Store({ // ... strict: process.env.NODE_ENV !== 'production' })
-
The vuex plugin is an option hat exposes hooks for each mutation. It is a normal function that receives the store as the only argument. You can create your own plugin or use built-in plugins. The plugin skeleton would be as below,
const myPlugin = store => { // called when the store is initialized store.subscribe((mutation, state) => { // called after every mutation. // The mutation comes in the format of `{ type, payload }`. }) }
After that plugin can be configured for plugins options as below,
const store = new Vuex.Store({ // ... plugins: [myPlugin] })
-
Similar to components you can't mutate state directly but they can trigger changes by by committing mutations. This way a plugin can be used to sync a data source to the store. For example, createWebSocketPlugin plugin is used to sync a websocket data source to the store.
export default function createWebSocketPlugin (socket) { return store => { socket.on('data', data => { store.commit('receiveData', data) }) store.subscribe(mutation => { if (mutation.type === 'UPDATE_DATA') { socket.emit('update', mutation.payload) } }) } }
And then configure plugin in vuex store as below
const plugin = createWebSocketPlugin(socket) const store = new Vuex.Store({ state, mutations, plugins: [plugin] })
-
A Vuex "store" is basically a container that holds your application state. The store creation is pretty straightforward. Below are the list of instructions to use vuex in an increment application,
- Configure vuex in vuejs ecosystem
import Vuex from "vuex"; Vue.use(Vuex)
- Provide an initial state object and some mutations
// Make sure to call Vue.use(Vuex) first if using a module system const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } } })
- Trigger state change with commit and access state variables,
store.commit('increment') console.log(store.state.count) // -> 1
-
Below are the two major differences between vuex store and plain global object
- Vuex stores are reactive: If the store's state changes then vue components will reactively and efficiently get updated
- Cannot directly mutate the store's state: The store's state is changed by explicitly committing mutations to ensure that every state change leaves a track-able record for tooling purpose
-
We want to explicitly track application state in order to implement tools that can log every mutation, take state snapshots, or even perform time travel debugging. So we need to commit a mutation instead of changing store's state directly.
-
Vuex's single state tree is single object contains all your application level state and serves as the "single source of truth". It does not conflict with modularity when you split state and mutations into sub modules.
-
You can install vuex using npm or yarn as below,
npm install vuex --save (or) yarn add vuex
In a module system, you must explicitly install Vuex via Vue.use()
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex)
(OR) You can also install it using CDN links such as unpkg.cpm which provides NPM-based CDN links. Just include vuex after Vue and it will install itself automatically.
<script src="https://unpkg.com/vue.js"></script> <script src="https://unpkg.com/vuex.js"></script>
Note: You can use a specific version/tag via URLs like https://unpkg.com/vuex@2.0.0. If you don't mention any version then it will point to latest version.
-
Yes, Vuex requires Promise. If your supporting browsers do not implement Promise (e.g. IE), you can use a polyfill library, such as es6-promise using npm or yarn.
npm install es6-promise --save # NPM yarn add es6-promise # Yarn
After that import into anywhere in your application,
import 'es6-promise/auto'
-
Since Vuex stores are reactive, you can retrieve" state from store by simply returning store's state from within a computed property. i.e, Whenever store state changes, it will cause the computed property to re-evaluate, and trigger associated DOM updates. Let's take a hello word component which display store's state in the template,
// let's create a hello world component const Greeting = { template: `<div>{{ greet }}</div>`, computed: { greet () { return store.state.msg } } }
-
Vuex provides a mechanism to "inject" the store into all child components from the root component with the store option. It will be enabled by vue.use(vuex). For example, let's inject into our app component as below,
const app = new Vue({ el: '#app', // provide the store using the "store" option. // this will inject the store instance to all child components. store, components: { Greeting }, template: ` <div class="app"> <greeting></greeting> </div> ` })
Now the store will be injected into all child components of the root and will be available on them as this.$store
// let's create a hello world component const Greeting = { template: `<div>{{ greet }}</div>`, computed: { greet () { return this.$store.state.msg } } }
-
In Vuex application, creating a computed property every time whenever we want to access the store's state property or getter is going to be repetitive and verbose, especially if a component needs more than one state property. In this case, we can make use of the mapState helper of vuex which generates computed getter functions for us. Let's take an increment example to demonstrate mapState helper,
// in full builds helpers are exposed as Vuex.mapState import { mapState } from 'vuex' export default { // ... computed: mapState({ // arrow functions can make the code very succinct! username: state => state.username, // passing the string value 'username' is same as `state => state.username` usernameAlias: 'username', // to access local state with `this`, a normal function must be used greeting (state) { return this.localTitle + state.username } }) }
We can also pass a string array to mapState when the name of a mapped computed property is the same as a state sub tree name
computed: mapState([ // map this.username to store.state.username 'username' ])
-
You can use object spread operator syntax in order to combine mapState helper(which returns an object) with other local computed properties. This way it simplify merging techniques using utilities.
computed: { localComputed () { /* ... */ }, // mix this into the outer object with the object spread operator ...mapState({ // ... }) }
-
No, if a piece of state strictly belongs to a single component, it could be just fine leaving it as local state. i.e, Eventhough vuex used in the application, it doesn't mean that you need to keep all the local state in vuex store. Other the code becomes more verbose and indirect although it makes your state mutations more explicit and debuggable.
-
Vuex getters acts as computed properties for stores to compute derived state based on store state. Similar to computed properties, a getter's result is cached based on its dependencies, and will only re-evaluate when some of its dependencies have changed. Let's take a todo example which as completedTodos getter to find all completed todos,
const store = new Vuex.Store({ state: { todos: [ { id: 1, text: 'Vue course', completed: true }, { id: 2, text: 'Vuex course', completed: false }, { id: 2, text: 'Vue Router course', completed: true } ] }, getters: { completedTodos: state => { return state.todos.filter(todo => todo.completed) } } })
**Note:**Getters receive state as first argument.
-
You can access values of store's getter object(store.getters) as properties. This is known as property style access. For example, you can access todo's status as a property,
store.getters.todosStatus
The getters can be passed as 2nd argument for other getters. For example, you can derive completed todo's count based on their status as below,
getters: { completedTodosCount: (state, getters) => { return getters.todosStatus === 'completed' } }
Note: The getters accessed as properties are cached as part of Vue's reactivity system.
-
You can access store's state in a method style by passing arguments. For example, you can pass user id to find user profile information as below,
getters: { getUserProfileById: (state) => (id) => { return state.users.find(user => user.id === id) } }
After that you can access it as a method call,
store.getters.getUserProfileById(111); {id: '111', name: 'John', age: 33}
-
The mapGetters is a helper that simply maps store getters to local computed properties. For example, the usage of getters for todo app would be as below,
import { mapGetters } from 'vuex' export default { computed: { // mix the getters into computed with object spread operator ...mapGetters([ 'completedTodos', 'todosCount', // ... ]) } }
-
Vuex mutations are similar to any events with a string
type
and ahandler
. The handler function is where we perform actual state modifications, and it will receive the state as the first argument. For example, the counter example with increment mutation would be as below,const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { // mutate state state.count++ } } })
You can't directly invoke mutation instead you need to call
store.commit
with its type. The above mutation would be triggered as folowsstore.commit('increment')
-
You can also pass payload for the mutation as an additional argument to
store.commit
. For example, the counter mutation with payload object would be as below,mutations: { increment (state, payload) { state.count += payload.increment } }
And then you can trigger increment commit
store.commit('increment', { increment: 20 })
Note: You can also pass primitives as payload.
-
You can also commit a mutation is by directly using an object that has a type property.
store.commit({ type: 'increment', value: 20 })
Now the entire object will be passed as the payload to mutation handlers(i.e, without any changes to handler signature).
mutations: { increment (state, payload) { state.count += payload.value } }
-
Since a Vuex store's state is made reactive by Vue, the same reactivity caveats of vue will apply to vuex mutations. These are the rules should be followed for vuex mutations,
- It is recommended to initialize store's initial state with all desired fields upfront
- Add new properties to state Object either by set method or object spread syntax
Vue.set(stateObject, 'newProperty', 'John')
(OR)
state.stateObject = { ...state.stateObject, newProperty: 'John' }
-
You need to remember that mutation handler functions must be synchronous. This is why because any state mutation performed in the callback is essentially un-trackable. It is going to be problematic when the devtool will need to capture a "before" and "after" snapshots of the state during the mutations.
mutations: { someMutation (state) { api.callAsyncMethod(() => { state.count++ }) } }
-
You can commit mutations in components with either this.$store.commit('mutation name') or mapMutations helper to map component methods to store.commit calls. For example, the usage of mapMutations helper on counter example would be as below,
import { mapMutations } from 'vuex' export default { methods: { ...mapMutations([ 'increment', // map `this.increment()` to `this.$store.commit('increment')` // `mapMutations` also supports payloads: 'incrementBy' // map `this.incrementBy(amount)` to `this.$store.commit('incrementBy', amount)` ]), ...mapMutations({ add: 'increment' // map `this.add()` to `this.$store.commit('increment')` }) } }
-
No, it is not mandatory. But you might observed that State management implementations such Flux and Redux use constants for mutation types. This convention is just a preference and useful to take advantage of tooling like linters, and putting all constants in a single file allows your collaborators to get an at-a-glance view of what mutations are possible in the entire application. For example, the mutations can be declared as below,
// mutation-types.js export const SOME_MUTATION = 'SOME_MUTATION'
And you can configure them in store as follows,
// store.js import Vuex from 'vuex' import { SOME_MUTATION } from './mutation-types' const store = new Vuex.Store({ state: { ... }, mutations: { // ES2015 computed property name feature to use a constant as the function name [SOME_MUTATION] (state) { // mutate state } } })
-
In Vuex, mutations are synchronous transactions. But if you want to handle asynchronous operations then you should use actions.
-
Actions are similar to mutations, but there are two main differences,
- Mutations perform mutations on the state, actions commit mutations.
- Actions can contain arbitrary asynchronous operations unlike mutations.