Nuxt 在最新版本 2.12 中引入了新的 fetch
。Fetch 提供了一種將資料引入 Nuxt 應用程式的全新方式。
在這篇文章中,我們將探索 fetch 鉤子的不同功能,並嘗試理解它的工作原理。
Fetch 鉤子與 Nuxt 生命週期
在 Nuxt 生命週期鉤子中,fetch
位於 Vue 生命週期中 created
鉤子之後。正如我們所知,所有的 Vue 生命週期鉤子都會呼叫它們的 this
上下文。這同樣適用於 fetch
鉤子。
Fetch 鉤子在元件例項在伺服器端建立後被呼叫。這使得 this
上下文在 fetch
內部可用。
export default {
fetch() {
console.log(this)
}
}
讓我們看看這對頁面元件意味著什麼。
頁面元件
藉助 this
上下文,fetch 能夠直接修改元件的資料。這意味著我們可以設定元件的本地資料,而無需從頁面元件分派 Vuex store action 或提交 mutation。
因此,Vuex 變得可選,但並非不可能。如果需要,我們仍然可以像往常一樣使用 this.$store
訪問 Vuex store。
fetch 鉤子的可用性
透過 fetch
,我們可以在任何 Vue 元件中非同步預取資料。這意味著,除了 /pages
目錄中的頁面元件之外,/layouts
和 /components
目錄中的所有其他 .vue
元件也可以從 fetch 鉤子中受益。
讓我們看看這對佈局和構建塊元件意味著什麼。
佈局元件
使用新的 fetch
,我們現在可以直接從佈局元件進行 API 呼叫。這在 v2.12 釋出之前是不可能實現的。
可能的用例
- 在 Nuxt 佈局中從後端獲取配置資料,動態生成頁尾和導航欄
- 在導航欄中獲取使用者相關資料(即使用者資料、購物車商品數量)
- 在
layouts/error.vue
中獲取網站相關資料
構建塊(子/巢狀)元件
由於 fetch
鉤子在子元件中也可用,我們可以將一些資料獲取任務從頁面級元件中分流出去,並將其委託給巢狀元件。這在 v2.12 釋出之前也是不可能實現的。
這極大地減輕了路由級別元件的責任。
可能的用例 - 我們仍然可以向子元件傳遞 props,但如果子元件需要擁有自己的資料獲取邏輯,現在它們也可以了!
多個 fetch 鉤子的呼叫順序
由於每個元件都可以有自己的資料獲取邏輯,您可能會問它們各自的呼叫順序是什麼?
fetch 鉤子在伺服器端(首次請求 Nuxt 應用程式時)呼叫一次,然後在客戶端導航到其他路由時呼叫。但是由於我們可以為每個元件定義一個 fetch 鉤子,所以 fetch 鉤子是按其層次結構順序呼叫的。
在伺服器端停用 fetch
此外,如果需要,我們甚至可以在伺服器端停用 fetch。
export default {
fetchOnServer: false
}
這樣,fetch 鉤子將只在客戶端呼叫。當 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 鉤子中檢查 process.server
來在伺服器端設定 HTTP 狀態碼,並隨後使用 throw new Error()
語句。
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 鉤子也可以作為方法,在使用者互動時呼叫,或者從元件方法中以程式設計方式呼叫。
<!-- 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
屬性和 activated
鉤子,透過新的 fetch 鉤子來提高 Nuxt 頁面元件的效能。
Nuxt 允許在記憶體中快取一定數量的頁面及其獲取的資料。還允許新增一個秒數,在此之前我們可以重新獲取資料。
為了使上述任何方法生效,我們必須在通用的 <nuxt />
和 <nuxt-child
> 元件中使用 keep-alive
屬性。
<template>
<div>
<nuxt keep-alive />
</div>
</template>
此外,我們可以將 :keep-alive-props
傳遞給 <nuxt />
元件,以快取一定數量的頁面及其獲取的資料。
:keep-alive-props
屬性允許我們指示在網站內導航到其他頁面時,應在記憶體中保留的最大頁面數量。
<nuxt keep-alive :keep-alive-props="{ max: 10 }" />
上述是一種提高頁面效能的方法,它更高階且通用,而下一種方法則透過使用 $fetchState
的 timestamp
屬性並將其與重新獲取資料之前的延遲秒數進行比較,來最佳化 fetch 請求呼叫。
Vue 的 activated
鉤子在此與 Nuxt 的 keep-alive
屬性一起使用,以重新獲取資料。
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
就頁面元件而言,新的 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
顯著不同。
這是一個破壞性更改嗎?
不,它不是。實際上,透過將
context
作為第一個引數傳遞,舊的 fetch 仍然可以使用,以避免在您現有的 Nuxt 應用程式中出現任何破壞性更改。
以下是 fetch
鉤子與 v2.12 之前和之後相比的一些顯著變化列表。
fetch
鉤子的呼叫順序 1.
之前 - fetch
鉤子在元件初始化之前被呼叫,因此 this
在 fetch 鉤子內部不可用。
之後 - fetch
在路由被訪問時,元件例項在伺服器端建立後被呼叫。
this
vs context
2.
之前 - 我們可以在頁面級元件中訪問 Nuxt context
,前提是將 context
作為第一個引數傳遞。
export default {
fetch(context) {
// …
}
}
之後 - 我們可以像 Vue 客戶端鉤子一樣訪問 this
上下文,無需傳遞任何引數。
export default {
fetch() {
console.log(this)
}
}
fetch
鉤子的可用性 3.
之前 - 只有頁面(路由級)元件才允許在伺服器端獲取資料。
之後 - 現在,我們可以在任何 Vue 元件中非同步預取資料。
fetch
鉤子的呼叫順序 4.
之前 - fetch
可以在伺服器端呼叫一次(首次請求 Nuxt 應用程式時),並在導航到後續路由時在客戶端呼叫。
之後 - 新的 fetch
與舊的 fetch 相同,但是……
…由於每個元件可以有一個 fetch
,因此 fetch
鉤子會按照它們的層次結構順序呼叫。
5. 錯誤處理
之前 - 我們使用 context.error
函式,該函式在 API 呼叫期間發生錯誤時顯示自定義錯誤頁面。
之後 - 新的 fetch
使用 $fetchState
物件在模板區域處理 API 呼叫期間的錯誤。
錯誤處理在元件級別執行。
這是否意味著我們無法像 Nuxt 2.12 之前那樣向用戶顯示自定義錯誤頁面?
是的,可以,但僅限於與頁面級元件資料相關的 asyncData()
。使用 fetch
時,我們可以利用 this.$nuxt.error({ statusCode: 404, message: 'Data not found' })
來顯示自定義錯誤頁面。
結論
新的 fetch 鉤子帶來了許多改進,並提供了更大的靈活性,以全新的方式獲取資料和組織路由級和構建塊元件!
當您規劃和設計需要同一路由內多次 API 呼叫的新 Nuxt 專案時,它肯定會讓您思考方式有所不同。
希望本文能幫助您熟悉新的 fetch
功能。我期待看到您用它構建出什麼。