1. 기본개념 & 기본생성방법
소스 : https://github.com/donghyeon0725/vue_tutorials
목표 : 싱글페이지 어플리케이션(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 인스턴스를 생성하는 방법으로도 사용할 수 있다.
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 |