[Express+Vue 搭建電商網站] 14 使用組件思維重構頁面邏輯

使用組件思維重構頁面邏輯

在先前我們學會怎麼使用 Vuex 進行狀態管理,如何使用 Action 取得 API 資料,以及使用 Mutation 更改狀態。
而這一篇中將透過 Vue 的組件化思維簡化原本複雜且分散於各個頁面的邏輯。

建立 ProductButton 組件

新建一個 src/components/products/ProductButton.vue 檔案,準備用這個組件替換掉操作購物車中狀態按鈕的組件

<template>
 <div>
 <button v-if="isAdding" class="button" @click="addToCart">加入購物車</button>
 <button v-else class="button" @click="removeFromCart(product._id)">從購物車移除</button>
 </div>
</template>
<script>
export default {
 props: ["product"],
 computed: {
 isAdding() {
 let isAdding = true;
 this.cart.map(product => {
 if (product._id === this.product._id) {
 isAdding = false;
 }
 });
 return isAdding;
 },
 cart() {
 return this.$store.state.cart;
 }
 },
 methods: {
 addToCart() {
 this.$store.commit("ADD_TO_CART", {
 product: this.product
 });
 },
 removeFromCart(productId) {
 this.$store.commit("REMOVE_FROM_CART", {
 productId
 });
 }
 }
};
</script>

在這個組件中,透過了 v-if 來判斷 isAdding 這個 computed 是否為真,而 isAdding 使用了來自 Vuex store 中 state 的 cart 參數
藉由遍歷整個 cart 判斷當前的商品是否在購物車內,進而顯示對應的按鈕,並綁定不同的行為。
addToCartremoveFromCart 這兩個方法會調用 mutation 而改變 Vuex store 中的狀態。

建立 ProductItem 組件

建立好了按鈕後,要建立個別商品在渲染時使用的組件

建立 src/components/products/ProductItem.vue 顯示商品相關資訊,並引入上一步建立的 ProductButton 組件

<template>
 <div>
 <div class="product">
 <p class="product__name">商品名稱:{{product.name}}</p>
 <p class="product__description">簡介:{{product.description}}</p>
 <p class="product__price">售價:{{product.price}}</p>
 <p class="product.manufacturer">生產商:{{product.manufacturer.name}}</p>
 <img :src="product.image" alt class="product__image" />
 <product-button :product="product"></product-button>
 </div>
 </div>
</template>
<script>
import ProductButton from "./ProductButton";
export default {
 name: "product-item",
 props: ["product"],
 components: {
 "product-button": ProductButton
 }
};
</script>

透過 import ProductButton from "./ProductButton" 引入剛剛建立的 ProductButton 組件,並註冊在 components 物件中,最後在模板中使用組件。

重構 ProductList 組件

接著就可以把 src/components/products/ProductList.vue 這個組件重構,把跟商品相關的模板部分移除

methods 中的加入購物車方法也一併移除,因為單一商品的資料以及按鈕都已經加入到剛剛的 ProductItem 組件中。

<template>
 <div>
 <div class="products">
 <div class="container">This is ProductList</div>
 <template v-for="product in products">
 <product-item :product="product" :key="product._id"></product-item>
 </template>
 </div>
 </div>
</template>
<style>
.product {
 border-bottom: 1px solid black;
}
.product__image {
 width: 100px;
 height: 100px;
}
</style>
<script>
import ProductItem from './ProductItem.vue';
export default {
 name: "product-list",
 created() {
 if (this.products.length === 0) {
 this.$store.dispatch("allProducts");
 }
 },
 computed: {
 // a computed getter
 products() {
 return this.$store.state.products;
 }
 },
 components: {
 'product-item': ProductItem
 },
};
</script>

我們在引用了 ProductItem 組件後,利用 v-for 把每個商品都傳入組件用來建立商品列表。

重構 Cart 頁面

就跟在 ProductItem 組件中一樣,我們也將 src/views/Cart.vue 頁面中的商品列表組件化

<template>
 <div>
 <div class="title">
 <h1>{{msg}}</h1>
 </div>
 <template v-for="product in cart">
 <product-item :product="product" :key="product._id"></product-item>
 </template>
 </div>
</template>
<style>
.product {
 border-bottom: 1px solid black;
}
.product__image {
 width: 100px;
 height: 100px;
}
</style>
<script>
import ProductItem from "@/components/products/ProductItem.vue";
export default {
 name: "home",
 data() {
 return {
 msg: "Welcome to the Cart Page"
 };
 },
 computed: {
 cart() {
 return this.$store.state.cart;
 }
 },
 components: {
 "product-item": ProductItem
 }
};
</script>

一樣引入 ProductItem 組件,並且在 components 中註冊,接著就可以在模板中把購物車內的商品傳入給組件使用。
而頁面的效果應該不會有所改變,只是把重複的部分整合成組件來共用,增加維護性。

留言