[Express+Vue 搭建電商網站] 17 使用常數管理變數

17 使用常數管理變數

使用常數管理變數

建立 ManufacturerForm 組件

和商品資訊一樣,我們也要將製造商資訊封裝到另一個單獨的組件 ManufacturerForm,新增 src/components/ManufacturerForm.vue 檔案

<template>
 <form @submit.prevent="saveManufacturer">
 <div class="form-group">
 <label>Name</label>
 <input type="text" placeholder="Name" v-model="model.name" name="name" class="form-control" />
 </div>
 <div class="form-group new-button">
 <button class="button">
 <i class="fa fa-pencil"></i>
 <!-- Conditional rendering for input text -->
 <span v-if="isEditing">Update Manufacturer</span>
 <span v-else>Add Manufacturer</span>
 </button>
 </div>
 </form>
</template>
<script>
export default {
 props: ["model", "isEditing"],
 methods: {
 saveManufacturer() {
 this.$emit("save-manufacturer", this.model);
 }
 }
};
</script>

重構 getters 檔案

在建立編輯生產商的組件前,需先加入對應的 getter 屬性。打開 src/store/getters.js 檔案,對 manufacturerGetters 增加一個屬性,用來取得狀態庫中的指定生產商

manufacturerById: (state, getters) => id => {
 if (getters.allManufacturers.length > 0) {
 return getters.allManufacturers.filter(manufacturer => manufacturer._id === id)[0]
 } else {
 return state.manufacturer;
 }
}

建立 EditManufacturers 頁面

在建立好顯示製造商資訊的表單組件 ManufacturerForm 以及設定好取得資料的 getter 之後,我們要來建立編輯製造商的頁面 src/views/admin/EditManufacturers.vue

<template>
 <manufacturer-form @save-manufacturer="addManufacturer" :model="model" :isEditing="true"></manufacturer-form>
</template>
<script>
import ManufacturerForm from "@/components/ManufacturerForm.vue";
export default {
 created() {
 this.$store.dispatch("manufacturerById", {
 manufacturerId: this.$route.params["id"]
 });
 },
 computed: {
 model() {
 const manufacturer = this.$store.getters.manufacturerById(
 this.$route.params["id"]
 );
 return { ...manufacturer };
 }
 },
 methods: {
 addManufacturer(model) {
 this.$store.dispatch("updateManufacturer", {
 manufacturer: model
 });
 }
 },
 components: {
 "manufacturer-form": ManufacturerForm
 }
};
</script>

組件在創建時會使用 action 去異步取得製造商資訊,並透過 mutation 修改狀態池。
之所以在 model 這個 computed 中回傳製造商資料的備份資料,是為了在修改資料被送出前不對當前 store 的生產商屬性做操作。而組件內如果對生產商有操作,會透過 action 修改商品資訊,然後呼叫 mutation 變更狀態池。

建立 NewManufacturers 頁面

跟剛剛的編輯頁面邏輯差不多,這邊要建立新增頁面 src/views/admin/NewManufacturers.vue

<template>
 <manufacturer-form @save-manufacturer="addManufacturer" :model="model"></manufacturer-form>
</template>
<script>
import ManufacturerForm from "@/components/ManufacturerForm.vue";
export default {
 computed: {
 model() {
 return {};
 }
 },
 methods: {
 addManufacturer(model) {
 this.$store.dispatch("addManufacturer", {
 manufacturer: model
 });
 }
 },
 components: {
 "manufacturer-form": ManufacturerForm
 }
};
</script>

重構 Admin menu

src/views/admin/Index.vue 加入新的頁面用來增加製造商,沒什麼多解釋的

<li>
 <router-link to="/admin/manufacturers/new">新增製造商</router-link>
</li>

增加路由邏輯

頁面跟連結都做好了,打開 src/router/index.js 引入路由設定

import NewManufacturers from '@/views/admin/NewManufacturers';
import EditManufacturers from '@/views/admin/EditManufacturers';

增加 admin 頁面下路由 children 屬性

{
 path: 'manufacturers/new',
 name: 'NewManufacturers',
 component: NewManufacturers,
},
{
 path: 'manufacturers/edit/:id',
 name: 'EditManufacturers',
 component: EditManufacturers,
},

用常數管理通用名稱

很多時候我們會把功能封裝到組件內,以便重複使用組件。而在組件內我們會定義很多方法名稱去呼叫不同的事件,例如當使用者按下了送出,觸發了 ADD_PRODUCT 事件,那在 action 中就要有相應的事件。並且 action 在後端處理完請求後,也會呼叫指定的 mutations 來處理狀態的變更。這中間只要有一個字打錯,整個流程就會錯誤,並且非常難除錯。
因此我們會使用常數的方式定義 actionsmutations 中的事件,只要我們都使用同一組常數就可以維持一致。關鍵的是如果這麼做打錯字的時候就會報錯,方便除錯。
接著就來實作建立 src/store/mutation-types.js 檔案作為 mapping 表

export const ALL_PRODUCTS = 'ALL_PRODUCTS';
export const ALL_PRODUCTS_SUCCESS = 'ALL_PRODUCTS_SUCCESS';
export const PRODUCT_BY_ID = 'PRODUCT_BY_ID';
export const PRODUCT_BY_ID_SUCCESS = 'PRODUCT_BY_ID_SUCCESS';
export const ADD_PRODUCT = 'ADD_PRODUCT';
export const ADD_PRODUCT_SUCCESS = 'ADD_PRODUCT_SUCCESS';
export const UPDATE_PRODUCT = 'UPDATE_PRODUCT';
export const UPDATE_PRODUCT_SUCCESS = 'UPDATE_PRODUCT_SUCCESS';
export const REMOVE_PRODUCT = 'REMOVE_PRODUCT';
export const REMOVE_PRODUCT_SUCCESS = 'REMOVE_PRODUCT_SUCCESS';
export const ADD_TO_CART = 'ADD_TO_CART';
export const REMOVE_FROM_CART = 'REMOVE_FROM_CART';
export const ALL_MANUFACTURERS = 'ALL_MANUFACTURER';
export const ALL_MANUFACTURERS_SUCCESS = 'ALL_MANUFACTURER_S';
export const MANUFACTURER_BY_ID = 'MANUFACTURER_BY_ID';
export const MANUFACTURER_BY_ID_SUCCESS = 'MANUFACTURER_BY_ID_SUCCESS';
export const ADD_MANUFACTURER = 'ADD_MANUFACTURER';
export const ADD_MANUFACTURER_SUCCESS = 'ADD_MANUFACTURER_SUCCESS';
export const UPDATE_MANUFACTURER = 'UPDATE_MANUFACTURER';
export const UPDATE_MANUFACTURER_SUCCESS = 'UPDATE_MANUFACTURER_SUCCESS';
export const REMOVE_MANUFACTURER = 'REMOVE_MANUFACTURER';
export const REMOVE_MANUFACTURER_SUCCESS = 'REMOVE_MANUFACTURER_SUCCESS';

重構 actions 檔案

打開 src/store/actions.js 檔案,引入剛剛的常數表文件後,把其中的方法名稱通通改成使用常數表內的常數。

import axios from 'axios';
import {
 ADD_PRODUCT,
 ADD_PRODUCT_SUCCESS,
 PRODUCT_BY_ID,
 PRODUCT_BY_ID_SUCCESS,
 UPDATE_PRODUCT,
 UPDATE_PRODUCT_SUCCESS,
 REMOVE_PRODUCT,
 REMOVE_PRODUCT_SUCCESS,
 ALL_PRODUCTS,
 ALL_PRODUCTS_SUCCESS,
 ALL_MANUFACTURERS,
 ALL_MANUFACTURERS_SUCCESS,
 MANUFACTURER_BY_ID,
 MANUFACTURER_BY_ID_SUCCESS,
 ADD_MANUFACTURER,
 ADD_MANUFACTURER_SUCCESS,
 UPDATE_MANUFACTURER,
 UPDATE_MANUFACTURER_SUCCESS,
 REMOVE_MANUFACTURER,
 REMOVE_MANUFACTURER_SUCCESS,
} from './mutation-types';
const API_BASE = 'http://localhost:3000/api/v1';
export const productActions = {
 allProducts({ commit }) {
 commit(ALL_PRODUCTS)
 axios.get(`${API_BASE}/products`).then(response => {
 commit(ALL_PRODUCTS_SUCCESS, {
 products: response.data,
 });
 })
 },
 productById({ commit }, payload) {
 commit(PRODUCT_BY_ID);
 const { productId } = payload;
 axios.get(`${API_BASE}/products/${productId}`).then(response => {
 commit(PRODUCT_BY_ID_SUCCESS, {
 product: response.data,
 });
 })
 },
 removeProduct({ commit }, payload) {
 commit(REMOVE_PRODUCT);
 const { productId } = payload;
 axios.delete(`${API_BASE}/products/${productId}`).then(() => {
 // 回傳 productId,用來刪除對應商品
 commit(REMOVE_PRODUCT_SUCCESS, {
 productId,
 });
 })
 },
 updateProduct({ commit }, payload) {
 commit(UPDATE_PRODUCT);
 const { product } = payload;
 axios.put(`${API_BASE}/products/${product._id}`, product).then(() => {
 commit(UPDATE_PRODUCT_SUCCESS, {
 product,
 });
 })
 },
 addProduct({ commit }, payload) {
 commit(ADD_PRODUCT);
 const { product } = payload;
 axios.post(`${API_BASE}/products`, product).then(response => {
 commit(ADD_PRODUCT_SUCCESS, {
 product: response.data,
 })
 })
 }
};
export const manufacturerActions = {
 allManufacturers({ commit }) {
 commit(ALL_MANUFACTURERS);
 axios.get(`${API_BASE}/manufacturers`).then(response => {
 commit(ALL_MANUFACTURERS_SUCCESS, {
 manufacturers: response.data,
 });
 })
 },
 manufacturerById({ commit }, payload) {
 commit(MANUFACTURER_BY_ID);
 const { manufacturerId } = payload;
 axios.get(`${API_BASE}/manufacturers/${manufacturerId}`).then(response => {
 commit(MANUFACTURER_BY_ID_SUCCESS, {
 manufacturer: response.data,
 });
 })
 },
 removeManufacturer({ commit }, payload) {
 commit(REMOVE_MANUFACTURER);
 const { manufacturerId } = payload;
 axios.delete(`${API_BASE}/manufacturers/${manufacturerId}`).then(() => {
 // 回傳 manufacturerId,用來刪除對應的製造商
 commit(REMOVE_MANUFACTURER_SUCCESS, {
 manufacturerId,
 });
 })
 },
 updateManufacturer({ commit }, payload) {
 commit(UPDATE_MANUFACTURER);
 const { manufacturer } = payload;
 axios.put(`${API_BASE}/manufacturers/${manufacturer._id}`, manufacturer).then(() => {
 commit(UPDATE_MANUFACTURER_SUCCESS, {
 manufacturer,
 });
 })
 },
 addManufacturer({ commit }, payload) {
 commit(ADD_MANUFACTURER);
 const { manufacturer } = payload;
 axios.post(`${API_BASE}/manufacturers`, manufacturer).then(response => {
 commit(ADD_MANUFACTURER_SUCCESS, {
 manufacturer: response.data,
 })
 })
 }
}

重構 manufacturer 檔案

src/store/mutations.js 也做跟上一步一樣的事情

import {
 ADD_PRODUCT,
 ADD_PRODUCT_SUCCESS,
 PRODUCT_BY_ID,
 PRODUCT_BY_ID_SUCCESS,
 UPDATE_PRODUCT,
 UPDATE_PRODUCT_SUCCESS,
 REMOVE_PRODUCT,
 REMOVE_PRODUCT_SUCCESS,
 ADD_TO_CART,
 REMOVE_FROM_CART,
 ALL_PRODUCTS,
 ALL_PRODUCTS_SUCCESS,
 ALL_MANUFACTURERS,
 ALL_MANUFACTURERS_SUCCESS,
 MANUFACTURER_BY_ID,
 MANUFACTURER_BY_ID_SUCCESS,
 ADD_MANUFACTURER,
 ADD_MANUFACTURER_SUCCESS,
 UPDATE_MANUFACTURER,
 UPDATE_MANUFACTURER_SUCCESS,
 REMOVE_MANUFACTURER,
 REMOVE_MANUFACTURER_SUCCESS,
} from './mutation-types';
export const productMutations = {
 [ALL_PRODUCTS](state) {
 state.showLoader = true;
 },
 [ALL_PRODUCTS_SUCCESS](state, payload) {
 const { products } = payload;
 state.showLoader = false;
 state.products = products;
 },
 [PRODUCT_BY_ID](state) {
 state.showLoader = true;
 },
 [PRODUCT_BY_ID_SUCCESS](state, payload) {
 state.showLoader = false;
 const { product } = payload;
 state.product = product;
 },
 [REMOVE_PRODUCT](state) {
 state.showLoader = true;
 },
 [REMOVE_PRODUCT_SUCCESS](state, payload) {
 state.showLoader = false;
 const { productId } = payload;
 state.products = state.products.filter(product => product._id !== productId);
 },
 [UPDATE_PRODUCT](state) {
 state.showLoader = true;
 },
 [UPDATE_PRODUCT_SUCCESS](state, payload) {
 state.showLoader = false;
 const { product: newProduct } = payload;
 state.product = newProduct;
 state.products = state.products.map(product => {
 if (product._id === newProduct._id) {
 return newProduct;
 }
 return product;
 })
 },
 [ADD_PRODUCT](state) {
 state.showLoader = true;
 },
 [ADD_PRODUCT_SUCCESS](state, payload) {
 state.showLoader = false;
 const { product } = payload;
 state.products = state.products.concat(product);
 },
};
export const cartMutations = {
 [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)
 },
}
export const manufacturerMutations = {
 [ALL_MANUFACTURERS](state) {
 state.showLoader = true;
 },
 [ALL_MANUFACTURERS_SUCCESS](state, payload) {
 const { manufacturers } = payload;
 state.showLoader = false;
 state.manufacturers = manufacturers;
 },
 [MANUFACTURER_BY_ID](state) {
 state.showLoader = true;
 },
 [MANUFACTURER_BY_ID_SUCCESS](state, payload) {
 state.showLoader = false;
 const { manufacturer } = payload;
 state.manufacturer = manufacturer;
 },
 [REMOVE_MANUFACTURER](state) {
 state.showLoader = true;
 },
 [REMOVE_MANUFACTURER_SUCCESS](state, payload) {
 state.showLoader = false;
 const { manufacturerId } = payload;
 state.manufacturers = state.manufacturers.filter(manufacturer => manufacturer._id !== manufacturerId);
 },
 [UPDATE_MANUFACTURER](state) {
 state.showLoader = true;
 },
 [UPDATE_MANUFACTURER_SUCCESS](state, payload) {
 state.showLoader = false;
 const { manufacturer: newManufacturer } = payload;
 state.manufacturers = state.manufacturers.map(manufacturer => {
 if (manufacturer._id === newManufacturer._id) {
 return newManufacturer;
 }
 return manufacturer;
 })
 },
 [ADD_MANUFACTURER](state) {
 state.showLoader = true;
 },
 [ADD_MANUFACTURER_SUCCESS](state, payload) {
 state.showLoader = false;
 const { manufacturer } = payload;
 state.manufacturers = state.manufacturers.concat(manufacturer);
 }
}

如此就完成了用常數替換 actionsmutations 兩隻檔案中的事件類型,增加了維護的可行性。

留言