Vue - 기본 구조

ezae·2022년 7월 4일
0

vue

목록 보기
1/3

vue에서의 컴포넌트

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>

webpack을 사용한 컴포넌트

아래는 반응속도를 테스트하는 코드이다

  • main.js
import Vue from 'vue';
import ResponseCheck from './ResponseCheck'

new Vue(ResponseCheck).$mount('#root');
  • webpack.config.js
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',
    }
}
  • ResponseCheck.html (body 태그)
<body>
    <div id="root"></div>
    <script src="./dist/app.js"></script>
</body>
  • ResponseCheck.vue
<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

<template>안에 화면구현을 위한 html코드를 작성하고, {{}}안에 data를 써서 가변적인 내용을 표현한다.

👾 script

<script>에서는 자바스크립트 코드(vue 코드)를 써주면 됨

data

가변적인 내용을 담는 값들을 모아놓는다고 생각하면 됨

methods

함수를 만드는 곳
@click="function" 또는 @click="function()" 으로 호출할 수 있음

watch

값의 변화를 감시할 때 쓴다.
data이름을 적는다던가 하면 React의 useEffect에서 []에 담아놓은것과 같이 값이 변할 때 이벤트를 실행할 수 있다.

computed

계산식을 담는다고 생각하면 된다.
평균 시간에서 바로 {{result.reduce((a, c) => a + c, 0) / result.length || 0}}를 써줘도 동일한 값이 보이는데 굳이 computed의 average라는 함수로 따로 뺀 이유는 성능을 위해서다.
예시와 같은 코드를 구현했을 때, {{message}}가 변경되면 화면이 전체 랜더링 되는데, template의 평균시간에서 바로 계산식을 써 줄 경우 계산을 또 해야하는 상황이 생긴다.
그러나 computed는 값을 캐싱하여 보여주므로 성능면에서 template에 바로 계산식을 써주는 것보다 적절하다고 할 수 있겠다.

👾 style

css를 작성하는 곳
컴포넌트에서 스타일을 선언할 때, scope를 써주면 같은 컴포넌트 내부에서만 사용하도록 할 수 있다.
예를 들어 box라는 class에 스타일을 지정하고, 다른 컴포넌트에서도 box라는 클래스명 사용한 경우
scope를 써주지 않으면 다른 컴포넌트에서도 스타일이 씌여질 수 있고, scope를 써주면 해당 컴포넌트 내부에서만 쓰임!
리액트 할 때 이거 때문에 짜증났었는데 뷰는 쏘 간단데스네

profile
삐약🐣

0개의 댓글