隨著前端應用的日漸複雜,狀態和資料管理成為了構建大型應用的關鍵。受 Redux 等專案的啟發,Vue.js 團隊也量身定做了狀態管理庫 Vuex。在這篇教程中,我們將帶你熟悉 Store、Mutation 和 Action 三大關鍵概念,並升級迷你商城應用的前端程式碼。
歡迎閱讀《從零到部署:用 Vue 和 Express 實現迷你全棧電商應用》系列:
- 《 從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(一)》
- 《 從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(二)》
- 《 從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(三)》
- 《 從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(四)》(也就是這篇)
- 《 從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(五)》
- 《 從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(六)》
- 《 從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(七)》
- 《 從零到部署:用 Vue 和 Express 實現迷你全棧電商應用(終篇)》
如果您覺得我們寫得還不錯,記得 點贊 + 關注 + 評論 三連,鼓勵我們寫出更好的教程?
使用 Vuex 進行狀態管理
我們在第一篇和第三篇中講解了 Vue 的基礎部分,利用這些知識你已經可以實現一些比較簡單的應用了。但是針對複雜的應用,比如元件巢狀超過三級,我們前面講解的知識處理起來就很費力了,還好 Vue 社群為我們打造了狀態管理容器 Vuex,用來處理大型應用的資料和狀態管理。
安裝 Vuex 依賴
首先我們開啟命令列,進入專案目錄,執行如下命令安裝 Vuex:
npm install vuex
複製程式碼
建立 Vuex Store
Vuex 是一個前端狀態管理工具,它致力於接管 Vue 的狀態,使得 Vue 專心做好渲染頁面的事情;它類似在前端建立了一個 “資料庫”,然後將所有的前端狀態都儲存在這個 “資料庫” 裡面。這個 “資料庫” 其實就是一個普通的 JavaScript 物件。
好了,講述了 Vuex 是幹什麼之後,我們來看一下如何在 Vue 中運用 Vuex。Vuex 建立的這個 “資料庫” 一般用術語 store
來表示,通常我們建立一個單獨的 store
資料夾,用於儲存和 store
有關的內容。我們在 src
資料夾下建立 store
資料夾,然後在裡面建立 index.js
檔案,程式碼如下:
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: [
{
name: '1',
}
],
// all manufacturers
manufacturers: [],
}
})
複製程式碼
上面的程式碼可以分為三部分。
- 首先我們匯入
Vue
和Vuex
- 然後我們呼叫
Vue.use
方法,告訴 Vue 我們將使用Vuex
,這和我們之前使用Vue.use(router)
的原理一樣 - 最後我們匯出
Vuex.Store
例項,並且傳入了strict
和state
引數。這裡strict
參數列示,我們必須使用 Vuex 的 Mutation 函式來改變state
,否則就會報錯(關於 Mutation 我們將在 “使用 Vuex 進行狀態管理” 一節講解)。而state
引數用來存放我們全域性的狀態,比如我們這裡定義了cart
、showLoader
等屬性都是後面我們完善應用的內容需要的資料。
將 Vuex 和 Vue 整合
當我們建立並匯出了 Vuex 的 store
例項之後,我們就可以使用它了。開啟 src/main.js
檔案,在開頭匯入之前建立的 store
,並將 store
新增到 Vue 初始化的引數列表裡,程式碼如下:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue';
import { ValidationProvider } from 'vee-validate';
import App from './App';
import router from './router';
import store from './store';
Vue.config.productionTip = false;
Vue.component('ValidationProvider', ValidationProvider);
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>',
});
複製程式碼
可以看到,在上面的檔案中,我們一開頭匯入了我們之前在 src/store/index.js
裡定義的 store
例項,接著,在 Vue 例項初始化時,我們將這個 store
例項使用物件屬性簡潔寫法新增到了引數列表裡。
當我們將 store
當做引數傳給 Vue 進行初始化之後,Vue 就會將 Store 裡面的 state
注入到所有的 Vue 元件中,這樣所有的 Vue 元件共享同一個全域性的 state
,它其實就是一個 JS 物件,應用中所有狀態的變化都是對 state
進行操作,然後響應式的觸發元件的重新渲染,所以這裡的 state
也有 “資料的唯一真相來源” 的稱謂。
這種將狀態儲存到一個全域性的 JavaScript 物件 -- state 中,然後所有的增、刪、改、查操作都是對這個 JavaScript 物件進行,使得我們可以避免元件巢狀層級過深時,元件之間傳遞屬性的複雜性,讓屬性的定義,獲取,修改非常直觀,方便開發大型應用和團隊協作。
檢視 Vuex 整合後的效果
在將 Vuex 和 Vue 整合好之後,我們馬上來看一下 Vuex 帶來的效果,不過在此之前我們先來講一講什麼是計算屬性(computed
)。
計算屬性(Computed)
首先我們新增了 script
部分,然後在匯出的物件裡面增加了一個 computed
屬性,這個屬性裡面的內容用於申明一些可能要在 template
裡面使用的複雜表示式。我們來看一個例子來講解一下 computed
屬性:
我們在模板中可能要獲取一個多級巢狀物件裡面的某個資料,或者要渲染的資料需要經過複雜的表示式來計算,比如我們要渲染這樣一個資料 obj1.obj2.obj3.a + obj1.obj4.b
,寫在模板裡就是這樣的:
<template>
<div>
{{ obj1.obj2.obj3.a + obj1.obj4.b }}
</div>
</template>
<script>
export default {
data: {
obj1: {
obj2: {
obj3: {
a
}
},
obj4: {
b
}
}
}
}
</script>
複製程式碼
可以看到,我們一眼看上去,這個模板裡面有這樣一個複雜的表示式,很不容易反應出來它到底要渲染什麼,這樣程式碼的可讀性就很差,所以 Vue 為我們提供了計算屬性( computed
),用於用簡單的變數來代表複雜的表示式結果,進而簡化模板中插值的內容,讓我們的模板看起來可讀性更好,上面的程式碼使用計算屬性來改進會變成下面這樣:
<template>
<div>
{{ addResult }}
</div>
</template>
<script>
export default {
data: {
obj1: {
obj2: {
obj3: {
a
}
},
obj4: {
b
}
}
},
computed: {
addResult() {
return this.obj1.obj2.obj3.a + this.obj1.obj4.b
}
}
}
</script>
複製程式碼
可以看到,當我們使用了計算屬性 addResult
之後,我們在模板裡面的寫法就簡化了很多,而且一目瞭然我們是渲染了什麼。
瞭解了計算屬性之後,我們開啟 src/pages/admin/Products.vue
,對內容作出如下改進以檢視 Vuex 和 Vue 整合之後的效果:
<template>
<div>
<div class="title">
<h1>This is Admin</h1>
</div>
<div class="body">
{{ product.name }}
</div>
</div>
</template>
<script>
export default {
computed: {
product() {
return this.$store.state.products[0];
}
}
}
</script>
複製程式碼
可以看到,上面的內容改進主要分為兩個部分:
- 首先我們定義了一個
product
計算屬性,它裡面返回一個從store
中儲存的state
取到的products
陣列的第一個元素,注意到當我們在 “將 Vuex 和 Vue 整合” 這一小節中將store
作為 Vue 初始化例項引數,所以我們在所有的 Vue 元件中可以通過this.$store.state
的形式取到 Vuex Store 中儲存的state
。 - 接著我們使用了計算屬性
product
,取到了它的name
屬性進行渲染。
小結
在這一小節中,我們學習瞭如何將 Vuex 整合進 Vue 中:
- 首先我們安裝了
vuex
依賴 - 接著我們在
src
下面建立了store
資料夾,用於儲存 Vuex 相關的內容,在store
檔案下之下,我們建立了index.js
檔案,在裡面例項化了Vuex.Store
類,我們在例項化的過程中傳遞了兩個引數:strict
和state
,strict
表示我們告訴 Vue,只允許Mutation
方法才能修改state
,確保修改狀態的唯一性;state
是我們整個應用的狀態,整個應用的狀態都是從它獲取,整個應用狀態的改變都是修改它,所以這個state
也有 “資料的唯一真相來源” 的稱謂。 - 然後我們通過在
main.js
裡面匯入例項化的store
,將它加入到初始化 Vue 例項的引數列表中,實現了 Vuex 和 Vue 的整合。 - 最後我們講解了計算屬性,然後通過在計算屬性中獲取
this.$store.state
的方式展示了 Vuex 整合之後的效果。
好了,我們已經整合了 Vuex,並在 Vue 元件中獲取了儲存在 Vuex Store 中的狀態(state),接下來我們來看一下如何修改這個狀態。
使用 Mutation 修改本地狀態
我們在上一節中定義了 Vuex Store,並在裡面儲存了全域性的狀態 state
,這一節我們來學習如何修改這一狀態。
理解 Mutation:修改狀態的唯一手段
Vuex 為我們提供了 Mutation
,它是修改 Vuex Store 中儲存狀態的唯一手段。
Mutation 是定義在 Vuex Store 的 mutation
屬性中的一系列形如 (state, payload) => newState
的函式,用於響應從 Vue 檢視層發出來的事件或動作,例如:
ACTION_NAME(state, payload) {
// 對 `state` 進行操作以返回新的 `state`
return newState;
}
複製程式碼
其中方法名 ACTION_NAME
用於對應從檢視層裡面發出的事件或動作的名稱,這個函式接收兩個引數 state
和 payload
,state
就是我們 Vuex Store 中儲存的 state
,payload
是被響應的那個事件或動作攜帶的引數,然後我們通過 payload
的引數來操作現有的 state
,返回新的 state
,通過這樣的方式,我們就可以響應修改 Vuex Store 中儲存的全域性狀態。
瞭解了 Mutation 的概念之後,我們馬上來看一下如何運用它。
初始化狀態(硬編碼)
我們開啟 src/store/index.js
檔案,修改其中的 state
並加上 mutations
如下:
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',
description: 'iPhone是美國蘋果公司研發的智慧手機系列,搭載蘋果公司研發的iOS作業系統',
image: 'https://i.gadgets360cdn.com/large/iPhone11_leak_1567592422045.jpg',
price: 2000,
manufacturer: 'Apple Inc'
},
{
_id: '2',
name: '榮耀20',
description: '李現同款 4800萬超廣角AI四攝 3200W美顏自拍 麒麟Kirin980全網通版8GB+128GB 藍水翡翠 全面屏手機',
image: 'https://article-fd.zol-img.com.cn/t_s640x2000/g4/M08/0E/0E/ChMlzF2myueILMN_AAGSPzoz23wAAYJ3QADttsAAZJX090.jpg',
price: 2499,
manufacturer: '華為'
},
{
_id: '3',
name: 'MIX2S',
description: '驍龍845 全面屏NFC 遊戲智慧拍照手機 白色 全網通 6+128',
image: 'http://himg2.huanqiu.com/attachment2010/2018/0129/08/39/20180129083933823.jpg',
price: 1688,
manufacturer: '小米'
},
{
_id: '4',
name: 'IQOO Pro',
description: '12GB+128GB 競速黑 高通驍龍855Plus手機 4800萬AI三攝 44W超快閃充 5G全網通手機',
image: 'https://www.tabletowo.pl/wp-content/uploads/2019/08/vivo-iqoo-pro-5g-blue-1.jpg',
price: 4098,
manufacturer: 'Vivo'
},
{
_id: '5',
name: 'Reno2',
description: '【12期免息1年碎屏險】4800萬變焦四攝8+128G防抖6.5英寸全面屏新 深海夜光(8GB+128GB) 官方標配',
image: 'https://news.maxabout.com/wp-content/uploads/2019/08/OPPO-Reno-2-1.jpg',
price: 2999,
manufacturer: 'OPPO'
}
],
// 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
中的products
屬性,在裡面儲存一開始我們的迷你電商平臺的初始資料,這裡我們是硬編碼到程式碼中的,在下一節 “使用 Action 獲取遠端資料”中,我們將動態獲取後端伺服器的資料。 - 接著我們在
Vuex.Store
例項化的引數中新增了一個mutations
屬性,在裡面定義了兩個函式ADD_TO_CART
和REMOVE_FROM_CART
,分別代表響應從檢視層發起的對應將商品新增至購物車和從購物車移除商品的動作。
建立 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
部分:
- 先定義了一個計算屬性
products
,通過this.$store.state.products
從本地狀態中獲取到了products
陣列,並作為計算屬性products
的返回值 - 然後定義了一個點選事件
addToCart
,並且傳入了當前處於啟用狀態的product
引數。當使用者點選“新增購物車”時,觸發addToCart
事件,也就是上面所說的檢視層發出的事件。這裡是通過this .$store.commit
將攜帶當前商品的物件{product}
作為載荷提交到型別為ADD_TO_CART
的mutation
中,在mutation
中進行本地狀態修改,我們會在後面抽離出的mutations
檔案中看到具體的操作。
再看該元件的 template
部分,使用 v-for
將從本地獲取到的 products
陣列進行遍歷,每個 product
物件的詳細資訊都會顯示在模板中。此外,我們還在每個 product
物件資訊的最後新增了一個“加入購物車”的按鈕,允許我們將指定商品新增到購物車。
在頁面中接入資料
Store 和元件都搞定之後,我們就可以在之前的頁面中接入資料了。修改主頁 src/pages/Home.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>
複製程式碼
可以看到,我們在匯入 ProductList
元件後,將其註冊到 components
中,然後在模板中使用這個元件。
接著修改購物車頁面 src/pages/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>
複製程式碼
我們在該元件中主要增加了兩部分程式碼:
-
首先是
script
部分,我們增加了一個計算屬性和一個點選事件。同樣是通過this.$store.state.cart
的方式從本地狀態中獲取購物車陣列,並作為計算屬性cart
的返回值;當使用者點選購物車中的某個商品將其移除購物車時就會觸發removeFromCart
事件,並且將要移除的商品 id 作為引數傳入,然後也是通過this.$store.commit
的方式將包含productId
的物件作為載荷提交到型別為REMOVE_FROM_CART
的mutation
中,在mutation
中進行本地狀態修改,具體修改操作我們可以在後面抽離出的mutations
檔案中看到。 -
然後是
template
部分,我們通過v-for
遍歷了購物車陣列,將購物車中的所有商品資訊展示在模板中。並在每個商品資訊的最後新增了一個移除購物車的按鈕,當使用者希望移除購物車中指定商品時,會觸發removeFromCart
事件。
檢視效果
在專案根目錄下執行 npm start
,進入開發伺服器檢視效果:
可以看到,一開始我們的購物車是空的,然後隨便選了兩款手機,點選“加入購物車”,然後就可以在購物車頁面看到了!我們還可以將購物車中的商品移除。
小結
在這一部分中我們學習瞭如何發起修改本地狀態的“通知”:
- 首先我們需要在
Vuex.Store
例項化的引數中新增一個mutations
屬性,在該屬性中新增對應的方法,比如ADD_TO_CART
和REMOVE_FROM_CART
。 - 然後我們需要通過使用者不同的操作(比如點選新增購物車或者移除購物車)來發起“通知”,進而通過
this.$store.commit
的方式將需要操作的物件作為載荷提交到對應型別(也就是ADD_TO_CART
和REMOVE_FROM_CART
)的mutation
中,在mutation
中進行本地狀態修改。
使用 Action 獲取遠端資料
我們在上一節中學習瞭如何在檢視層發起本地狀態修改的“通知”,這一節我們來學習如何從後端獲取遠端資料。請求庫我們採用的是 axios,通過以下命令安裝依賴:
npm install axios
複製程式碼
理解 Action:非同步操作
Vuex 為我們提供了 Action
,它是用來進行非同步操作,我們可以在這裡向後端發起網路資料請求,並將請求到的資料提交到對應的 mutation
中。
Action 是定義在 Vuex Store 的 action
屬性中的一系列方法,用於響應從 Vue 檢視層分發出來的事件或動作,一個 Action 是形如 (context, payload) => response.data
的函式:
productById(context, payload) {
// 進行非同步操作,從後端獲取遠端資料並返回
return response.data;
}
複製程式碼
其中:
- 函式名
productById
用於對應從檢視層裡面分發出的事件或動作的名稱 - 函式接收兩個引數
context
和payload
context
指的是action
的上下文,與store
例項具有相同的方法和屬性,因此我們可以呼叫context.commit
提交一個mutation
,或者通過context.state
和context.getters
來獲取state
和getters
,但是context
物件又不是store
例項本身payload
是分發時攜帶的引數,然後我們通過payload
中的引數來進行非同步操作,從而獲取後端響應資料並返回。這樣我們就可以根據使用者的操作同步更新後端資料,並將後端響應的資料提交給mutation
,然後利用mutation
進行本地資料更新。
實現第一個 Action
讓我們趁熱打鐵,實現第一個 Action。再次來到 src/store/index.js
檔案,修改程式碼如下:
import Vue from 'vue';
import Vuex from 'vuex';
import axios from 'axios';
const API_BASE = 'http://localhost:3000/api/v1';
Vue.use(Vuex);
export default new Vuex.Store({
strict: true,
state: {
// bought items
cart: [],
// ajax loader
showLoader: false,
// selected product
product: {},
// all products
products: [],
// 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)
},
ALL_PRODUCTS(state) {
state.showLoader = true;
},
ALL_PRODUCTS_SUCCESS(state, payload) {
const { products } = payload;
state.showLoader = false;
state.products = products;
}
},
actions: {
allProducts({ commit }) {
commit('ALL_PRODUCTS')
axios.get(`${API_BASE}/products`).then(response => {
console.log('response', response);
commit('ALL_PRODUCTS_SUCCESS', {
products: response.data,
});
})
}
}
});
複製程式碼
可以看到,我們做了以下幾件事:
- 匯入了
axios
,並定義了API_BASE
後端介面根路由; - 我們在
store
中去掉了之前硬編碼的假資料,使products
預設值為空陣列; - 然後在
mutations
屬性中新增了ALL_PRODUCTS
和ALL_PRODUCTS_SUCCESS
方法,用來響應action
中提交的對應型別事件;ALL_PRODUCTS
將state.showLoader
設為true
,顯示載入狀態;ALL_PRODUCTS_SUCCESS
將action
中提交的資料儲存到state
中,並取消載入狀態; - 最後新增了
actions
屬性,在actions
屬性中定義了allProducts
函式用於響應檢視層分發的對應型別的事件;我們首先提交了型別為ALL_PRODUCTS
的mutation
,接著在axios
請求成功後提交ALL_PRODUCTS_SUCCESS
,並附帶products
資料體(payload
)
提示
我們可以看到
allProducts
方法中傳入了{ commit }
引數,這是採用瞭解構賦值的方式const { commit } = context
,避免後面使用context.commit
過於繁瑣。
更新 ProductList 元件
再來看 src/components/products/ProductList.vue
檔案,我們對其做了修改,主要新增了生命週期函式 created
,在該元件剛被建立時首先判斷本地 products
中是否有商品,如果沒有就向後端發起網路請求獲取資料。程式碼如下:
<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.manufacturer">生產廠商:{{product.manufacturer.name}}</p>
<img :src="product.image" alt="" class="product__image">
<button @click="addToCart(product)">加入購物車</button>
</div>
</template>
</div>
</div>
</template>
<!-- style -->
<script>
export default {
name: 'product-list',
created() {
if (this.products.length === 0) {
this.$store.dispatch('allProducts')
}
},
computed: {
// ...
},
methods: {
// ...
}
}
</script>
複製程式碼
注意到我們修改了兩個地方:
- 調整模板中“生產廠商”欄位,把
{{product.manufacturer}}
修改為{{product.manufacturer.name}}
- 新增
created
生命週期方法,在該元件剛被建立時判斷this.products.length === 0
是true
還是false
,如果是true
則證明本地中還沒有任何商品,需要向後端獲取商品資料,於是通過this.$store.dispatch
的方式觸發型別為allProducts
的action
中,在action
中進行非同步操作,發起網路請求向後端請求商品資料並返回;如果是false
則證明本地中存在商品,所以可以直接從本地獲取然後進行渲染。
最後我們也同樣需要調整一下 src/pages/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.manufacturer">生產廠商:{{product.manufacturer.name}}</p>
<img :src="product.image" alt="" class="product__image">
<button @click="removeFromCart(product._id)">從購物車中移除</button>
</div>
</template>
</div>
</template>
<!-- style -->
<!-- script -->
複製程式碼
同樣把 {{product.manufacturer}}
修改為 {{product.manufacturer.name}}
。
檢視效果
在測試這一步效果之前,首先確保 MongoDB 和後端 API 伺服器已經開啟。同時,如果你之前沒有在第二篇教程中測試過,很有可能你的資料庫是空的,那麼可以下載我們提供的 MongoDB JSON 資料檔案 manufacturers.json 和 products.json,然後執行以下命令:
mongoimport -d test -c manufacturers manufacturers.json
mongoimport -d test -c products products.json
複製程式碼
然後再進入前端測試,你應該就可以看到從後臺獲取到的資料,然後同樣可以新增到購物車哦!
小結
在這一部分中我們學習瞭如何使用 Action
獲取遠端資料,並將獲取的資料提交到對應的 Mutation
中:
- 首先我們需要匯入相關依賴:
axios
和API_BASE
,由於發起網路請求。 - 其次我們需要在
store
例項中新增actions
屬性,並在actions
屬性定義對應的方法,用於響應檢視層分發的對應型別的事件。 - 在不同的方法中發起不同的網路請求,你是需要從後端獲取資料,還是修改後端資料等等。然後將後端響應的資料結果提交到對應型別的
mutation
中。
在下一篇教程中,我們將進一步探索 Vue 元件化,從而簡化頁面邏輯,並抽出 Getters 和 Mutation 資料邏輯。
想要學習更多精彩的實戰技術教程?來圖雀社群逛逛吧。