[Express+Vue 搭建電商網站] 13 使用 Axios 取得 API 資料

使用 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 是從組件分派的事件或動作名稱,接收兩個參數 contextpayload
context 具有跟 store 相同的方法與屬性,可以透過 context.commit 來提交一個 mutation 或是透過 context.statecontext.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,
 });
 })
 }
 }
});

我們總共做了幾件事情

  1. 導入 axios,定義了後端 API 的網址到 API_BASE
  2. 刪除 store 中的假資料,清空 products 陣列
  3. mutations 增加了 ALL_PRODUCTSALL_PRODUCTS_SUCCESS 方法,用來處理撈取後端資料的載入狀態以及資料。
  4. 最後加入了 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 方法觸發名叫 allProductsaction

為什麼我們這邊不使用 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 中的商品列表和前端的資料比對。
取得的資料和先前一樣,可以加入與移出購物車。

留言