1 컴포넌트
source: categories/study/vue-principle/vue-principle_1.md
1.1 컴포넌트의 필요성
- 웹팩을 써야되는 이유
- 컴포넌트 시스템 도입
- 같은 부분을 여러번 사용하고 싶을 때?
- 아래와 같이 끝말잇기를 세개를 동시에 넣으면 세개가 동시에 바뀐다.
- 데이터가 서로 공유가 되고있기 때문
- 그래서 아래와 같이 데이터들,
ref
, 메소드 등을 다 나눠서 연결(바인딩)해줘야 한다. - 하지만 아래와 같은 방식은 별로다. 같은 코드인데 반복(중복)해서 넣어줘야되는 것이 불편하고 보기도 안좋다.
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<div>{{ word }}</div>
<form v-on:submit="onSubmitForm">
<input type="text" ref="answer" v-model="value">
<button type="submit">입력!</button>
</form>
<div>{{ result }}</div>
<div>{{ word1 }}</div>
<form v-on:submit="onSubmitForm1">
<input type="text" ref="answer1" v-model="value1">
<button type="submit">입력!</button>
</form>
<div>{{ result1 }}</div>
<div>{{ word2 }}</div>
<form v-on:submit="onSubmitForm2">
<input type="text" ref="answer2" v-model="value2">
<button type="submit">입력!</button>
</form>
<div>{{ result2 }}</div>
</div>
<script>
const app = new Vue({
el: '#root',
data: {
word: '제로초',
word1: 'Vue',
word2: '강좌',
result: '',
result1: '',
result2: '',
value: '',
value1: '',
value2: '',
},
methods: {
onSubmitForm(e) {
e.preventDefault();
if (this.word[this.word.length - 1] === this.value[0]) {
this.result = '딩동댕';
this.word = this.value;
this.value = '';
this.$refs.answer.focus();
} else {
this.result = '땡';
this.value = '';
this.$refs.answer.focus();
}
},
onSubmitForm1(e) {
e.preventDefault();
if (this.word1[this.word1.length - 1] === this.value1[0]) {
this.result1 = '딩동댕';
this.word1 = this.value1;
this.value1 = '';
this.$refs.answer1.focus();
} else {
this.result1 = '땡';
this.value1 = '';
this.$refs.answer1.focus();
}
},
onSubmitForm2(e) {
e.preventDefault();
if (this.word2[this.word2.length - 1] === this.value2[0]) {
this.result2 = '딩동댕';
this.word2 = this.value2;
this.value2 = '';
this.$refs.answer2.focus();
} else {
this.result2 = '땡';
this.value2 = '';
this.$refs.answer2.focus();
}
},
},
})
</script>
</body>
</html>
- 프로그래밍에서 중요한 것
- 중복을 줄여라
- 위 코드는 너무 중복이 많다.
- 이렇게 반복되는 부분을 컴포넌트로 만들면된다. (여기서 컴포넌트 개념 등장)
끝말잇기 컴포넌트
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<word-relay></word-relay>
<word-relay></word-relay>
<word-relay></word-relay>
</div>
<script>
Vue.component('word-relay', {
template: `
<div>{{ word }}</div>
<form v-on:submit="onSubmitForm">
<input type="text" ref="answer" v-model="value">
<button type="submit">입력!</button>
</form>
<div>{{ result }}</div>
`,
data: {
word: '제로초',
result: '',
value: '',
},
methods: {
onSubmitForm(e) {
e.preventDefault();
if (this.word[this.word.length - 1] === this.value[0]) {
this.result = '딩동댕';
this.word = this.value;
this.value = '';
this.$refs.answer.focus();
} else {
this.result = '땡';
this.value = '';
this.$refs.answer.focus();
}
},
}
});
</script>
<script>
const app = new Vue({
el: '#root',
})
</script>
</body>
</html>
1.2 컴포넌트의 특성
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<word-relay></word-relay>
<word-relay></word-relay>
<word-relay></word-relay>
</div>
<script>
// 아래 컴포넌트 형식은 전역 컴포넌트이다. 반대로 지역 컴포넌트도 있다.
Vue.component('word-relay', {
template: `
<div>
<div>{{ word }}</div>
<form v-on:submit="onSubmitForm">
<input type="text" ref="answer" v-model="value">
<button type="submit">입력!</button>
</form>
<div>{{ result }}</div>
</div>
`,
data() {
return {
word: '제로초',
result: '',
value: '',
}
},
methods: {
onSubmitForm(e) {
e.preventDefault();
if (this.word[this.word.length - 1] === this.value[0]) {
this.result = '딩동댕';
this.word = this.value;
this.value = '';
this.$refs.answer.focus();
} else {
this.result = '땡';
this.value = '';
this.$refs.answer.focus();
}
},
}
});
</script>
<script>
const app = new Vue({
el: '#root',
})
</script>
</body>
</html>
- 위 코드를 그대로 실행하면 에러 발생
- "data" option should be a function that returns a per-instance value in component definitions.
- data 를 객체 형식이 아닌 컴포넌트에선 함수 형식으로 만들어야 한다.
- 같은 data 를 참조하지 않고 각각 새로운 객체를 참조하도록 새로운 객체를 만들도록 함수 형식으로 작성해야된다.
- 위 에러 해결한 후 발생하는 에러
- Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.
- 위에 component 를 보면 루트 수준에 형제 태그가 3개가 있다. 이 루트 수준의 태그는 반드시 1개만 있어야된다.
- 이점이 마음에 안드시는 분들도 있을 것이다. 쓸데없는
div
태그 하나가 더 들어갔기 때문이다. - 이러한 자잘한 점들 때문에 웹팩을 이용하게된다. 웹팩을 이용하면 이런한 문제를 해결할 수 있다.
- component 는 같은 것을 렌더링하지만, 중복을 제거할 수 있다.
- data 들은 각 component 별로 따로따로 존재한다.
-
component 는 vue instance 보다는 위에 위치하여야하고 스크립트는 div#root 보다는 아래에 위치해야 한다.
- component 를 만든다고 해서 서버에 부담을 줄여주거나 그런건 아니다.
- 어차피 처음에 모든 컴포넌트를 서버에서 내려주기 때문.
1.3 props 와 웹팩의 필요성
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<word-relay start-word="제로초"></word-relay>
<word-relay start-word="초밥"></word-relay>
<word-relay start-word="바보"></word-relay>
</div>
<script>
// 아래 컴포넌트 형식은 전역 컴포넌트이다. 반대로 지역 컴포넌트도 있다.
Vue.component('wordRelay', {
template: `
<div>
<div>{{ word }}</div>
<form v-on:submit="onSubmitForm">
<input type="text" ref="answer" v-model="value">
<button type="submit">입력!</button>
</form>
<div>{{ result }}</div>
</div>
`,
props: ['startWord'],
data() {
return {
word: this.startWord,
result: '',
value: '',
}
},
methods: {
onSubmitForm(e) {
e.preventDefault();
if (this.word[this.word.length - 1] === this.value[0]) {
this.result = '딩동댕';
this.word = this.value;
this.value = '';
this.$refs.answer.focus();
} else {
this.result = '땡';
this.value = '';
this.$refs.answer.focus();
}
},
}
});
</script>
<script>
const app = new Vue({
el: '#root',
})
</script>
</body>
</html>
- component 가 많아지다보면 script 파일을 여러개 만들 것이다.
- 그리고 각 파일을
script
태그로 불러올 것이다.- 이때 불편함이 생긴다.
- 불러온
script
순서도 일치해야하고 하나 수정하면 다른 파일에 영향을 미치는지 아닌지도 검사해야되고.. - 이러한 문제들을 해겨하기 위해 CommonJS, RequireJS 같은 시스템을 브라우저에서 많이 쓰다가
- 웹팩이 나오면서 스크립트들을 아예 하나로 합쳐주는 역할을 해주게되었다.
- 스크립트 파일이 여러개 있을 때의 복잡함을 웹팩이 많이 해소해준다.