티스토리 뷰

728x90

vue의 인스턴스로 구현했던 코드를 Vue.component로 바꾸는 작업을 진행한다.

 

 

Components - Intro to Vue.js | Vue Mastery

In this lesson we’ll be learning components: reusable blocks of code that can have both structure and functionality and create a more modular and maintainable codebase.

www.vuemastery.com

<!DOCTYPE html>
<html>
    <head>
        <title>App</title>
        <link rel="stylesheet" type="text/css" href="./style.css">
    </head>
    <body>
        <div class="nav-bar"></div>
        <div id="app">
            <product></product>
        </div>

        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="main.js"></script>
    </body>
</html>
Vue.component("product", {
    template: `
    <div class="product">
        <div class="product-image">
            <img v-bind:src="image"/>
        </div>
        <div class="product-info">
            <h1>{{ title }}</h1><span v-show="onSale">On Sale!</span>
            <p v-if="inStock">In Stock</p>
            <p v-else>Out of Stock</p>

            <ul>
                <li v-for="detail in details">{{ detail }}</li>
            </ul>

            <div class="color-box"
                v-for="(variant, index) in variants" 
                :key="variant.variantId"
                :style="{backgroundColor: variant.variantColor}"
                @mouseover="updateProduct(index)"
                >
            </div>

            <button v-on:click="addToCart"
            :disabled="!inStock"
            :class="{ disabledButton: !inStock }"
            >Add to cart</button>
            <div class="cart">
                <p>Cart({{ cart }})</p>
            </div>
        </div>
    </div>
    `,
    data(){
        return {
            brand: 'Vue Mastery',
            product: 'Socks',
            selectedVarint: 0,
            onSale: false,
            details: ["80% cotton", "20% polyester", "Gender-neutral" ],
            variants: [
                {
                    variantId: 2234,
                    variantColor: "green",
                    variantImage: "./assets/vmSocks-green-onWhite.jpg",
                    variantQuantity: 10
                },
                {
                    variantId: 2235,
                    variantColor: "blue",
                    variantImage: "./assets/vmSocks-blue-onWhite.jpg",
                    variantQuantity: 0
                }
            ],
            cart: 0,
        }
    },
    methods: {
        addToCart(){
            this.cart +=1
        },
        updateProduct(index){
            this.selectedVarint = index
        }
    },
    computed: {
        title(){
            return `${this.brand} ${this.product}`
        },
        image(){
            return this.variants[this.selectedVarint].variantImage
        },
        inStock(){
            return this.variants[this.selectedVarint].variantQuantity
        },
    }
})
var app = new Vue({
    el: '#app',
})

product 이름을 가진 컴포넌트를 Vue.component로 생성한다.

template에 기존 html 코드를 넣는다. 단, 최상위 요소(root element)는 필수다. 리액트도 최상위 요소 1개는 필수인데 단순하게 묶는 용도라면 Fragment tag(<></>)를 사용하면 편리하다.

data를 JSON 형태가 아닌 메서드 형태로 변경한 후 필요한 데이터를 반환한다.

 

변경하고 나니 template에 문자열로 되어 있다 보니 가독성이 무척 나빠졌다. 가독성을 높이는 좋은 방법이 있는지 궁금하다.

 

props를 v-bind 문법으로 전달하는 방법도 소개하고 있다. premium 고객 여부에 따라 배송료 가격을 다르게 노출하는 기능을 추가하면 다음과 같다.

 

<!DOCTYPE html>
<html>
    <head>
        <title>App</title>
        <link rel="stylesheet" type="text/css" href="./style.css">
    </head>
    <body>
        <div class="nav-bar"></div>
        <div id="app">
            <product :premium="premium"></product>
        </div>

        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="main.js"></script>
    </body>
</html>
Vue.component("product", {
    props: {
        premium: {
            type: Boolean,
            required: true
        }
    },
    template: `
    <div class="product">
        <div class="product-image">
            <img v-bind:src="image"/>
        </div>
        <div class="product-info">
            <h1>{{ title }}</h1><span v-show="onSale">On Sale!</span>
            <p v-if="inStock">In Stock</p>
            <p v-else>Out of Stock</p>
            <p>Shipping: {{ shipping }}</p>

            <ul>
                <li v-for="detail in details">{{ detail }}</li>
            </ul>

            <div class="color-box"
                v-for="(variant, index) in variants" 
                :key="variant.variantId"
                :style="{backgroundColor: variant.variantColor}"
                @mouseover="updateProduct(index)"
                >
            </div>

            <button v-on:click="addToCart"
            :disabled="!inStock"
            :class="{ disabledButton: !inStock }"
            >Add to cart</button>
            <div class="cart">
                <p>Cart({{ cart }})</p>
            </div>
        </div>
    </div>
    `,
    data(){
        return {
            brand: 'Vue Mastery',
            product: 'Socks',
            selectedVarint: 0,
            onSale: false,
            details: ["80% cotton", "20% polyester", "Gender-neutral" ],
            variants: [
                {
                    variantId: 2234,
                    variantColor: "green",
                    variantImage: "./assets/vmSocks-green-onWhite.jpg",
                    variantQuantity: 10
                },
                {
                    variantId: 2235,
                    variantColor: "blue",
                    variantImage: "./assets/vmSocks-blue-onWhite.jpg",
                    variantQuantity: 0
                }
            ],
            cart: 0,
        }
    },
    methods: {
        addToCart(){
            this.cart +=1
        },
        updateProduct(index){
            this.selectedVarint = index
        }
    },
    computed: {
        title(){
            return `${this.brand} ${this.product}`
        },
        image(){
            return this.variants[this.selectedVarint].variantImage
        },
        inStock(){
            return this.variants[this.selectedVarint].variantQuantity
        },
        shipping(){
            if(this.premium){
                return "Free"
            } else {
                return 2.99
            }
        }
    }
})
var app = new Vue({
    el: '#app',
    data: {
        premium: false
    }
})

컴포넌트 생성 시 props 옵션 값에 props의 필수 여부와 타입을 지정한다. 리액트에도 같은 기능을 제공하고 있는데 타입 스크립트를 사용하면 타입 체크를 하지 않아도 되기 때문에 타입 스크립트 사용을 추천한다.

 

728x90
댓글