1. 기본개념 & 기본생성방법
소스 : https://github.com/donghyeon0725/vue_tutorials
donghyeon0725/vue_tutorials
Contribute to donghyeon0725/vue_tutorials development by creating an account on GitHub.
github.com
목표 : 싱글페이지 어플리케이션(SPA)을 만들며, vue.js 가 내부적으로 어떻게 동작하는지 알아보는 것
Vue 인스턴스 생성
<script src="https://unpkg.com/vue@2.5.13/dist/vue.js"></script>
// Vue 인스턴스 내부에 전달할 객체를 options 이라고 부를 것이다. new Vue({ /*options*/ })
뷰는 기본적으로 CLI를 사용하는 방법도 있겠지만, js 파일을 불러와 Vue 인스턴스를 생성하는 방법으로도 사용할 수 있다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>MessageApp</title> | |
</head> | |
<body> | |
<div id="app"></div> | |
<script src="https://unpkg.com/vue@2.5.13/dist/vue.js"></script> | |
<script> | |
let vm = new Vue({ | |
el: '#app' | |
}); | |
</script> | |
</body> | |
</html> |
10번 줄 처럼 뷰와 양방향 바인딩을 맺을 요소를 DOM에 생성한다 생성하고
뷰 인스턴스인 vm을 생성한다.
그리고 options 내부의 el 프로퍼티를 이용해서 css 선택자로 선택하면 뷰를 사용할 준비가 끝난 것이다.
양방향 바인딩
let vm = new Vue({ el: '#app', data: { messages: [], newMessage: '' }, ...
초기값을 설정해야 양방향 바인딩이 수월하다.
또한 들어가야할 데이터가 어떤 것인지 한눈에 보인다.
컴포넌트 내부에 모듈을 넣을 때
- 탬플릿으로 구성한 뒤 모듈 하나하나 구현하는 방법
- 직접 내부에 HTML 요소 부착한 뒤에 직접 바인딩하기
직접 내부에 HTML 요소 부착한 뒤에 직접 바인딩하기
<div id="app" v-cloak> <ul> <li v-for="message in messages"> {{ message.text.toLowerCase() }} - {{ message.createdAt }} <button @click="deleteMessage(message)">X</button> </li> </ul> <form v-on:submit.prevent="addMessage"> <textarea v-model="newMessage" placeholder="Leave a message"></textarea> <div> <button type="submit" :disabled="addDisabled">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: { //뷰 내부적으로 이 값을 this 키워드가 참조하게 되고, 이 값들이 태그와 값을 양방향바인딩 한다. messages: [], newMessage: '' }, 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); } }, computed: { addDisabled() { return this.messages.length > 10 || this.newMessage.length > 50; //v-bind:disabled="messages.length >= 10 || this.newMessage.length > 50" } } }); </script>
- v-cloak 은 div의 속성 => 잠시 display:none 하기 위해 사용 (보간법을 위해 사용한 이중괄호를 노출시키지 않기 위함)
- v-for 은 반복을 지시하는 지시자. 내부의 messages는 Vue 인스턴스의 data 객체 내부의 프로퍼티를 가리킴. 뷰가 이를 랜더링한다. 리스트에서 꺼낸 값을 message(별칭)에 담아줌. 이 때 messages에 접근하기 위해 굳이 vm.messages 로 접근할 필요가 없음. 지시자를 사용해서 양방향 바인딩이 되었다.
- {{}} => 이중괄호. 내부의 값을 가져옴. 이때 toLowerCase와 같은 자바스크립트 표현식을 사용할 수 있다.
- @click은 v-on의 줄임표현. 이벤트를 달아준다.
- deleteMessage는 뷰 인스턴스의 methods 프로퍼티 내부의 값
- v-on:submit.prevent="addMessage" 으로 폼에 addMessage 함수를 부탁했는데, 이 때 submit이 자동으로 되는 것을 방지하기 위해 prevent를 덧붙였다.
- 그리고 textarea의 v-model 지시자를 통해서 양방향바인딩을 생성했다. 양방향 바인딩 된 객체는 vue가 데이터를 계속 보고 있다가 변화가 생기면 textarea 의 값을 변경시킬 것이다.
- methods를 작성할 때 화살표 함수는 사용하면 안된다. 이유는 this키워드를 통해 vm 객체에 접근할 수 없게 되기 때문이다.
- addMessage 내부의 this 키워드가 vm이 아닌, data를 가리키는 이유는 vue가 내부적으로 프록시패턴(대리인, wrapper)을 사용해서 실행컨택스트를 data로 바꿔 놓았기 때문이다(아마 apply 나 bind 같은 메소드로 바꾸어 놓았을 것 같음)
- 양방향 바인딩(vm 인스턴스 내부 데이터 변화가 생기면 DOM에 부착된 요소도 변경이 생기는 이유)이 가능한 이유는 옵저버 패턴을 통해서 와쳐라는 것이 뷰의 인스턴스를 계속 관찰하고 있기 때문이다.
- 양방향 바인딩 될 값이 아닌, 계산된 값을 일방향으로 바인딩 하고 싶은 경우 computed 프로퍼티를 고려해볼 수 있다. 내부에 추가해주면 된다.
- index.html
<div id="app" v-cloak> <message-list :items="messages" @ondelete="deleteMessage"></message-list> <ul> <li v-for="message in messages"> {{ message.text.toLowerCase() }} - {{ message.createdAt }} <button @click="deleteMessage(message)">X</button> </li> </ul> </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: '' }, 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); } }, components: { MessageList /* MessageList: MessageList 가 파스칼 표기법인 MessageList 가 케밥표기법인 message-list 으로 변환되고 message-list 위와 같은 이름이 id로 사용된다. * */ } }); </script>
- <script type="module"> 이라는 타입으로 스크립트를 열어야 다른 모듈을 import 할 수 있다.
- <message-list :items="messages" @ondelete="deleteMessage"></message-list> 에서 message-list라는 파스칼표기법에서 케밥표기법인 MessageList으로 작성해서 components 란 프로퍼티로 넘겨주면 태그를 모듈 속 template 속성의 태그로 랜더링 해준다.
- 속성 items 을 이용해서 값을 자식 컴포넌트로 전달할 수 있다.
- ondelete이란 이름의 이벤트를 만들었고, 해당 이벤트가 동작하는 시점을 자식 컴포넌트가 결정할 예정이다.
MessageList.js
import MessageListItem from "./MessageListItem.js"; export default { name: 'MessageList', template: ` <ol type="I"> <message-list-item :key="item.id" v-for="item in items" :item="item" value="3" :itemCount="true" @delete="deleteMessage(item)" > </message-list-item> </ol> `, // 바인딩은 기본적으로 태그안에 속성값을 두는 것이고, 자식 모듈이 props를 통해 값을 가져가서 바인딩하는 경우 해당 속성을 가지게 됨 props: { //고립된 모듈의 스코프 특성상, 부모 컴포넌트의 값을 전달 받을 수 있도록 해놓은 장치 items: { type: Array, required: true } }, methods: { deleteMessage(message) { this.$emit('ondelete', message); } }, components: { MessageListItem } }
- 기본적으로 디버깅을 위해서 name이라는 속성은 추가하는 것이 좋다.
- 모듈이므로 export 하고 있다.
- 자식 컴포넌트에서 또 자식 컴포넌트를 사용할 수 있다.
- props 라는 프로퍼티로 부모 태그의 속성값을 받을 수 있다. 이때 속성의 이름과 프로퍼티의 이름이 같아야 한다. 꼭 있어야 하는 값이 아닌 경우, [items] 같이 배열의 형태로 줄 수도 있다.
- 자식컴포넌트에서 클릭시 deleteMessage 메소드가 호출되고 이때 부모의 이벤트중 ondelete 이라는 이벤트를 호출한다. 해당 이벤트를 트리거 해주는 키워드가 $emit 이다. 한편 이벤트의 첫번 째 인자로 message를 전달하기 위해서 $emit의 두번째 인자로 message를 넣어주었다.
MessageListItem.js
//import lifecycleLogger from '../mixins/lifecycle-logger.mixin.js'; export default { name: 'MessageListItem', //mixins: [lifecycleLogger], template: ` <li :value="value"> {{ item.text }} - {{ item.createdAt }} <button @click="deleteClicked" :disabled="itemCount">X</button> </li> `, props: { item: { type: Object, required: true }, itemCount: { type: Boolean, required: true }, value: { type: String, required: true } //['item', 'itemCount', 'value'] => required false 일 때 }, methods: { deleteClicked() { this.$emit('delete'); } } }
'모던 웹 애플리케이션 개발 > 뷰(Vue)' 카테고리의 다른 글
3. 반응형 시스템이 가능한 이유 (0) | 2021.03.05 |
---|---|
2. 라이프 사이클 (0) | 2021.03.05 |
댓글을 사용할 수 없습니다.