[Express+Vue 搭建電商網站] 12 在 Vuex 使用 Mutation 管理狀態

使用 Mutation

在 Vuex 中 Mutation 是修改 Vuex store 內容的唯一方法
Mutation 是定義在 Vuex store 物件中 mutations 屬性的一系列函數
長得會像

ACTION_NAME(state, payload) {
 return state++;
}

其中 ACTION_NAME 就是組件發出的事件或動作的名稱,這個函數接收兩個參數 statepayload
state 就是在 Vuex store 裡面儲存的 statepayload 則是跟著呼叫的事件或是動作一起傳來的參數,然後就可以對現有的 state 做操作而回傳新的 state,透過這種方式我們可以輕鬆的管理所有狀態。

初始化 mutations 狀態

打開 src/store/index.js 檔案,修改其中的 state並,加入 mutations,這邊我們先把資料寫死,未來會接上先前寫的後端 API

import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
 strict: true,
 state: {
 // bought items
 cart: [],
 // ajax loader
 showLoader: false,
 // selected product
 product: {},
 // all products
 products: [
 {
 _id: '1',
 name: 'iPhone 11',
 description: '全新雙相機系統,捕捉所見所愛,範圍更多更廣。智慧型手機歷來最快速的晶片,加上滿足一天的電池續航力,讓你完成更多事,充電更少次。而智慧型手機中最高的影片畫質,讓你的點滴回憶,看起來比過去更加動人精彩。',
 image: 'https://i.gadgets360cdn.com/large/iPhone11_leak_1567592422045.jpg',
 price: 44000,
 manufacturer: 'Apple Inc'
 },
 {
 _id: '2',
 name: 'Pixel 4',
 description: '只要用Pixel 4 拍照,不用進工作室後製編輯,也能拍出相同品質的相片!Pixel 4 是第一支能夠拍下銀河的手機,只要輕輕一點,就能拍下美麗星空。',
 image: 'https://pgw.udn.com.tw/gw/photo.php?u=https://uc.udn.com.tw/photo/2019/10/16/realtime/6946830.jpg',
 price: 24990,
 manufacturer: 'Google'
 },
 {
 _id: '3',
 name: 'Xperia 1 II',
 description: '由於 Sony 先前整合了 Mobile 手機部門到 Sony 本家消費性電子產品部門的緣故,所以這次以這樣的命名方式出現也不讓人意外。',
 image: 'https://timgm.eprice.com.tw/tw/mobile/img/2020-02/24/5484360/innocences_1_a8c4b844f3c0c83646b79e366c3d8111.jpg',
 price: 29000,
 manufacturer: 'SONY'
 },
 {
 _id: '4',
 name: 'V30S',
 description: 'LG V30 S ThinQ 最大的改變是 Vision AI 功能的加入,拍照的同時相機會自動識別畫面中的場景,準確的設定場景模式。',
 image: 'https://img.eprice.com.tw/img/mobile/5858/large.png',
 price: 12500,
 manufacturer: 'LG'
 },
 {
 _id: '5',
 name: 'Galaxy Note 9',
 description: '做為三星每年下半年度的旗艦手機,Note 系列從 2011 年的第一代 Galaxy Note 起,就以 S Pen 做為最大賣點,並且帶動大螢幕智慧手機的風潮直到今日。',
 image: 'https://timgm.eprice.com.tw/tw/mobile/img/2018-08/09/5115231/hat7029_1_77763a4f06a1fba43ab32e66d90bcba1.jpg',
 price: 18900,
 manufacturer: 'Samsung'
 }
 ],
 // all manufacturers
 manufacturers: [],
 },
 mutations: {
 ADD_TO_CART(state, payload) {
 const { product } = payload;
 state.cart.push(product)
 },
 REMOVE_FROM_CART(state, payload) {
 const { productId } = payload
 state.cart = state.cart.filter(product => product._id !== productId)
 }
 }
});

除了 state 中暫時寫死的商品內容外,我們加了兩個 mutations 屬性下的方法,分別代表組件中「將商品加入購物車」以及「從購物車移除移出商品」的動作

ProductList 組件

接著要新建「商品列表」的組件,用來展示商品詳細資訊
新建 /src/components/products/ProductList.vue

<template>
 <div>
 <div class="products">
 <div class="container">This is ProductList</div>
 <template v-for="product in products">
 <div :key="product._id" 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}}</p>
 <img :src="product.image" alt class="product__image" />
 <button @click="addToCart(product)">加入購物車</button>
 </div>
 </template>
 </div>
 </div>
</template>
<style>
.product {
 border-bottom: 1px solid black;
}
.product__image {
 width: 100px;
 height: 100px;
}
</style>
<script>
export default {
 name: "product-list",
 computed: {
 // a computed getter
 products() {
 return this.$store.state.products;
 }
 },
 methods: {
 addToCart(product) {
 this.$store.commit("ADD_TO_CART", {
 product
 });
 }
 }
};
</script>

<script> 中定義了一個 computed 作為商品資料的預處理,這樣在模板渲染時就只需要使用 {products} 就可以取得在 Vuex store 中的 products 資料。然後定義了一個點擊事件 addToCart 來處理加入購物車按鈕的點擊,會通過 this.$store.commit 方法將目前商品物件 {product} 當作 payload 來操作 Vux store 中 mutation 所定義的 ADD_TO_CART 方法做狀態修改。
組件建立完之後要在畫面引入才能使用,修改首頁 src/pages/Home.vue 把剛剛建立的 ProductList.vue 組件加入畫面

<template>
 <div>
 <div class="title">
 <h1>In Stock</h1>
 </div>
 <product-list></product-list>
 </div>
</template>
<script>
import ProductList from '@/components/products/ProductList.vue';
 export default {
 name: 'home',
 data () {
 return {
 msg: 'Welcome to Your Vue.js App'
 };
 },
 components: {
 'product-list': ProductList
 }
 }
</script>

購物車

接著要修改購物車頁面 /src/views/Cart.vue 將商品資訊顯示出來。

<template>
 <div>
 <div class="title">
 <h1>{{msg}}</h1>
 </div>
 <template v-for="product in cart">
 <div :key="product._id" 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}}</p>
 <img :src="product.image" alt class="product__image" />
 <button @click="removeFromCart(product._id)">從購物車中移除</button>
 </div>
 </template>
 </div>
</template>
<style>
.product {
 border-bottom: 1px solid black;
}
.product__image {
 width: 100px;
 height: 100px;
}
</style>
<script>
export default {
 name: "home",
 data() {
 return {
 msg: "Welcome to the Cart Page"
 };
 },
 computed: {
 cart() {
 return this.$store.state.cart;
 }
 },
 methods: {
 removeFromCart(productId) {
 this.$store.commit("REMOVE_FROM_CART", {
 productId
 });
 }
 }
};
</script>

同樣的加入 computed 對商品做預處理,這邊接收的是 this.$store.state.cart 也就是購物車中的商品列表。
寫好觸發 Vuex mutation 中移出購物車的按鈕方法 removeFromCart
接著就可以開啟瀏覽器預覽執行結果
vuex-mutations
一開始購物車是空的,我們隨便加了幾款手機進購物車後,在購物車頁面可以看見剛剛有選中的商品!而在購物車頁面可以將商品從購物車移除。

留言