Nuxt 在最新發佈的 2.12 版本中引入了新的 fetch
。Fetch 提供了一種將資料導入 Nuxt 應用程式的全新方式。
在這篇文章中,我們將探索 fetch hook 的不同功能,並嘗試了解它的運作方式。
Fetch Hook 和 Nuxt 生命週期
就 Nuxt 的生命週期 hooks 而言,fetch
位於 Vue 生命周期中的 created
hook 之後。我們已經知道,所有的 Vue 生命周期 hooks 都使用它們的 this
上下文呼叫。這同樣適用於 fetch
hook。
Fetch hook 在伺服器端建立元件實例後呼叫。這使得 this
上下文在 fetch
內部可用。
export default {
fetch() {
console.log(this)
}
}
讓我們看看這對頁面元件意味著什麼。
頁面元件
藉助 this
上下文,fetch 能夠直接更改元件的資料。這表示我們可以設定元件的本地資料,而無需從頁面元件發送 Vuex store action 或提交 mutation。
因此,Vuex 成為可選的,但並非不可能。如果需要,我們仍然可以像平常一樣使用 this.$store
來存取 Vuex store。
fetch hook 的可用性
透過 fetch
,我們可以在任何 Vue 元件中非同步預先提取資料。這表示,除了在 /pages
目錄中找到的頁面元件外,在 /layouts
和 /components
目錄中找到的所有其他 .vue
元件也可以從 fetch hook 中受益。
讓我們看看這對佈局和構建模組元件意味著什麼。
佈局元件
使用新的 fetch
,我們現在可以直接從佈局元件進行 API 呼叫。這在 v2.12 發佈之前是不可能的。
可能的使用案例
- 在 Nuxt 佈局中從後端提取配置資料,以動態生成頁腳和導覽列
- 在導覽列中提取使用者相關資料(例如,使用者個人資料、購物車項目計數)
- 在
layouts/error.vue
上提取網站相關資料
構建模組(子/巢狀)元件
由於子元件中也提供了 fetch
hook,我們可以將一些資料提取任務從頁面級別的元件卸載,並將它們委派給巢狀元件。這在 v2.12 發佈之前也是不可能的。
這在很大程度上減少了路由級別元件的責任。
可能的使用案例 - 我們仍然可以將 props 傳遞給子元件,但是如果子元件需要有自己的資料提取邏輯,它們現在可以了!
多個 fetch hooks 的呼叫順序
由於每個元件都可以有自己的資料提取邏輯,您可能會問它們的呼叫順序是什麼?
Fetch hook 在伺服器端呼叫一次(在首次請求 Nuxt 應用程式時),然後在導覽到其他路由時在客戶端呼叫。但是由於我們可以為每個元件定義一個 fetch hook,因此 fetch hooks 會按照其層次結構的順序呼叫。
在伺服器端停用 fetch
此外,如果需要,我們甚至可以在伺服器端停用 fetch。
export default {
fetchOnServer: false
}
這樣,fetch hook 將僅在客戶端呼叫。當 fetchOnServer
設定為 false 時,當元件在伺服器端渲染時,$fetchState.pending
會變為 true
。
錯誤處理
新的 fetch
在元件級別處理錯誤。讓我們看看如何處理。
由於我們是非同步提取資料,新的 fetch() 會提供一個 $fetchState
物件,以檢查請求是否已完成並順利進行。
以下是 $fetchState
物件的外觀。
$fetchState = {
pending: true | false,
error: null | {},
timestamp: Integer
};
我們有三個鍵,
- Pending - 當在客戶端呼叫 fetch 時,讓您顯示預留位置
- Error - 讓您顯示錯誤訊息
- Timestamp - 顯示上次 fetch 的時間戳記,這對於使用
keep-alive
進行快取很有用
這些鍵隨後直接在元件的模板區域中使用,以在從 API 提取資料的過程中顯示相關的預留位置。
<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>
當在元件級別發生錯誤時,我們可以透過在 fetch hook 中檢查 process.server
並使用 throw new Error()
語句,在伺服器端設定 HTTP 狀態碼。
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 狀態碼對於正確的 SEO 很有用。
Fetch 作為方法
新的 fetch hook 也可作為一種方法,可以在使用者互動時呼叫,或從元件方法中以程式設計方式呼叫。
<!-- from template in template -->
<button @click="$fetch">Refresh Data</button>
// from component methods in script section
export default {
methods: {
refresh() {
this.$fetch()
}
}
}
讓 Nuxt 頁面更高效能
我們可以使用 :keep-alive-props
prop 和 activated
hook,透過新的 fetch hook 讓 Nuxt 頁面元件更高效能。
Nuxt 允許在記憶體中快取特定數量的頁面及其提取的資料。並且還允許增加秒數,以便我們可以重新提取資料。
為了使上述任何方法都能運作,我們必須在通用的 <nuxt />
和 <nuxt-child
> 元件中使用 keep-alive
prop。
<template>
<div>
<nuxt keep-alive />
</div>
</template>
此外,我們可以將 :keep-alive-props
傳遞給 <nuxt />
元件,以快取多個頁面及其提取的資料。
:keep-alive-props
prop 允許我們指出在網站內導覽到其他位置時,應保留在記憶體中的最大頁面數量。
<nuxt keep-alive :keep-alive-props="{ max: 10 }" />
以上是一種提高頁面效能的方法,它更為高階和通用,而下一種方法則深入到優化 fetch 請求呼叫,方法是使用 $fetchState
的 timestamp
屬性,並將其與重新提取資料之前的秒數延遲進行比較。
Vue 的 activated
hook 在此處與 Nuxt 的 keep-alive
prop 一起使用,以重新提取資料。
export default {
activated() {
// Call fetch again if last fetch more than a minute ago
if (this.$fetchState.timestamp <= Date.now() - 60000) {
this.$fetch()
}
}
}
asyncData vs Fetch
就頁面元件而言,新的 fetch
看起來與 asyncData()
非常相似,因為它們都處理本地資料。但是,以下有一些值得注意的關鍵差異。
截至 Nuxt 2.12,asyncData
方法仍然是一個有效的特性。讓我們研究一下 asyncData
和新的 fetch
之間的一些關鍵差異。
asyncData
asyncData
僅限於頁面級別的元件this
上下文不可用- 透過返回資料新增 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
}
}
新的 Fetch
fetch
在所有 Vue 元件中都可用this
上下文可用- 只是更改本地資料
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
版本有很大的不同。
這是一個重大變更嗎?
不是。實際上,舊的 fetch 仍然可以透過將
context
作為第一個參數傳遞來使用,以避免現有 Nuxt 應用程式中的任何重大變更。
以下是 fetch
hook 中與 v2.12之前和之後相比的顯著變更列表。
1. fetch
hook 的呼叫順序
之前 - fetch
hook 在初始化元件之前呼叫,因此 this
在 fetch hook 內部不可用。
之後 - fetch
在存取路由時,在伺服器端建立元件實例後呼叫。
2. this
vs context
之前 - 我們可以存取頁面級別元件上的 Nuxt context
,前提是 context
作為第一個參數傳遞。
export default {
fetch(context) {
// …
}
}
之後 - 我們可以像 Vue 客戶端 hook 一樣存取 this
上下文,而無需傳遞任何參數。
export default {
fetch() {
console.log(this)
}
}
3. fetch
hook 的可用性
之前 - 只允許頁面(路由級別)元件在伺服器端提取資料。
之後 - 現在,我們可以在任何 Vue 元件中非同步預先提取資料。
4. fetch
hook 的呼叫順序
之前 - fetch
可以呼叫伺服器端一次(在首次請求 Nuxt 應用程式時),以及在導覽到其他路由時呼叫客戶端。
之後 - 新的 fetch
與舊的 fetch 相同,但是…
…由於我們可以為每個元件設定一個 fetch
,因此 fetch
hooks 會按照其層次結構的順序呼叫。
5. 錯誤處理
之前 - 當 API 呼叫期間發生錯誤時,我們使用 context.error
函式來顯示自訂錯誤頁面。
之後 - 新的 fetch
使用 $fetchState
物件在 API 呼叫期間處理模板區域中的錯誤。
錯誤處理是在元件級別執行的。
這是否表示我們無法像 Nuxt 2.12 之前那樣向使用者顯示自訂錯誤頁面?
是的,可以,但是僅限於頁面級別元件資料的 asyncData()
。當使用 fetch
時,我們可以使用 this.$nuxt.error({ statusCode: 404, message: 'Data not found' })
來顯示自訂錯誤頁面。
結論
新的 fetch hook 帶來了許多改進,並在提取資料和組織路由級別和構建模組元件方面提供了更大的靈活性,這是一種全新的方式!
當您規劃和設計新的 Nuxt 專案時,如果需要在同一路由中進行多次 API 呼叫,肯定會讓您有不同的思考方式。
我希望這篇文章能幫助您熟悉新的 fetch
功能。我很期待看到您用它來建構什麼。