本文由圖雀社區成員 Holy 使用 Tuture 實戰教程寫做工具 寫做而成,歡迎加入圖雀社區,一塊兒創做精彩的免費技術實戰教程,予力編程行業發展。
前面五篇教程咱們已經基本實現了迷你全棧電商應用的界面展現以及功能邏輯,相信你們在這個過程當中都收穫頗豐,而且邁向了全棧工程師的第一步。可是咱們卻並不知足於此,咱們還須要對咱們的項目代碼進行優化,使得咱們的代碼可讀性更高,也更好維護。相信細心的大家已經感受到了項目中的store實例實在是過於臃腫,所以,本篇教程就是帶你們一塊兒學習如何抽出 Getters 、 Mutations 和Actions 邏輯實現store的「減重」以及如何幹掉 mutation-types 硬編碼。javascript
歡迎閱讀《從零到部署:用 Vue 和 Express 實現迷你全棧電商應用》系列:html
若是你但願直接從這一步開始,請運行如下命令:vue
git clone -b section-six https://github.com/tuture-dev/vue-online-shop-frontend.git cd vue-online-shop-frontend
本文所涉及的源代碼都放在了 Github 上,若是您以爲咱們寫得還不錯,但願您能給❤️ 這篇文章點贊+Github倉庫加星❤️哦~
這一節咱們來學習如何抽出在store
實例中定義的複雜getters
和mutation
邏輯。java
咱們發現以前咱們直接把全部的getter
屬性和方法都定義在了store
實例中的getters
屬性中,全部的mutation
屬性也都定義在了store
實例中的mutations
屬性中,這樣顯得store
實例特別的累贅,所以咱們能夠經過對象展開運算符將這些複雜的邏輯抽取到對應的 Getters
和 Mutations
文件中。ios
首先咱們作一點本土化,把以前的 src/pages/admin/Index.vue
中的英文導航改爲中文版,方便查看;而且咱們增長了查看生產商導航。git
<template> <div> <div class="admin-new"> <div class="container"> <div class="col-lg-3 col-md-3 col-sm-12 col-xs-12"> <ul class="admin-menu"> <li> <router-link to="/admin">查看商品</router-link> </li> <li> <router-link to="/admin/new">添加商品</router-link> </li> <li> <router-link to="/admin/manufacturers">查看生產商</router-link> </li> </ul> </div> <router-view></router-view> </div> </div> </div> </template>
這裏咱們將有關商品的導航欄修改成中文版,讓用戶可以秒懂;除此以外咱們又添加了有關製造商的導航,這裏增長的是查看生產商導航,並添加了對應的導航跳轉路徑,該路徑須要與對應路由參數一致。github
咱們建立的src/pages/admin/Manufacturers.vue
文件是本地製造商組件,用於展現製造商的信息。vuex
<template> <div> <table class="table"> <thead> <tr> <th>製造商</th> <th></th> <th></th> </tr> </thead> <tbody> <tr v-for="manufacturer in manufacturers" :key="manufacturer._id"> <td>{{manufacturer.name}}</td> <td class="modify"><router-link :to="'/admin/manufacturers/edit/' + manufacturer._id">修改</router-link></td> <td class="remove"><a @click="removeManufacturer(manufacturer._id)" href="#">刪除</a></td> </tr> </tbody> </table> </div> </template> <style> table { margin: 0 auto; } .modify { color: blue; } .remove a { color: red; } </style> <script> export default { created() { if (this.manufacturers.length === 0) { this.$store.dispatch('allManufacturers'); } }, computed: { manufacturers() { return this.$store.getters.allManufacturers } }, methods: { removeManufacturer(manufacturerId) { // 使用 JavaScript BOM 的 confirm 方法來詢問用戶是否刪除此製造商 const res = confirm('是否刪除此製造商?'); // 若是用戶贊成,那麼就刪除此製造商 if (res) { this.$store.dispatch('removeManufacturer', { manufacturerId, }) } } } } </script>
這裏首先定義了一個計算屬性manufacturers
,經過this.$store.getters.allManufacturers
屬性訪問的形式調用對應的getter
屬性allManufacturers
從本地獲取manufacturers
,並返回給計算屬性manufacturers
。編程
而後在該組件剛被建立時判斷本地中是否存在manufacturers
,若是沒有則經過this.$store.dispatch
分發到類型爲allManufacturers
的action
中進行異步操做獲取全部製造商,並將獲取的製造商提交到對應的mutation
中,在mutation
中修改本地狀態,將獲取的全部製造商保存到本地。axios
最後利用v-for
在表格中遍歷manufacturers
,每一個製造商的信息在一行展現,除了信息以外還有兩個功能(修改和刪除製造商),點擊修改則會根據'/admin/manufacturers/edit/' + manufacturer._id
路由到指定頁面;點擊刪除則會觸發removeManufacturer
事件,首先詢問用戶是否贊成刪除,若用戶贊成則將選中製造商的id做爲載荷分發到類型爲removeManufacturer
的action
中,在action
中進行異步操做刪除後端對應商品,並將對應商品id提交到對應的mutation
中,在mutation
中進行本地狀態修改,刪除本地對應的商品。
根據Manufacturers
組件的設計原則,咱們須要再次進入src/pages/admin/Products.vue
文件。按照Manufacturers
組件的UI展現以及數據處理,將Products
組件進行一下重構。
<template> <div> <table class="table"> <thead> <tr> <th>名稱</th> <th>價格</th> <th>製造商</th> <th></th> <th></th> </tr> </thead> <tbody> <tr v-for="product in products" :key="product._id"> <td>{{product.name}}</td> <td>{{product.price}}</td> <td>{{product.manufacturer.name}}</td> <td class="modify"><router-link :to="'/admin/edit/' + product._id">修改</router-link></td> <td class="remove"><a @click="removeProduct(product._id)" href="#">刪除</a></td> </tr> </tbody> </table> </div> </template> <style> table { margin: 0 auto; } .modify { color: blue; } .remove a { color: red; } </style> <script> export default { created() { if (this.products.length === 0) { this.$store.dispatch('allProducts'); } }, computed: { products() { return this.$store.getters.allProducts } }, methods: { removeProduct(productId) { // 使用 JavaScript BOM 的 confirm 方法來詢問用戶是否刪除此商品 const res = confirm('是否刪除此商品?'); // 若是用戶贊成,那麼就刪除此商品 if (res) { this.$store.dispatch('removeProduct', { productId, }) } } } } // ...
這部分代碼邏輯與src/pages/admin/Manufacturers.vue
文件中的代碼邏輯類似,若是您理解了上面的代碼邏輯,那麼咱們相信您對這裏的代碼也能融會貫通,因此這裏就再也不贅述了。
咱們已經建立了有關製造商的導航以及查看製造商組件,還需對其配置相應的路由參數才能實現跳轉。所以再次進入src/router/index.js
文件,這裏咱們導入了製造商組件並增長了製造商相關路由參數。
// ... import New from '@/pages/admin/New'; import Products from '@/pages/admin/Products'; import Edit from '@/pages/admin/Edit'; import Manufacturers from '@/pages/admin/Manufacturers'; Vue.use(Router); // ... name: 'Edit', component: Edit, }, { path: 'manufacturers', name: 'Manufacturers', component: Manufacturers, }, ] }, { // ...
把項目跑起來,點擊Admin
而後再點擊查看生產商,咱們能夠看到從後端獲取的全部生產商: 下面展現的就是從後端獲取的製造商,而且咱們能夠對其進行修改和刪除操做。
咱們首先建立了src/store/getters.js
文件,用於存放各類不一樣類型的getter
屬性和方法。這裏咱們導出了兩個對象分別爲productGetters
和manufacturerGetters
,前者包含了有關商品的getter
屬性與方法,後者包含了有關製造商的getter
屬性與方法。
export const productGetters = { allProducts(state) { return state.products }, productById: (state, getters) => id => { if (getters.allProducts.length > 0) { return getters.allProducts.filter(product => product._id === id)[0] } else { return state.product; } } } export const manufacturerGetters = { allManufacturers(state) { return state.manufacturers; } }
在productGetters
對象中定義的就是有關商品的getter
屬性和方法,如allProducts
,productById
等等;在manufacturerGetters
對象中定義的就是有關製造商的getter
屬性和方法,如allManufacturers
等等。
咱們能夠採用屬性調用和方法調用的方式調用這裏的getter(根據咱們在getter對象中定義的是屬性仍是方法)
一樣的咱們建立了src/store/mutations.js
文件,用於存放從store
實例的mutations
屬性中抽取出來的各類mutation
屬性,這裏咱們定義了三個對象分別爲productMutations
,cartMutations
以及manufacturerMutations
。
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); } }; 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; }, 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); } }
在productMutations
對象中定義了有關商品響應Vue視圖層以及avtion
中提交的事件,好比ALL_PRODUCTS
,ALL_PRODUCTS_SUCCESS
,PRODUCT_BY_ID
以及PRODUCT_BY_ID_SUCCESS
等等。
在cartMutations
對象中定義了有關購物車響應Vue視圖層提交的事件,好比ADD_TO_CART
,REMOVE_FROM_CART
等等。
在manufacturerMutations
對象中定義了有關製造商響應Vue視圖層以及avtion
中提交的事件,好比ALL_MANUFACTURERS
,ALL_MANUFACTURERS_SUCCESS
,REMOVE_MANUFACTURER
以及REMOVE_MANUFACTURER_SUCCESS
等等。
咱們將store
實例中的Getter
屬性和Mutation
屬性抽出以後要再進行導入。再回到src/store/index.js
文件,這裏就是抽離getters
和mutations
邏輯以後的store
實例,看起來是否是輕盈了不少,也加強了代碼的可讀性。
// ... import Vuex from 'vuex'; import axios from 'axios'; import { productGetters, manufacturerGetters } from './getters'; import { productMutations, cartMutations, manufacturerMutations } from './mutations'; const API_BASE = 'http://localhost:3000/api/v1'; Vue.use(Vuex); // ... manufacturers: [], }, mutations: { ...productMutations, ...cartMutations, ...manufacturerMutations, }, getters: { ...productGetters, ...manufacturerGetters, }, actions: { allProducts({ commit }) { // ... 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, }); }) }, allManufacturers({ commit }) { commit('ALL_MANUFACTURERS'); axios.get(`${API_BASE}/manufacturers`).then(response => { commit('ALL_MANUFACTURERS_SUCCESS', { manufacturers: response.data, }); }) }, removeManufacturer({ commit }, payload) { commit('REMOVE_MANUFACTURER'); const { manufacturerId } = payload; axios.delete(`${API_BASE}/manufacturers/${manufacturerId}`).then(() => { // 返回 manufacturerId,用於刪除本地對應的製造商 commit('REMOVE_MANUFACTURER_SUCCESS', { manufacturerId, }); }) }, } });
這裏首先導入了getters
和mutations
文件中導出的全部對象,而後在store
實例的getters
和mutations
屬性中經過對象展開運算符的方式將對應的屬性和方法導入到store
實例中。
對象展開運算符是ES7草案中的新特性,將一個對象當中的對象的一部分取出來成爲一個新對象賦值給展開運算符的參數,而後插入到另一個對象當中。例如:
let shortcuts = { attr1: 3, attr2: 4 } let shortcuts2 = {} shortcuts2 = {...shortcuts} //上面的這種用法實際上至關因而: shortcuts2 = {attr1: 3, attr2: 4}
除此以外咱們又在actions
中添加了一些其餘的action
屬性,由於此時actions
還未被抽離,因此可能依然顯得有些臃腫,不過在後面咱們立刻也會將它抽離出來。
這一節咱們學習瞭如何抽出Getters
和Mutations
邏輯,減輕store
實例中的負載:
getters
和mutations
JS文件,在兩個JS文件中分別定義不一樣類型的getters
和mutations
對象並導出,而後在getters
和mutations
對象中定義相應的一些屬性和方法。store
的index
文件中導入這些getters
和mutations
對象,並在store
實例的getters
和mutations
屬性中經過對象展開運算符混入這些對象。this.$store.getters.屬性
和this.$store.mutations.屬性
的方式調用.上一節咱們學習瞭如何抽出Getters
和Mutations
邏輯,這一節咱們以一樣的方式抽出Actions
邏輯。
src/pages/admin/Edit.vue
是商品編輯組件,當觸發'/admin/edit/' + product._id
就會路由到指定商品信息編輯頁面,而後對商品信息進行修改。
以前咱們直接將展現商品信息的代碼放在該組件中,可是咱們發現展現商品信息這部分功能在新建商品和編輯商品組件中都須要使用,所以咱們打算把這部分代碼封裝爲一個展現商品信息的表單組件ProductForm
,這樣的話咱們在新建商品和編輯商品組件中都能複用該組件。
除此以外咱們還在該組件中添加了數據處理功能。
<template> <div> <div class="title"> <h1>This is Admin/Edit</h1> </div> <product-form @save-product="updateProduct" :model="model" :manufacturers="manufacturers" :isEditing="true" ></product-form> </div> </template> <script> import ProductForm from '@/components/products/ProductForm.vue'; export default { created() { const { name } = this.model; if (!name) { this.$store.dispatch('productById', { productId: this.$route.params['id'] }); } if (this.manufacturers.length === 0) { this.$store.dispatch('allManufacturers'); } }, computed: { manufacturers() { return this.$store.getters.allManufacturers; }, model() { const product = this.$store.getters.productById(this.$route.params['id']); // 這裏返回 product 的拷貝,是爲了在修改 product 的拷貝以後,在保存以前不修改本地 Vuex stire 的 product 屬性 return { ...product, manufacturer: { ...product.manufacturer } }; } }, methods: { updateProduct(product) { this.$store.dispatch('updateProduct', { product, }) } }, components: { 'product-form': ProductForm } } </script>
咱們先來看該組件的script
部分,首先定義了兩個計算屬性model
和manufacturers
返回本地商品和製造商。經過方法訪問的方式調用指定的getter
屬性productById
,參數爲當前處於激活狀態的路由對象的id,這裏返回product
的拷貝,是爲了在修改 product
的拷貝以後,在保存以前不修改本地 Vuex store 的product
屬性。計算屬性manufacturers
經過相同的方式獲取本地數據。
當該組件剛被建立時判斷計算屬性model
中是否有值,若是沒有則表示本地中沒有該商品,將包含該商品id的對象做爲載荷分發到類型爲productById
的action
中,在action
中進行異步操做從後端獲取對應商品,並提交到對應類型的mutation
中,在mutation
中將獲取到的商品保存到本地。除此以外判斷計算屬性manufacturers
中是否有值,若是沒有則經過相同的方式從後端獲取並保存到本地。
在template
中使用了子組件ProductForm
用表單的形式來展現商品信息,當用戶提交表單則會向父組件發射save-product
事件,父組件監聽到以後觸發updateProduct
事件,並將傳入的商品參數做爲載荷分發到類型爲updateProduct
的action
中,通知後端進行同步更新數據並提交到對應的mutation
中進行本地數據更新。
src/pages/admin/New.vue
是添加商品組件,與Edit
組件的代碼邏輯類似,只是一個是修改商品信息,一個是添加商品信息。
咱們將該組件中原先寫死的數據改爲了從後端動態獲取, 並將獲取的數據傳遞給子組件ProductForm。
<template> <product-form @save-product="addProduct" :model="model" :manufacturers="manufacturers" > </product-form> </template> <script> import ProductForm from '@/components/products/ProductForm.vue'; export default { created() { if (this.manufacturers.length === 0) { this.$store.dispatch('allManufacturers'); } }, computed: { manufacturers() { return this.$store.getters.allManufacturers; }, model() { return {}; } }, methods: { addProduct(model) { this.$store.dispatch('addProduct', { product: model, }) }, }, components: { 'product-form': ProductForm } } </script>
該組件代碼邏輯和Edit.vue
組件類似,只是在這裏咱們定義的計算屬性model
返回一個空對象做爲默認值,由於咱們是添加商品,本地中還不存在該商品。
像以前同樣咱們建立了src/store/actions.js
文件,用於存儲從store
實例的actions
屬性中抽取出來的不一樣類型的action
屬性。這裏咱們定義了兩個Actions
對象:productActions
和manufacturerActions
,分別表示有關商品和製造商對視圖層分發的事件做出的響應,並導出了這兩個對象。
import axios from 'axios'; 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, }); }) }, removeManufacturer({ commit }, payload) { commit('REMOVE_MANUFACTURER'); const { manufacturerId } = payload; axios.delete(`${API_BASE}/manufacturers/${manufacturerId}`).then(() => { // 返回 manufacturerId,用於刪除本地對應的製造商 commit('REMOVE_MANUFACTURER_SUCCESS', { manufacturerId, }); }) }, }
在該文件中咱們首先導入axios
依賴,以及定義了 API_BASE
後端接口根路由;
而後咱們定義並導出了兩個對象:
productActions
對象中定義了一些有關商品在視圖層分發對應的事件時,action
做出的響應,好比allProducts
,productById
,removeProduct
以及updateProduct
等等。manufacturerActions
對象中定義了一些有關製造商在視圖層分發對應的事件時,action
做出的響應,好比allManufacturers
,removeManufacturer
等等。咱們再次來到src/store/index.js
文件中,添加有關抽取Actions
邏輯以後的信息。
import Vue from 'vue'; import Vuex from 'vuex'; import { productGetters, manufacturerGetters } from './getters'; import { productMutations, cartMutations, manufacturerMutations } from './mutations'; import { productActions, manufacturerActions } from './actions'; Vue.use(Vuex); // ... actions: { ...productActions, ...manufacturerActions, } });
這裏咱們首先導入了actions.js
文件中導出的一些Action對象,並經過對象展開運算符在store
實例的actions
屬性中混入了不一樣類型的action
屬性,實現了Actions邏輯的抽取。
咱們在src/store/mutations.js
文件中又添加了一些mutation
屬性,用於用戶進行不一樣的操做進行本地數據的同步。
// ... 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); } }; // ...
上述添加的都是有關商品的mutation
屬性:UPDATE_PRODUCT
,UPDATE_PRODUCT_SUCCESS
,ADD_PRODUCT
以及ADD_PRODUCT_SUCCESS
分別表示更新商品信息,更新商品信息成功,添加商品以及添加商品成功。
這一節咱們學習瞭如何抽出Actions
邏輯,減輕store
實例中的負載:
actions
JS文件,在文件中定義不一樣類型的Actions
對象並導出,而後在Actions
對象中定義相應的一些屬性。store
的index
文件中導入這些Actions
對象,並在store
實例的actions
屬性中經過對象展開運算符混入這些對象。this.$store.actions.屬性
的方式調用。這一節咱們主要是進一步完善咱們的項目功能以及去掉一些硬編碼。
和商品信息展現功能同樣,咱們也須要將製造商信息展現部分封裝到一個單獨的組件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>
該組件經過父子組件傳值從父組件獲取到了model
和isEditing
的值,並將model
對象的信息展現在表單中。
表單信息中還經過v-if
來判斷isEditing
的值是true
仍是false
,若是是true
則建立Update Manufacturer
,反之建立Add Manufacturer
。
當用戶提交表單時觸發saveManufacturer
事件,此時會向父組件發送類型爲save-manufacturer
的事件通知其保存這次的修改操做。
在建立編輯製造商組件以前,咱們須要在getters文件中添加對應的getter屬性。
咱們在src/store/getters.js
文件的manufacturerGetters
對象中又添加了一個manufacturerById
方法,用於獲取本地中指定的製造商。
// ... export const manufacturerGetters = { allManufacturers(state) { return state.manufacturers; }, manufacturerById: (state, getters) => id => { if (getters.allManufacturers.length > 0) { return getters.allManufacturers.filter(manufacturer => manufacturer._id === id)[0] } else { return state.manufacturer; } } }
manufacturerById
方法中的id參數是Vue視圖層經過方法調用時傳入的id,經過這個id判斷本地中是否存在該製造商,若是存在則返回該製造商,若是不存在則返回一個空對象。
在建立了展現製造商信息的表單組件ManufacturerForm
以及添加了用於獲取本地指定製造商數據的getter屬性以後,緊接着咱們又建立了src/pages/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']); // 這裏返回 product 的拷貝,是爲了在修改 product 的拷貝以後,在保存以前不修改本地 Vuex stire 的 product 屬性 return { ...manufacturer }; } }, methods: { addManufacturer(model) { this.$store.dispatch('updateManufacturer', { manufacturer: model, }) }, }, components: { 'manufacturer-form': ManufacturerForm } } </script>
該組件剛被建立時將當前處於激活狀態的路由對象的id參數做爲載荷分發到類型爲manufacturerById
的action
中,在action
中進行異步操做從服務器獲取對應制造商,而後將該製造商提交到對應mutation
中進行本地狀態修改,將獲取到的製造商保存到本地。
咱們定義了計算屬性model
返回manufacturer
的拷貝,是爲了在修改manufacturer
的拷貝以後,在保存以前不修改本地 store
中的manufacturer
屬性。這裏以方法訪問的形式從getters
中經過當前激活的路由對象中的id參數獲取本地狀態中的對應制造商做爲manufacturer
的拷貝,並返回給計算屬性model
,而後傳給子組件ManufacturerForm
。
該組件在addManufacturer
事件中將子組件傳入的新制造商對象做爲載荷分發到類型爲updateManufacturer
的action
中,在action
中進行異步操做修改後端對應的商品信息,而後將新對象提交到對應的mutation
中進行本地狀態修改,修改本地狀態中的manufacturer
對象。
一樣的咱們繼續建立了src/pages/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>
該組件邏輯代碼與New.vue
組件相似,一個是添加商品組件,一個是添加製造商組件,您能夠對比着來看。
以前咱們在該入口文件中增長了查看生產商導航,這裏咱們又增長了添加生產商導航。
// ... <li> <router-link to="/admin/manufacturers">查看生產商</router-link> </li> <li> <router-link to="/admin/manufacturers/new">添加生產商</router-link> </li> </ul> </div> <router-view></router-view> // ...
咱們已經建立了添加和修改製造商組件以及添加了對應的入口導航,接着咱們須要在該文件中對其進行路由參數配置。
再次進入src/router/index.js
文件,咱們導入了添加製造商和修改製造商的組件並配置了相關路由參數。
// ... import Products from '@/pages/admin/Products'; import Edit from '@/pages/admin/Edit'; import Manufacturers from '@/pages/admin/Manufacturers'; import NewManufacturers from '@/pages/admin/NewManufacturers'; import EditManufacturers from '@/pages/admin/EditManufacturers'; Vue.use(Router); // ... name: 'Manufacturers', component: Manufacturers, }, { path: 'manufacturers/new', name: 'NewManufacturers', component: NewManufacturers, }, { path: 'manufacturers/edit/:id', name: 'EditManufacturers', component: EditManufacturers, }, ] }, { // ...
這裏添加製造商的路由配置就是靜態路由的配置方式;修改製造商的路由配置採用了動態傳參的方式,這裏使用的是$route.params
對象的Post
方式傳參。
配置好添加製造商和修改製造商的路由參數以後,咱們又能夠進行驗收啦,運行項目,點擊Admin而後再點擊添加製造商,咱們能夠看到添加製造商的表單: 而後再點擊查看製造商,咱們又能夠看到一系列的製造商,而且每一個製造商都對應的有修改和刪除操做,這裏咱們隨便選擇一個製造商進行修改,就會看到該製造商的表單信息:
這一節咱們將對咱們的項目代碼進行優化,幹掉一些硬編碼。
咱們都知道在actions
文件和mutations
文件中有一部分的事件類型是須要保持一致的,好比在咱們在視圖層分發一個添加商品事件ADD_PRODUCT
,在actions
文件中就須要有對應事件類型的action
接收,而後向後端發起請求並將請求結果提交到對應類型的mutation
中,這就要求了這幾個文件中的對應事件類型都要保持一致。但是咱們在開發過程當中不免會出錯,好比漏掉一個字母就會致使兩個文件中的對應事件沒法接收,尷尬的是控制檯也沒有報錯,這就形成了咱們很難查錯。
所以,咱們採用了字符串常量的形式定義actions
文件和mutations
文件中的事件類型,只要咱們寫錯一個單詞都會致使字符串常量不一致,關鍵的是這個時候會報錯,利於咱們查錯。
進而咱們建立了src/store/mutation-types.js
文件,用於定義一些字符串常量來表示各類事件類型,並導出這些字符串常量。
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';
咱們再次來到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, }) }) } }
這裏咱們首先導入了mutation-types
文件中定義的一些字符串常量,替換掉了對應的事件類型。
同actions文件同樣,咱們再次進入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; // ... return product; }) }, [ADD_PRODUCT](state) { state.showLoader = true; }, [ADD_PRODUCT_SUCCESS](state, payload) { state.showLoader = false; const { product } = payload; // ... }; 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); } }
這裏咱們首先導入了mutation-types
文件中定義的一些字符串常量,替換掉了對應的事件類型。
這一節咱們主要作了如下工做:
想要學習更多精彩的實戰技術教程?來 圖雀社區逛逛吧。本文所涉及的源代碼都放在了 Github 上,若是您以爲咱們寫得還不錯,但願您能給❤️這篇文章點贊+Github倉庫加星❤️哦