vue에서도 컴포넌트 개념이 당연하게도 존재한다
아래 예제는 끝말잇기를 컴포넌트로 만들고, 컴포넌트를 세번 호출하는 코드이다.
Vue.component('컴포넌트이름',{})를 사용하고 data는 함수로 리턴한다.
코드 순서는 컴포넌트는 인스턴스보다 위에 위치해야 하며,
스크립트는 div#root보다 아래에 위치해야 한다.
또한 html상에선 word-relay, start-word같이 kebab-case를 사용했다면,
스크립트 내에선 wordRelay, startWord같이 camelCase를 사용한다.
<div id="root">
<word-relay start-word="커피"></word-relay>
<word-relay start-word="시작"></word-relay>
<word-relay start-word="사이다"></word-relay>
</div>
<script>
//vue 전역 컴포넌트
Vue.component('wordRelay', {
template: `
<div>
<p>끝말잇기 시작!</p>
<span>제시어 : <b>{{word}}</b></span>
<form v-on:submit="onSubmitForm">
<input ref="answer" v-model="value"/>
<button type="submit">입력</button>
</form>
<div id="result">{{result}}</div>
</div>
`,
props: ['startWord'],
data(){
return {
word: this.startWord,
value: '',
result: ''
}
},
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>
//vue 인스턴스
const app = new Vue({
el : "#root"
})
</script>
아래는 반응속도를 테스트하는 코드이다
import Vue from 'vue';
import ResponseCheck from './ResponseCheck'
new Vue(ResponseCheck).$mount('#root');
const vueLoaderPlugin = require('vue-loader/lib/plugin')
const path = require('path');
module.exports = {
mode: 'development',
devtool: 'eval',
resolve: {
extensions: ['.js', '.vue'],
},
entry: {
app: path.join(__dirname, 'main.js'),
//app : 하나로 합쳐질 파일의 이름 (output에서 쓰임)
//main.js < 합쳐질 파일들 중 메인 파일
},
module: { //js파일을 합칠 때 어떻게 합칠 것인지
rules: [{
test: /\.vue$/, //.vue로 끝나는 파일
use: 'vue-loader' //use === loader라서 아무거나 써도 무방
},{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
}],
},
plugins: [
new vueLoaderPlugin()
],
output: {
filename: '[name].js', //또는 app.js [name]으로 하면 자동으로 들어감
path: path.join(__dirname, 'dist'), //__dirname를 써주면 현재경로
publicPath: '/dist',
}
}
<body>
<div id="root"></div>
<script src="./dist/app.js"></script>
</body>
<template>
<div>
<div id="screen" :class="state" @click="onClickScreen">
{{message}}
</div>
<template v-show="result.length">
<div>평균 시간 : {{average}}</div>
<button @click="onReset">리셋</button>
</template>
</div>
</template>
<script>
let startTime = 0;
let endTime = 0;
let timeout = null;
export default {
data(){
return{
result: [],
state: 'waiting',
message: '클릭해서 시작하세요.'
}
},
computed: {
average(){
return this.result.reduce((a, c) => a + c, 0) / this.result.length || 0
}
},
methods: {
onReset(){
this.result = [];
},
onClickScreen(){
if(this.state === 'waiting'){
this.state = 'ready';
this.message = '초록색이 되면 클릭하세요!'
timeout = setTimeout(()=> {
this.state='now';
this.message = '클릭!'
startTime = new Date();
}, Math.floor(Math.random() * 1000) + 2000); //2~3초
}else if(this.state === 'ready'){
clearTimeout(timeout)
this.state = 'waiting'
this.message = '너무 성급하시군요 ㅋ^^ㅋ 초록색이 된 후에 클릭하세요!'
}else if(this.state === 'now'){
endTime = new Date();
this.state = 'waiting'
this.message = '클릭해서 게임을 시작하세요.'
this.result.push(endTime - startTime);
console.log(endTime, startTime, this.result)
}
}
}
}
</script>
<style scoped>
#screen {
width: 300px;
height: 200px;
text-align: center;
user-select: none;
}
#screen.waiting {
background-color: aqua;
}
#screen.ready {
background-color: red;
color: white;
}
#screen.now {
background-color: greenyellow;
}
</style>
웹팩 구조 볼라고 파일 다 가져왔는데
지금은 ResponseCheck.vue 코드로 컴포넌트 구조파악을 해보겠ㄷr,,
<template>
안에 화면구현을 위한 html코드를 작성하고, {{}}안에 data를 써서 가변적인 내용을 표현한다.
<script>
에서는 자바스크립트 코드(vue 코드)를 써주면 됨
가변적인 내용을 담는 값들을 모아놓는다고 생각하면 됨
함수를 만드는 곳
@click="function" 또는 @click="function()" 으로 호출할 수 있음
값의 변화를 감시할 때 쓴다.
data이름을 적는다던가 하면 React의 useEffect에서 []
에 담아놓은것과 같이 값이 변할 때 이벤트를 실행할 수 있다.
계산식을 담는다고 생각하면 된다.
평균 시간에서 바로 {{result.reduce((a, c) => a + c, 0) / result.length || 0}}를 써줘도 동일한 값이 보이는데 굳이 computed의 average라는 함수로 따로 뺀 이유는 성능을 위해서다.
예시와 같은 코드를 구현했을 때, {{message}}가 변경되면 화면이 전체 랜더링 되는데, template의 평균시간에서 바로 계산식을 써 줄 경우 계산을 또 해야하는 상황이 생긴다.
그러나 computed는 값을 캐싱하여 보여주므로 성능면에서 template에 바로 계산식을 써주는 것보다 적절하다고 할 수 있겠다.
css를 작성하는 곳
컴포넌트에서 스타일을 선언할 때, scope를 써주면 같은 컴포넌트 내부에서만 사용하도록 할 수 있다.
예를 들어 box라는 class에 스타일을 지정하고, 다른 컴포넌트에서도 box라는 클래스명 사용한 경우
scope를 써주지 않으면 다른 컴포넌트에서도 스타일이 씌여질 수 있고, scope를 써주면 해당 컴포넌트 내부에서만 쓰임!
리액트 할 때 이거 때문에 짜증났었는데 뷰는 쏘 간단데스네