목차
- 기본 개념
- Vue 인스턴스
- 컴포넌트
- Vue 인스턴스 라이프 사이클
- 지시자
- 필터
- 믹스인
- 플러그인
- 반응형 시스템 동작 방식
- 내부 구현
- 로직과 설계
기본 개념
: 작은 규모에서 대규모로 성장시킬 수 있는 점진적인 프레임워크 Vue.js
- Vue 인스턴스
- 컴포넌트
- Vue 인스턴스 라이프 사이클
- 지시자
- 필터
- 믹스인
- 플러그인
싱글 페이지 애플리케이션 SPA인 메시지 앱
- 메시지 추가하기
- 메시지 리스트 보기
- 메시지 삭제하기
- 특정 조건에서 추가 기능을 자동으로 비활성화하기
목차
- Vue인스턴스를 이용해 index.html 생성
- 컴포넌트를 이용해 메시지 리스트 렌더링과 메시지 삭제 구현
- 지시자를 이용해 페이지 열린 뒤 폼의 입력 항목에 초점
- 필터를 이용해 메시지 앱에 보기 좋은 날짜와 시간 형식 제공
- 훅 함수에서 인스턴스 이름과 단계를 출력하기 위해 믹스인 이용
- 플러그인을 이용해 인스턴스의 라이프 사이클 추적
Vue 인스턴스
- 루트 Vue 인스턴스
- 컴포넌트 인스턴스
루트 인스턴스 생성
: Vue 함수를 이용
new Vue({/* options */});
-options 객체에 애플리케이션 기술해 이 객체를 이용해 Vue 인스턴스 초기화
(1) index.html 작성
<!DOCTYPE html>
<html>
<head>
<title>Messages App</title>
</head>
<body>
<div id="app" v-cloak>
<script src="https://unpkg.com/vue@2.5.13/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
});
</script>
</body>
</html>
(2) 데이터 모델 정의
-객체 리터럴을 사용해 data프로퍼티 값으로 일반객체 객체 이용
-message는 배열, newMessagesms는 문자열로 초깃값 지정
-초깃값을 지정해주면, 프로퍼티를 반응형으로 만들어준다.
<script>
let vm = new Vue({
el: '#app',
data: {
messages: [],
newMessage: ''
}
});
</script>
일반 객체를 반환하는 함수를 이용할 수도 있다.
data(){
return{
messages:[],
newMessage:''
}
}
Vue.js가 함수를 이용해 새로운 컴포넌트에 대한 새로운 데이터 모델을 생성하므로 데이터 구조 정의시 함수를 사용해야만 한다.
-일반 객체를 사용하면 컴포넌트의 모든 인스턴스가 같은 data 객체를 공유하므로, 루트 Vue 인스턴스 이외에는 일반 객체를 사용하면 안된다.
(3) data 객체로 메시지를 표시하고 추가하는 템플릿 추가
템플릿 추가 방법
- options 객체의 template 프로퍼티를 활용해 인라인 템플릿 문자열 추가
- 템플릿을 부착 지점인 <div><id="app"></div>에 직접 삽입
- 템플릿 마크업을 <script type="x-template" id="tmplApp"> 태크 내에 넣어 options객체의 template 프로퍼티 값으로 "#tmplapp"을 넣는다.
템플릿 직접 삽입 순서
- Vue에 내장된 v-for 지시자로 메시지 리스트 렌더링
v-for : <별칭> in <원본데이터>
li 태그에 v-for 지시자를 추가해 별칭을 사용 - 이중 중괄호 구문을 이용해 리스트 내 객체의 text 프로퍼티와 createdAt 프로퍼티 출력
이중 중괄호를 사용하면 출력결과와 해당 데이터 사이에 데이터 바인딩 생성
: 태그를 실제 값으로 대체할 수 있으며 자바스크립트 표현식도 사용 가능 : {{message.text.toLowerCase()}} - Vue의 지시자인 v-on을 이용해 submit 이벤트에 이벤트 리스너 부착
prevent : 실제로 폼을 제출하지 않도록 event.preventDefault()호출 지시
v-on을 이용해 DOM 이벤트에 리스너를 부착하고, Vue 사용자 정의 컴포넌트의 사용자 정의 이벤트 수신 - Vue의 지시자인 v-model을 이용해 textarea요소와 newMessage 프로퍼티 사이에 양방향 바인딩
textarea 요소 값이 변경될 때마다 newMessage 자동으로 업데이트
newMessage 요소 값이 변경될 때마다 textarea 자동으로 업데이트 - 폼의 submit 이벤트 트리거 할 수 있도록 버튼추가
<div id="app">
<ul>
<li v-for="message in messages">
{{ message.text }} - {{ message.createdAt }}
</li>
</ul>
<form v-on:submit.prevent="addMessage">
<textarea v-model="newMessage" placeholder="Leave a message">
</textarea>
<div><button type="submit">Add</button></div>
</form>
</div>
(4) 양방향 바인딩
-이벤트를 수신하는 addMessage 메소드를 options 객체의 methods 프로퍼티로 메소드 생성
<script src="https://unpkg.com/vue@2.5.13/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
messages: [],
newMessage: ''
},
computed: {
addDisabled () {
return this.messages.length >= 10 || this.newMessage.length > 50
}
},
methods: {
addMessage (event) {
if (!this.newMessage) return
//배열에 새로운 메시지 추가 후, newMessage 프로퍼티 바인딩
this.messages.push({text: this.newMessage, createdAt: new Date()})
//newMessage 프로퍼티 초기화
this.newMessage = ''
},
deleteMessage (message) {
this.messages.splice(this.messages.indexOf(message), 1)
}
}
})
</script>
메소드 만들 때 화살표 함수는 사용 금지 -> this로 Vue 인스턴스에 접근 불가능하므로
(5) UI에서 메시지 삭제 방법 추가
<li v-for="message in messages">
{{ message.text }} - {{ message.createdAt }}
<button @click="deleteMessage(message)">X</button>
</li>
(6) 버튼을 추가하고 이벤트 리스너에 메소드 연결
-메소드 이름 대신 인라인 구문 사용
<script>
let vm = new Vue({
el: '#app',
data: {
messages: [],
newMessage: ''
},
computed: {
addDisabled () {
return this.messages.length >= 10 || this.newMessage.length > 50
}
},
methods: {
addMessage (event) {
if (!this.newMessage) return
this.messages.push({text: this.newMessage, createdAt: new Date()})
this.newMessage = ''
},
deleteMessage (message) {
this.messages.splice(this.messages.indexOf(message), 1)
}
}
})
</script>
-Array.prototype.splice() 메소드가 messages 배열에서 선택된 메시지를 삭제한다.
-Vue.js를 이용하면 변경 사항을 감지해 DOM을 자동으로 업데이트한다.
(7) 메시지 추가하기 기능을 자동으로 비활성화하는 기능 추가
-리스트에 10개 메시지가 있으면 Add 버튼 비활성화
: v-bind 지시자를 이용 add 버튼의 disabled 속성과 message.length 표현식 연결해 배열의 길이 변경시 diabled 속성 자동 업데이트
<form @submit.prevent="addMessage">
<textarea v-model="newMessage" placeholder="Leave a message">
</textarea>
<div><button v-bind:disabled="messages.length >= 10" type="submit">Add</button></div>
</form>
(8) 텍스트 길이가 50자 초과시 Add 버튼 비활성화
: v-bind 지시자를 이용 newMessage.length>50으로 변경
-> 이전 값에 추가해 messages.length >= 10 || newMessage.length>50로 변경하는데 유지 관리를 위해 computed 프로퍼티 이용
-> 종속된 대상을 추적하고 변경시 업데이트한다.
-> v-bind를 줄여 ':'로 표현 가능
<div><button :disabled="addDisabled" type="submit">Add</button></div>
<script>
let vm = new Vue({
el: '#app',
data: {
messages: [],
newMessage: ''
},
computed: {
addDisabled () {
return this.messages.length >= 10 || this.newMessage.length > 50
}
}
})
</script>
(9) v-bind 지시자로 HTML 요소의 내장된 속성 연결하고 템플릿 부착 지점에 v-cloak 지시자 추가
-Vue의 사용자 정의 컴포넌트의 프로퍼티 연결 가능
-v-cloak 지시자 추가 후, 템플릿 마크업을 숨기는 CSS 규칙 추가
-> DOM 준비 완료시 v-cloak 지시자 제거
<style>
[v-cloak] {display: none;}
body > div {width: 500px; margin: 0 auto;}
textarea {width: 100%;}
ul {padding: 0 15px;}
</style>
<div id="app" v-cloak>
<ul>
<li v-for="message in messages">
{{ message.text }} - {{ message.createdAt }}
<button @click="deleteMessage(message)">X</button>
</li>
</ul>
<form @submit.prevent="addMessage">
<textarea v-model="newMessage" placeholder="Leave a message">
</textarea>
<div><button v-bind:disabled="addDisabled" type="submit">Add</button></div>
</form>
</div>
# Vue 인스턴스의 options 객체의 data 객체, computed 객체, methods 객체 활용 방법과 객체의 프로퍼티가 따로 분리 정의된 상태에서 this로 접근 가능
<!DOCTYPE html>
<html>
<head>
<title>Messages App</title>
<style>
[v-cloak] {display: none;}
body > div {width: 500px; margin: 0 auto;}
textarea {width: 100%;}
ul {padding: 0 15px;}
</style>
</head>
<body>
<div id="app" v-cloak>
<ul>
<li v-for="message in messages">
{{ message.text }} - {{ message.createdAt }}
<button @click="deleteMessage(message)">X</button>
</li>
</ul>
<form @submit.prevent="addMessage">
<textarea v-model="newMessage" placeholder="Leave a message">
</textarea>
<div><button v-bind:disabled="addDisabled" type="submit">Add</button></div>
</form>
</div>
<script src="https://unpkg.com/vue@2.5.13/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
messages: [],
newMessage: ''
},
computed: {
addDisabled () {
return this.messages.length >= 10 || this.newMessage.length > 50
}
},
methods: {
addMessage (event) {
if (!this.newMessage) return
this.messages.push({text: this.newMessage, createdAt: new Date()})
this.newMessage = ''
},
deleteMessage (message) {
this.messages.splice(this.messages.indexOf(message), 1)
}
}
})
</script>
</body>
</html>
컴포넌트
HTML 요소를 확장하고 추가 로직을 제공하며 해당 코드를 재사용하는 방법
-Vue 컴포넌트는 생성 중에 Vue 인스턴스와 같은 options객체를 받는다.
컴포넌트전역 등록
Vue.component (id, [definition])
첫 번째 인자는 템플릿에서 사용할 태그이름으로 컴포넌트의 id
두 번째 인자는 컴포넌트의 정의로 options 객체이거나 options 객체를 반환하는 함수
메시지 리스트 렌더링 순서
- 컴포넌트를 MessageList로, 태그이름을 message-list로 정의
- 템플릿 확장
- Delete 버튼 클릭 반응 추가
1. MessageList 컴포넌트가 data 객체 messages 프로퍼티에 접근 설정
: MessageList 컴포넌트에 items 프로퍼티 추가하고 v-bind 지시자로 messages 데이터와 연결
-Vue.js는 컴포넌트 자체 스코프를 가지므로 자식 컴포넌트에서 부모 데이터 직접 참조 불가능
-Vue.js가 제공하는 options 객체의 props 프로퍼티를 이용해 컴포넌트에 전달할 수 있는 데이터 정의
-props는 배열 또는 객체를 값으로 가진다.
2. 메시지 삭제 설정
: 렌더링 로직과 삭제 로직을 MessageList 컴포넌트에 있지만, <form>은 컴포넌트에 추가되지 않는다.
-하나의 컴포넌트는 하나의 책임에 집중해야하므로 MessageList 컴포넌트 내에서 Delete 버튼 클릭에 대한 메시지로 부모 컴포넌트와 통신하는 방법 사용
-Vue.js에서는 사용자 정의 이벤트를 이용해 부모 컴포넌트와 통신
현재 인스턴스의 이벤트를 트리거하는 $emit() 메소드를 이용해 첫 번째 인자로 이벤트명 두 번째 인자로 추가 데이터를 전달해
: 부모 컴포넌트가 v-on지시자를 이용해 이벤트를 리스너에 부착해 Delete 버튼 클릭시 MessageList가 delete 이벤트를 트리거하고 삭제할 메시지 전달
(1) index.html 수정해 컴포넌트 활용
<!DOCTYPE html>
<html>
<head>
<title>Messages App</title>
<style>
[v-cloak] {display: none;}
body > div {width: 500px; margin: 0 auto;}
textarea {width: 100%;}
ul {padding: 0 15px;}
</style>
</head>
<body>
<div id="app" v-cloak>
<message-list :items="messages" @delete="deleteMessage"></message-list>
<form @submit.prevent="addMessage">
<textarea v-model="newMessage" placeholder="Leave a message"></textarea>
<div><button :disabled="addDisabled" type="submit">Add</button></div>
</form>
</div>
<script src="https://unpkg.com/vue@2.5.13/dist/vue.js"></script>
<script type="module">
import MessageList from './components/MessageList.js'
let vm = new Vue({
el: '#app',
data: {
messages: [],
newMessage: ''
},
computed: {
addDisabled () {
return this.messages.length >= 10 || this.newMessage.length > 50
}
},
components: {
MessageList
},
methods: {
addMessage (event) {
if (!this.newMessage) return
let now = new Date()
this.messages.push({text: this.newMessage, createdAt: now, id: now.getTime()})
this.newMessage = ''
},
deleteMessage (message) {
this.messages.splice(this.messages.indexOf(message), 1)
}
}
})
</script>
</body>
</html>
# v-bind 줄임말 ':', v-on 줄임말 '@', 사용자 정의 컴포넌트와 네이티브 HTML 요소를 같은 방식으로 데이터 바인딩하고 이벤트 리스너 부착
(2) MessageList 컴포넌트 생성
: Vue.js에서 컴포넌트 먼저 등록해 부모 컴포넌트에서 사용하도록 배치하고 컴포넌트 필요한 것들을 별도의 파일로 저장해 재사용
<script type="module">
import MessageList from './components/MessageList.js'
let vm = new Vue({
el: '#app',
data: {
messages: [],
newMessage: ''
},
computed: {
addDisabled () {
return this.messages.length >= 10 || this.newMessage.length > 50
}
},
components: {
MessageList
}
});
</script>
(3) components/MessageList.js 작성
export default {
name: 'MessageList',
template: `<ul>
<li v-for="item in items" :item="item">
{{ item.text }} - {{ item.createdAt }}
<button @click="deleteMessage(item)">X</button></li></ul>`,
props: {
items: {
type: Array,
required: true
}
},
methods: {
deleteMessage (message) {
this.$emit('delete', message)
}
}
}
(4) 루트 Vue 인스턴스에 MessageList 등록
-ES6 모듈로 작성한 MessageList을 사용하기 위해 모듈 타입 추가
-컴포넌트에 사용할 MessageList:MessageList 등록
-Vue.js는 프로퍼티 이름을 파스칼 표기법에서 케밥 표기법으로 변환하고 컴포넌트 이름을 ID로 활용하므로, 템플릿에서 컴포넌트를 <message-list>로 사용 가능
-컴포넌트 이름 규칙 : 부모 컴포넌트와 밀접하게 연결된 자식 컴포넌트는 부모 컴포넌트의 이름을 접두사로 포함
components/MessageListItem.js
export default {
name: 'MessageListItem',
template: `<li>{{ item.text }} - {{ item.createdAt }}
<button @click="deleteClicked">X</button></li>`,
props: {
item: {
type: Object,
required: true
}
},
methods: {
deleteClicked () {
this.$emit('delete')
}
}
}
# item 프로퍼티로 부모 컴포넌트의 데이터를 받을 수 있고, Delete 버튼을 클릭해 delete 이벤트 트리거 가능
components/MessageList
import MessageListItem from './MessageListItem.js'
export default {
name: 'MessageList',
template: `<ul><message-list-item v-for="item in items"
:item="item" :key="item.id"@delete="deleteMessage(item)">
</message-list-item></ul>`,
props: {
items: {
type: Array,
required: true
}
},
components: {
MessageListItem
},
methods: {
deleteMessage (message) {
this.$emit('delete', message)
}
}
}
Vue 인스턴스 라이프 사이클
: 라이프 사이클 동안 각 단계별 로직을 정의하는 기능 제공
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- activated
- deactivated
- beforeDestroy
- destroyed
- errorCaptured
지시자
: Vue 애플리케이션에서 표현식의 값 변경시 DOM에 변경 사항 적용
- v-for 지시자
- 원본 데이터를 기반으로 요소 또는 데이터 블록을 여러 번 레더링
- v-on 지시자
- 리스너를 DOM 요소에 부착
- 사용자 정의 지시자
- 지시자 정의 객체 생성 후, Vue.directive()를 이용해 전역으로 등록
- 지시자 정의 객체 생성 후, 컴포넌트의 directvie 프로퍼티를 이용해 해당 컴포넌트에 로컬로 등록
Vue.js가 지시자 외부에서 지시자에 전달하는 훅 함수를 지시자 정의 객체 내에 추가해 다양한 로직 적용
- bind
- inserted
- update
- componentUpdated
- unbind
v-focus 사용자 정의 지시자 예제
directives/focus.directive.js
// https://vuejs.org/v2/guide/custom-directive.html
// `v-focus`라는 전역 사용자 정의 지시자 등록하기
Vue.directive('focus', {
//바인딩된 요소가 DOM으로 삽입될 때
inserted: function (el) {
// 요소에 초점을 둔다.
el.focus()
}
})
index.html
<!DOCTYPE html>
<html>
<head>
<title>Messages App</title>
<style>
[v-cloak] {display: none;}
body > div {width: 500px; margin: 0 auto;}
textarea {width: 100%;}
ul {padding: 0 15px;}
</style>
</head>
<body>
<div id="app" v-cloak>
<message-list :items="messages" @delete="deleteMessage"></message-list>
<form @submit.prevent="addMessage">
<textarea v-model="newMessage" placeholder="Leave a message" v-focus></textarea>
<div><button :disabled="addDisabled" type="submit">Add</button></div>
</form>
</div>
<script src="https://unpkg.com/vue@2.5.13/dist/vue.js"></script>
<script type="module">
import MessageList from './components/MessageList.js'
import lifecyleLogger from './mixins/lifecycle-logger.mixin.js'
import './directives/focus.directive.js'
import './filters/datetime.filter.js'
let vm = new Vue({
el: '#app',
name: 'MessagesApp',
mixins: [lifecyleLogger],
data: {
messages: [],
newMessage: ''
},
computed: {
addDisabled () {
return this.messages.length >= 10 || this.newMessage.length > 50
}
},
components: {
MessageList
},
methods: {
addMessage (event) {
if (!this.newMessage) return
let now = new Date()
this.messages.push({id: now.getTime(), text: this.newMessage, createdAt: now})
this.newMessage = ''
},
deleteMessage (message) {
this.messages.splice(this.messages.indexOf(message), 1)
}
}
})
</script>
</body>
</html>
<form @submit.prevent="addMessage">
<textarea v-model="newMessage" placeholder="Leave a message" v-focus></textarea>
<div><button :disabled="addDisabled" type="submit">Add</button></div>
</form>
#지시자 파일을 ${directiveName}.directive.js형식으로 지정해 편집기에서 열 때 접미사로부터 지시자임을 알 수 있다.
필터
: Vue 애플리케이션에서 필터를 이용해 이중 중괄호 보간법 혹은 v-bind 표현법을 이용해 텍스트 형식을 지정
-필터는 표현식의 값을 첫 번째 인자를 가지는 자바스크립트 함수
-Vue.js는 필터를 등록하는 두 가지 방법
- Vue.filter()로 전역으로 등록
- 컴포넌트의 options 객체의 filters 프로퍼티를 로컬로 등록
createdAt 프로퍼티에 datetime 전역 필터 작성해 규약에 따라 파일명을 지시자 이름과 유사하게 명명
${filterName}.filter.js
filters/datetime.filter.js
//날짜와 시간 형식을 다루기 위해 Intl.DateTimeFormat 이용
const formatter = new Intl.DateTimeFormat('en-US', {
year: 'numeric', month: 'long', week: 'long', day: 'numeric',
hour: 'numeric', minute: 'numeric', second: 'numeric'
})
Vue.filter('datetime', function(value) {
if (!value) return ''
return formatter.format(value)
})
필터 적용
(1) index.html
<script type="module">
import MessageList from './components/MessageList.js'
import lifecyleLogger from './mixins/lifecycle-logger.mixin.js'
import './directives/focus.directive.js'
import './filters/datetime.filter.js'
let vm = new Vue({
el: '#app',
name: 'MessagesApp',
mixins: [lifecyleLogger],
data: {
messages: [],
newMessage: ''
},
computed: {
addDisabled () {
return this.messages.length >= 10 || this.newMessage.length > 50
}
},
components: {
MessageList
},
methods: {
addMessage (event) {
if (!this.newMessage) return
let now = new Date()
this.messages.push({id: now.getTime(), text: this.newMessage, createdAt: now})
this.newMessage = ''
},
deleteMessage (message) {
this.messages.splice(this.messages.indexOf(message), 1)
}
}
})
</script>
(2) item.createdAt에 {{itemcreatedAt | datetime}} 필터 추가
import lifecyleLogger from '../mixins/lifecycle-logger.mixin.js'
export default {
name: 'MessageListItem',
mixins: [lifecyleLogger],
template: `<li>{{ item.text }} - {{ item.createdAt | datetime }}
<button @click="deleteClicked">X</button></li>`,
props: {
item: {
type: Object,
required: true
}
},
methods: {
deleteClicked () {
this.$emit('delete')
}
}
}
(3) date-fns 라이브러리 활용해 필터에 추가 인자 제공해 원하는 패턴 전달
: filter함수에 두 번째 매개변수 추가
Vue.filter('datetime', function(value, pattern){
...
});
필터 사용시 패턴 지정 방법
{{ item.cratedAt | datetime('MM/DD/YYYY') }}
믹스인
: 코드를 재사용할 수 있는 또 다른 방법으로 모든 컴포넌트의 옵션을 포함 가능한 자바스크립트 객체
-Vue 컴포넌트의 options 객체에 믹스인을 혼합해 여러 컴포넌트에서 활용 가능
-로컬이나 전역으로 사용가능한데, 전역으로 사용시 생성되는 모든 Vue 인스턴스에 영향을 미친다.
mixins/lifecycle-logger.mixin.js
: 훅을 정의해 Vue 인스턴스의 라이프 사이클을 리턴하는 믹스인으로 내부에서 {this.$options.name}을 통해 인스턴스 접근
-$options는 인스턴스 프로퍼티로 Vue 컴포넌트 정의에 사용하는 options객체 참조해 this.$options.name으로 컴포넌트 이름에 접근
export default {
created () {
console.log(`${this.$options.name} created`)
},
beforeMount () {
console.log(`${this.$options.name} about to mount`)
},
mounted () {
console.log(`${this.$options.name} mounted`)
},
destroyed () {
console.log(`${this.$options.name} destroyed`)
}
}
믹스인 적용한 index.html
<script type="module">
import MessageList from './components/MessageList.js'
import lifecyleLogger from './mixins/lifecycle-logger.mixin.js'
import './directives/focus.directive.js'
import './filters/datetime.filter.js'
let vm = new Vue({
el: '#app',
name: 'MessagesApp',
mixins: [lifecyleLogger],
data: {
messages: [],
newMessage: ''
},
computed: {
addDisabled () {
return this.messages.length >= 10 || this.newMessage.length > 50
}
},
components: {
MessageList
},
methods: {
addMessage (event) {
if (!this.newMessage) return
let now = new Date()
this.messages.push({id: now.getTime(), text: this.newMessage, createdAt: now})
this.newMessage = ''
},
deleteMessage (message) {
this.messages.splice(this.messages.indexOf(message), 1)
}
}
})
</script>
믹스인 적용한 MessageList 컴포넌트
import lifecyleLogger from '../mixins/lifecycle-logger.mixin.js'
import MessageListItem from './MessageListItem.js'
export default {
name: 'MessageList',
mixins: [lifecyleLogger],
template: `<ul><message-list-item v-for="item in items"
:item="item" :key="item.id"@delete="deleteMessage(item)">
</message-list-item></ul>`,
props: {
items: {
type: Array,
required: true
}
},
components: {
MessageListItem
},
methods: {
deleteMessage (message) {
this.$emit('delete', message)
}
}
}
믹스인 적용한 MessageListItem
import lifecyleLogger from '../mixins/lifecycle-logger.mixin.js'
export default {
name: 'MessageListItem',
mixins: [lifecyleLogger],
template: `<li>{{ item.text }} - {{ item.createdAt | datetime }}
<button @click="deleteClicked">X</button></li>`,
props: {
item: {
type: Object,
required: true
}
},
methods: {
deleteClicked () {
this.$emit('delete')
}
}
}
#모든 인스턴스 라이플 사이클의 여러 단계를 확인해 AOP 적용
플러그인
: Vue.js 프레임워크에 확장성 제공
Vuex와 Vue Router, Vuelidate와 같은 플러그인 제공
plugins/lifecycle-logger.plugin.js
- install([vue 생성자], [options 객체]) 메서드를 가지는 일반 객체 생성
- install() 메소드 내부에서 vue 생성자에 추가
- 믹스인이 아닌 플러그인을 이용해 인스턴스의 라이프 사이클 추적 가능, 훅을 끌 수 있는 스위치 기능도 제공
이름 규칙을 이용해 명명
${pluginName}.plugin.js
const switchers = {
created: true,
beforeMount: true,
mounted: true,
destroyed: true
}
export default {
install (Vue, options) {
Object.assign(switchers, options)
Vue.mixin({
created () {
if (switchers.created) {
console.log(`${this.$options.name} created`)
}
},
beforeMount () {
if (switchers.beforeMount) {
console.log(`${this.$options.name} about to mount`)
}
},
mounted () {
if (switchers.mounted) {
console.log(`${this.$options.name} mounted`)
}
},
destroyed () {
if (switchers.destroyed) {
console.log(`${this.$options.name} destroyed`)
}
}
})
}
}
Object.assign() 메소드로 options 객체 내의 switchers를 미리 정의된 switchers에 병합
-> 라이프 사이클 훅 함수를 전역 믹스인으로 적용
믹스인을 제거 후 플러그인 적용
<script type="module">
import MessageList from './components/MessageList.js'
import LifecycleLogger from './plugins/lifecycle-logger.plugin.js'
import './directives/focus.directive.js'
import './filters/datetime.filter.js'
//beforceMount 훅 추적 비활성화
Vue.use(LifecycleLogger, {beforeMount: false})
window.vm = new Vue({
el: '#app',
name: 'MessagesApp',
data: {
messages: [],
newMessage: ''
},
computed: {
addDisabled () {
return this.messages.length >= 10 || this.newMessage.length > 50
}
},
components: {
MessageList
},
methods: {
addMessage (event) {
if (!this.newMessage) return
let now = new Date()
this.messages.push({id: now.getTime(), text: this.newMessage, createdAt: now})
this.newMessage = ''
},
deleteMessage (message) {
this.messages.splice(this.messages.indexOf(message), 1)
}
}
})
</script>
반응형 시스템 동작 방식
: 반응형 데이터 반인딩 시스템을 이용해 데이터와 뷰를 동기화 상태로 유지한다.
-옵저버 디자인 패턴으로 데이터 의존성 수집 -> 데이터 변경시 와처에 통지
-Vue 인스턴스 초기화되는 동안 data 객체의 프로퍼티에 접근해 값을 업데이트하기 위해
Object.defineProperty() 메소드로 게터와 세터 함수를 생성해 data 객체의 모든 프로퍼티를 반응형으로 생성
-render 함수가 DOM 업데이트 시 템플릿에서 사용된 프로퍼티의 게터 함수 호출 -> 모든 Vue 컴포넌트 인스턴스에 대해 렌더 와처를 생성해 의존관계에 있는 프로퍼티 수집 -> 변경 추적해 렌더 와처에 통지 -> render 함수 트리거해 렌더 와처 DOM을 업데이트
'Server Programming > Spring Boot 2 Full-Stack Programming' 카테고리의 다른 글
[작업 관리 애플리케이션 개발] 3-2. 스프링 부트와 Vue.js 조합 (연동) (0) | 2022.11.24 |
---|---|
[작업 관리 애플리케이션 개발] 3. 스프링 5 -> 스프링 부트 + MySQL (0) | 2022.11.22 |
[작업 관리 애플리케이션 개발] 1. 자바 개발자 관점의 자바스크립트 (0) | 2022.11.22 |