Nuxt 在最新發布的 2.12 版本中引入了新的 fetch
。Fetch 提供了一種將資料帶入 Nuxt 應用程式的全新方式。
在這篇文章中,我們將探索 fetch hook 的不同功能,並嘗試了解它的運作方式。
Fetch Hook 與 Nuxt 生命周期
就 Nuxt 生命周期 hooks 而言,fetch
位於 Vue 生命周期中 created
hook 之後。正如我們已經知道的那樣,所有 Vue 生命周期 hooks 都使用它們的 this
context 呼叫。fetch
hook 也適用相同規則。
Fetch hook 在 component 實例於 server-side 建立後呼叫。這使得 this
context 在 fetch
內部可用。
export default {
fetch() {
console.log(this)
}
}
讓我們看看這對 page components 可能意味著什麼。
Page Components
藉由 this
context 的幫助,fetch 能夠直接修改 component 的資料。這表示我們可以設定 component 的 local data,而無需從 page component dispatch Vuex store action 或 commit mutation。
因此,Vuex 變成可選的,但並非不可能使用。如果需要,我們仍然可以像平常一樣使用 this.$store
來存取 Vuex store。
fetch hook 的可用性
透過 fetch
,我們可以在任何 Vue components 中非同步預先載入資料。這表示,除了在 /pages
目錄中找到的 page components 之外,在 /layouts
和 /components
目錄中找到的所有其他 .vue
components 也可以受益於 fetch hook。
讓我們看看這對 layout 和 building-block components 可能意味著什麼。
Layout Components
使用新的 fetch
,現在我們可以從 layout components 直接進行 API 呼叫。這在 v2.12 版本發布之前是不可能的。
可能的用例
- 在 Nuxt layouts 中從後端 fetch config data,以動態生成 footer 和 navbar
- 在 navbar 中 fetch user 相關資料 (例如:user profile、shopping-cart item count)
- 在
layouts/error.vue
上 fetch site 相關資料
Building-block (Child/Nested) Components
由於 fetch
hook 在 child components 中也可用,我們可以將一些 data-fetching 任務從 page-level components 卸載,並將它們委派給 nested components。這在 v2.12 版本發布之前也是不可能的。
這在很大程度上減輕了 route-level components 的責任。
可能的用例 - 我們仍然可以將 props 傳遞給 child components,但如果 child components 需要有自己的 data-fetching 邏輯,現在他們可以做到了!
多個 fetch hooks 的呼叫順序
由於每個 component 都可以有自己的 data-fetching 邏輯,您可能會問它們各自的呼叫順序是什麼?
Fetch hook 在 server-side 呼叫一次 (在對 Nuxt 應用程式的第一個請求時),然後在 client-side,當導航到更遠的路徑時。但是由於我們可以為每個 component 定義一個 fetch hook,因此 fetch hooks 會按照它們的層次結構順序呼叫。
在 server-side 停用 fetch
此外,如果需要,我們甚至可以在 server-side 停用 fetch。
export default {
fetchOnServer: false
}
這樣,fetch hook 將只會在 client-side 呼叫。當 fetchOnServer
設定為 false 時,$fetchState.pending
會在 component 於 server-side 渲染時變成 true
。
錯誤處理
新的 fetch
在 component level 處理錯誤。讓我們看看如何處理。
由於我們正在非同步 fetch 資料,新的 fetch()
提供了一個 $fetchState
object 來檢查請求是否已完成並成功進行。
以下是 $fetchState
object 的樣子。
$fetchState = {
pending: true | false,
error: null | {},
timestamp: Integer
};
我們有三個 keys,
- Pending - 讓您在 client-side 呼叫 fetch 時顯示 placeholder
- Error - 讓您顯示錯誤訊息
- Timestamp - 顯示上次 fetch 的 timestamp,這對於使用
keep-alive
進行快取非常有用
這些 keys 然後直接在 component 的 template area 中使用,以在從 API fetch 資料的過程中顯示相關的 placeholders。
<template>
<div>
<p v-if="$fetchState.pending">Fetching posts...</p>
<p v-else-if="$fetchState.error">Error while fetching posts</p>
<ul v-else>
…
</ul>
</div>
</template>
當錯誤發生在 component-level 時,我們可以透過在 fetch hook 中檢查 process.server
並接著使用 throw new Error()
statement 在 server-side 設定 HTTP status code。
async fetch() {
const post = await fetch(`https://jsonplaceholder.typicode.com/posts/${this.$route.params.id}`)
.then((res) => res.json())
if (post.id === this.$route.params.id) {
this.post = post
} else {
// set status code on server and
if (process.server) {
this.$nuxt.context.res.statusCode = 404
}
// use throw new Error()
throw new Error('Post not found')
}
}
以這種方式設定 HTTP status code 對於正確的 SEO 很有用。
Fetch 作為一個方法
新的 fetch hook 也充當一個方法,可以根據使用者互動或從 component methods 以程式方式調用。
<!-- from template in template -->
<button @click="$fetch">Refresh Data</button>
// from component methods in script section
export default {
methods: {
refresh() {
this.$fetch()
}
}
}
使 Nuxt pages 更加高效
我們可以利用 :keep-alive-props
prop 和 activated
hook,透過新的 fetch hook 使 Nuxt page components 更加高效。
Nuxt 允許在記憶體中快取一定數量的 pages 以及它們 fetch 的資料。並且還允許在我們重新 fetch 資料之前加入一定的秒數。
為了使上述任何方法都能運作,我們必須在通用的 <nuxt />
和 <nuxt-child>
components 中使用 keep-alive
prop。
<template>
<div>
<nuxt keep-alive />
</div>
</template>
此外,我們可以將 :keep-alive-props
傳遞給 <nuxt />
component,以快取一定數量的 pages 以及它們 fetch 的資料。
:keep-alive-props
prop 允許我們指示在我們在網站內其他地方導航時,應該在記憶體中保留的最大 pages 數量。
<nuxt keep-alive :keep-alive-props="{ max: 10 }" />
以上是一種提升 page 效能的方法,它更為高階和通用,而下一個方法則深入研究,透過使用 $fetchState
的 timestamp
property 並將其與重新 fetch 資料之前的秒數延遲進行比較,來優化 fetch request 呼叫。
Vue 的 activated
hook 在此與 Nuxt 的 keep-alive
prop 一起使用,以重新 fetch 資料。
export default {
activated() {
// Call fetch again if last fetch more than a minute ago
if (this.$fetchState.timestamp <= Date.now() - 60000) {
this.$fetch()
}
}
}
asyncData 與 Fetch
就 page components 而言,新的 fetch
看起來與 asyncData()
非常相似,因為它們都處理 local data。但以下有一些值得注意的關鍵差異。
截至 Nuxt 2.12,asyncData method 仍然是一個 active feature。讓我們檢查 asyncData
和新的 fetch
之間的一些關鍵差異。
asyncData
asyncData
僅限於 page-level componentsthis
context 不可用- 透過 return 資料來新增 payload
export default {
async asyncData(context) {
const data = await context.$axios.$get(
`https://jsonplaceholder.typicode.com/todos`
)
// `todos` does not have to be declared in data()
return { todos: data.Item }
// `todos` is merged with local data
}
}
New Fetch
fetch
在所有 Vue components 中都可用this
context 可用- 簡單地 mutate local data
export default {
data() {
return {
todos: []
}
},
async fetch() {
const { data } = await axios.get(
`https://jsonplaceholder.typicode.com/todos`
)
// `todos` has to be declared in data()
this.todos = data
}
}
Nuxt 2.12 之前的 Fetch
如果您使用 Nuxt 一段時間了,那麼您會知道以前版本的 fetch
有顯著的不同。
這是一個 breaking change 嗎?
不,它不是。實際上,舊的 fetch 仍然可以透過傳遞
context
作為第一個參數來使用,以避免在您現有的 Nuxt 應用程式中產生任何 breaking changes。
以下是 fetch
hook 與 v2.12 之前和之後相比的 notable changes 列表。
1. fetch hook 的呼叫順序
之前 - fetch hook 在初始化 component 之前呼叫,因此 this
在 fetch hook 內部不可用。
之後 - fetch
在 component 實例於 server-side 建立後,當 route 被存取時呼叫。
2. this 與 context
之前 - 我們可以存取 page-level components 上的 Nuxt context,前提是 context
作為第一個參數傳遞。
export default {
fetch(context) {
// …
}
}
之後 - 我們可以像 Vue client-side hooks 一樣存取 this
context,而無需傳遞任何參數。
export default {
fetch() {
console.log(this)
}
}
3. fetch hook 的可用性
之前 - 只有 page (route-level) components 被允許在 server-side fetch 資料。
之後 - 現在,我們可以在任何 Vue components 中非同步預先載入資料。
4. fetch hook 的呼叫順序
之前 - fetch
可以在 server-side 呼叫一次 (在對 Nuxt 應用程式的第一個請求時) 和 client-side,當導航到更遠的路徑時。
之後 - 新的 fetch
與舊的 fetch 相同,但是…
…由於我們可以為每個 component 設定一個 fetch
,fetch hooks 會按照它們的層次結構順序呼叫。
5. 錯誤處理
之前 - 我們使用 context.error
function,當 API 呼叫期間發生錯誤時,它會顯示自訂錯誤頁面。
之後 - 新的 fetch
使用 $fetchState
object 在 API 呼叫期間處理 template area 中的錯誤。
錯誤處理在 component level 執行。
這是否表示我們無法像 Nuxt 2.12 之前那樣向使用者顯示自訂錯誤頁面?
是的,我們可以,但僅限於 asyncData()
,當它關於 page-level component data 時。當使用 fetch
時,我們可以利用 this.$nuxt.error({ statusCode: 404, message: 'Data not found' })
來顯示自訂錯誤頁面。
結論
新的 fetch hook 帶來了許多改進,並在 fetch 資料和以全新的方式組織 route-level 和 building-block components 方面提供了更大的彈性!
當您規劃和設計需要同一 route 內多個 API 呼叫的新 Nuxt 專案時,它肯定會讓您稍微改變思考方式。
我希望這篇文章有助於您熟悉新的 fetch 功能。我很想看看您用它構建了什麼。