在鐵人賽即將過半之際,終於要來串接 API 顯示真實的書單資料了!不過在此之前,我們應該先來了解一下 Vue.js 的生命週期。
每個 Vue 實例在被創建時都會經歷一連串的初始化過程,這期間可能會需要設置 data、編譯 <template> 內容、掛載實例、監聽數據變化同步更新數據和畫面等等;Vue.js 將這個過程拆分成四個主要階段——created
、mounted
、updated
、destroyed
,另外還有在這四個階段之前的前置階段(beforeCreate
、beforeMounted
、beforeUpdated
、beforeDestroyed
),每個階段都各有 hook function 可被調用。
完整的生命週期可以在實作中慢慢認識,本篇先單論 created
和 mounted
的區別:
created
:實例已創建,完成初始化資料
mounted
:實例已掛載,完成編譯 <template> 內容
Vue.js 官方本身有推薦可以使用 axios 來作為串接 API 的工具,並在文件中示範在 mounted
階段發送 API。
不過,由於 created
為負責處理資料初始化的階段,通常更適合在此階段向後端 API 獲取資料並將其設置為 data 屬性,且串接 API 設置資料與操作 DOM 無關,因此可以不用等到 mounted
才執行,所以在以下範例會提前在 created
發送 API。
那麼先安裝好 axios 之後,我們嘗試在 All.vue 中取得所有書目資料,再利用模板語法整理出要顯示的資料內容並稍加排版。(若想先測試是否能成功取得資料,也可以透過 <pre>
標籤將 API 原始 JSON 格式內容顯示在畫面上進行初步確認)
<div class="All">
<h1>All books</h1>
<div class="bookshelf">
<!-- <pre>{{ bookList }}</pre> -->
<div class="book" v-for="book in bookList" :key="book.id">
<img :src="book.image" alt="book image" />
<p>
原價:<span>{{ book.originPrice }}</span> |
特價:<span class="bargain">{{ book.sellPrice }}</span>
</p>
<p>ISBN:<span>{{ book.ISBN }}</span></p>
<p><span>{{ book.name }}</span></p>
<a :href="book.link" target="_blank">連結</a>
</div>
</div>
</div>
import axios from "axios";
export default {
name: "All",
data() {
return {
bookList: "",
};
},
created() {
axios
.get("https://bookshelf.goodideas-studio.com/api")
.then((response) => (this.bookList = response.data));
},
};
先不論版面美醜,把資料串起來之後,看起來就煞有其事了吧!
等到之後認識其他 Vue Router 的導航守衛(Navigation Guards),會發現其實還有其他可以發送 API 的時機點,當然一切仍需視情況而定,也可以思考專案想要給使用者什麼樣的 UX 體驗。
目前在 created
或 mounted
階段都是屬於在元件內的生命週期,兩者的分水嶺為 <template> 渲染成 HTML 的之前或之後;若是將判斷時機改為透過路由控制,實現方式則可再分成要在完成導航之前或之後獲取數據:
導航之前獲取數據:在進入路由之前,先透過全域/路由/元件內的導航守衛發送 API
導航守衛為異步執行,所以使用者會停留在當前頁面,等數據獲取完畢之後才會進入路由,此時可以在過渡期間顯示進度條或相關指示,如遇數據獲取失敗也可彈出錯誤警示框以提升使用者體驗。
導航之後獲取數據:等進入路由之後,在 created 階段發送 API
生命週期的 Hook 為同步執行,所以固定內容會照常先進行渲染,使用者體驗上就不會發生因為資料串接過程卡住或錯誤而產生全白畫面的情況;而等待獲取數據的期間還可以額外展示 loading 效果,讓使用者可以辨識出部分區塊內容是在等待資料串接的過程。