[Express+Vue 搭建電商網站] 17 使用常數管理變數
使用 Express + Vue 搭建一個電商網站 - 使用常數管理變數
很多時候我們會把功能封裝到組件內,以便重複使用組件,而在組件內我們會定義很多方法名稱去呼叫不同的事件
這中間只要有一個字打錯,整個流程就會錯誤,並且非常難除錯,因此我們會使用常數維持一致
打錯字的時候就會報錯,方便除錯
建立 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 來處理狀態的變更
這中間只要有一個字打錯,整個流程就會錯誤,並且非常難除錯
因此我們會使用常數的方式定義 actions 和 mutations 中的事件
只要我們都使用同一組常數就可以維持一致。關鍵的是如果這麼做打錯字的時候就會報錯,方便除錯
接著就來實作建立 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); } }
如此就完成了用常數替換 actions 和 mutations 兩隻檔案中的事件類型
後續要維護就不會東一塊西一塊的過於分散,還要用全域搜尋人工替換,增加了維護的可行性
專案範例程式碼 GitHub 網址:ray247k/mini-E-commerce
