[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