使用 Axios 取得 API 資料
首先先安裝 axios
npm i axios
在 Vuex store 中可以使用 action 屬性,乍看之下跟 mutation
類似,不同的地方在於
- Action 提交的是 mutation,而不是直接變更狀態
- Action 可以包含任意異步操作
用於響應 Vue 組件中分派的事件或動作,一個action
是類似於(context, payload) => response.data
的函數:
productById(context, payload) {
// 異步操作,從後端取得資料
return response.data;
}
其中 productById
是從組件分派的事件或動作名稱,接收兩個參數 context
、payload
context
具有跟 store
相同的方法與屬性,可以透過 context.commit
來提交一個 mutation
或是透過 context.state
和 context.getters
来取得 state 和 getters
payload
就是攜帶的參數,可以透過他來執行異步操作,從而取得後端資料並回傳。
所以我們可以在 Action
中異步執行 axios
來抓取後端 API 的資料,取得回傳結果後將結果提交給 mutation
,並更新使用者端的資料。
實現 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 的網址到
API_BASE
- 刪除 store 中的假資料,清空
products
陣列 - mutations 增加了
ALL_PRODUCTS
和ALL_PRODUCTS_SUCCESS
方法,用來處理撈取後端資料的載入狀態以及資料。 - 最後加入了
actions
屬性,定義了allProducts
函數,用來響應組件的對應事件。首先提交了ALL_PRODUCTS
mutation,接著在axios
取得後端資料後提交了ALL_PRODUCTS_SUCCESS
,並且把取得的資料命名為products
一併作為payload
傳入。
提示:
可以看到在 allProducts
中我們傳入了 { commit }
參數,這地方使用了解構賦值 const { commit } = context
,代替比較長的 context.commit
。
因為目前我們沒用到其他 context
屬性,所以這麼做是可以的
更新組件
ProductList
將 src/components/products/ProductList.vue
的 <script>
區塊改成以下
<script>
export default {
name: "product-list",
created() {
if (this.products.length === 0) {
this.$store.dispatch("allProducts");
}
},
computed: {
// a computed getter
products() {
return this.$store.state.products;
}
},
methods: {
addToCart(product) {
this.$store.commit("ADD_TO_CART", {
product
});
}
}
};
</script>
增加了一個 created()
生命週期方法,在這個組件被建立時判斷使用者端是否有商品資料,若是沒有,則需要跟後端 API 要資料。
於是通過 this.$store.dispatch
方法觸發名叫 allProducts
的 action
。
為什麼我們這邊不使用 commit
操作 mutation
而是使用 dispatch
呢?
是因為 mutation
必須是一個同步執行的程式,而這邊是一個異步請求,需要使用 dispatch
來操作 Action
進行異步請求。
再來是 <template>
,我們做部分修改,讓畫面渲染時的資料符合當初後端 API 定義的格式
<p class="product.manufacturer">生產商:{{product.manufacturer.name}}</p>
Cart
接著打開 src/views/Cart.vue
頁面,因為上面也有顯示生產商的名稱,同樣把 {{product.manufacturer}}
改成 {{product.manufacturer.name}}
測試結果
首先確認先前的後端 API 專案已經啟動,並且 MongoDB 也在運行中,如果先前在撰寫 API 時你沒有進行測試,則資料庫應該為空。
先去加一些資料吧!
完成之後進入前端測試,應該就可以發現商品的資料就是後端 API 傳來的資料。
如果不確定的話,可以照著先前的教學,使用 Postman 取得 API 中的商品列表和前端的資料比對。
取得的資料和先前一樣,可以加入與移出購物車。
留言
張貼留言