실행순서
webpack 에서 제일 중요한 두가지가 entry point라는 진입점과 컴파일된 결과물을 저장하는 output 이라고 하였다. 기본적인 entry point는 src/app.js인데 vue에서는 src/main.js가 entry point이다.
1 2 3 4 5 6 |
... new Vue({ router, store, render: h => h(App) }).$mount("#app"); |
이 main.js 화일을 살펴보면 App이라는 루트 컴포넌트를 index.html의 app이라는 DOM으로 렌더링을 하고 있다는 것을 알 수 있다.
앞에서 페이지 방식에서도 Vue Instance와 2-3 가지 컴포넌트를 만들었으므로 앞에서 페이지 방식으로 만든 shop 페이지를 shop이라는 메뉴 페이지를 만들고 마이그레이션을 하도록 하겠다.
메뉴 추가
home 과 About 두개의 메뉴가 존재하는데, 여기에 shop 이라는 메뉴를 추가해보겠다. 먼저 view 폴더에 Shop.vue 를 추가한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<template> <div> Shop </div> </template> <script> export default { name: "Shop" } </script> <style scoped> </style> |
router.js에 라우팅 패스를 추가한다.
1 2 3 4 5 6 7 8 9 |
... }, { path: "/shop", name: "shop", component: () => import("./views/Shop.vue") } ] }); |
루트 컴포넌트인 App.vue에 메뉴 링크를 추가한다.
1 2 3 4 5 6 7 8 9 10 11 |
<template> <div id="app"> <div id="nav"> <router-link to="/">Home</router-link> | <router-link to="/about">About</router-link> | <router-link to="/shop">Shop</router-link> </div> <router-view /> </div> </template> ... |
이제 화면에 shop 링크가 보일것이고 클릭하면 /shop 유알엘로 이동하고 화면에 Shop이라는 글자가 보일것이다.
리팩토링
Introduction에서 작성한 Vue Instance 를 Shop.vue로 이동한다. css는 하단에 scope를 제거하고 입력한다. template에 product라는 컴포넌트는 아직 인식이 안될것이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
<template> <div> <div class="cart"> <p>Cart({{ cart }})</p> </div> <product :premium="premium" @add-to-cart="updateCart"></product> </div> </template> <script> import Product from "../components/Product"; export default { name: "Shop", components: { 'product': Product }, data() { return { premium: true, cart: [] } }, methods: { updateCart(id) { this.cart.push(id); } } } </script> <style> body { font-family: tahoma; color:#282828; margin: 0px; } .nav-bar { background: linear-gradient(-90deg, #84CF6A, #16C0B0); height: 60px; margin-bottom: 15px; } .product { display: flex; flex-flow: wrap; padding: 1rem; } img { border: 1px solid #d8d8d8; width: 70%; margin: 40px; box-shadow: 0px .5px 1px #d8d8d8; } .product-image { width: 80%; } .product-image, .product-info { margin-top: 10px; width: 50%; } .color-box { width: 40px; height: 40px; margin-top: 5px; } .cart { margin-right: 25px; float: right; border: 1px solid #d8d8d8; padding: 5px 20px; } button { margin-top: 30px; border: none; background-color: #1E95EA; color: white; height: 40px; width: 100px; font-size: 14px; } .disabledButton { background-color: #d8d8d8; } .review-form { width: 400px; padding: 20px; margin: 40px; border: 1px solid #d8d8d8; } input { width: 100%; height: 25px; margin-bottom: 20px; } textarea { width: 100%; height: 60px; } .tab { margin-left: 20px; cursor: pointer; } .activeTab { color: #16C0B0; text-decoration: underline; } </style> |
앞에서는 product를 Vue.copmonent(‘id’, {}) 로
글로벌로 등록하였다. 여기서는 local로 등록한다. components에 있는 부분처럼 components에 Product.vue 를 등록하다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
<template> <div class="product"> <div class="product-image"> <img :src="image"> </div> <div class="product-info"> <h1>{{title}}</h1> <p v-if="inStock">In Stock</p> <p v-else>Out of Stock</p> <p>User is premium: {{premium}}</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 @click="addToCart" :disabled="!inStock" :class="{ disabledButton: !inStock }" >Add to cart</button> </div> <div> <h2>Reviews</h2> <p v-if="!reviews.length">There are no reviews yet.</p> <ul> <li v-for="review in reviews"> <p>{{ review.name }}</p> <p>Rating: {{ review.rating }}</p> <p>{{ review.review }}</p> </li> </ul> </div> <product-review @review-submitted="addReview"></product-review> </div> </template> <script> import ProductReview from "./ProductReview"; export default { name: "Product", components: { 'product-review': ProductReview }, premium: { type: Boolean, required: true, } }, data() { return { product: 'Socks', brand: 'Vue Mastery', details: ['80% cotton', '20% polyester', 'Gender-neutral'], selectedVariant: 0, variants: [ { variantId: 2234, variantColor: 'green', variantImage: 'https://www.vuemastery.com/images/challenges/vmSocks-green-onWhite.jpg', variantQuantity: 10 }, { variantId: 2235, variantColor: 'blue', variantImage: 'https://www.vuemastery.com/images/challenges/vmSocks-blue-onWhite.jpg', variantQuantity: 0 } ], reviews: [] } }, computed: { title() { return this.brand + ' ' + this.product; }, image() { return this.variants[this.selectedVariant].variantImage; }, inStock() { return this.variants[this.selectedVariant].variantQuantity; }, shipping() { if (this.premium) { return "free" } else { return 2.99 } } }, methods: { addToCart() { this.$emit('add-to-cart', this.variants[this.selectedVariant].variantId); }, updateProduct(index) { this.selectedVariant = index; }, addReview(productReview) { this.reviews.push(productReview) } } } </script> <style scoped> </style> |
마찬가지로 product-review 컴포넌트도 local로 등록한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
<template> <form class="review-form" @submit.prevent="onSubmit"> <p> <label for="name">Name:</label> <input id="name" v-model="name" placeholder="name" required> </p> <p> <label for="review">Review:</label> <textarea id="review" v-model="review"></textarea> </p> <p> <label for="rating">Rating:</label> <select id="rating" v-model.number="rating"> <option>5</option> <option>4</option> <option>3</option> <option>2</option> <option>1</option> </select> </p> <p> <input type="submit" value="Submit"> </p> </form> </template> <script> export default { name: "ProductReview", data() { return { name: null, review: null, rating: null } }, methods: { onSubmit() { let productReview = { name: this.name, review: this.review, rating: this.rating } this.$emit('review-submitted', productReview); this.name = null this.review = null this.rating = null } } } </script> <style scoped> </style> |