釋出·  

瞭解 Nuxt 2.12 中 fetch 的工作原理

探索 fetch 鉤子的不同功能,學習一種將資料引入 Nuxt 應用程式的全新方式。
Krutie Patel

Krutie Patel

@KrutiePatel

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
};

我們有三個鍵,

  1. Pending - 允許您在客戶端呼叫 fetch 時顯示佔位符
  2. Error - 允許您顯示錯誤訊息
  3. 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 屬性。

layouts/default.vue
<template>
  <div>
    <nuxt keep-alive />
  </div>
</template>

此外,我們可以將 :keep-alive-props 傳遞給 <nuxt /> 元件,以快取一定數量的頁面及其獲取的資料。

:keep-alive-props 屬性允許我們指示在網站內導航到其他頁面時,應在記憶體中保留的最大頁面數量。

layouts/default.vue
<nuxt keep-alive :keep-alive-props="{ max: 10 }" />

上述是一種提高頁面效能的方法,它更高階且通用,而下一種方法則透過使用 $fetchStatetimestamp 屬性並將其與重新獲取資料之前的延遲秒數進行比較,來最佳化 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

  1. asyncData 僅限於頁面級元件
  2. this 上下文不可用
  3. 透過返回資料來新增 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

  1. fetch 在所有 Vue 元件中均可用
  2. this 上下文可用
  3. 簡單地修改本地資料
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 之前之後相比的一些顯著變化列表。

1. fetch 鉤子的呼叫順序

之前 - fetch 鉤子在元件初始化之前被呼叫,因此 this 在 fetch 鉤子內部不可用。

之後 - fetch 在路由被訪問時,元件例項在伺服器端建立後被呼叫。

2. this vs context

之前 - 我們可以在頁面級元件中訪問 Nuxt context,前提是將 context 作為第一個引數傳遞。

export default {
  fetch(context) {
    // …
  }
}

之後 - 我們可以像 Vue 客戶端鉤子一樣訪問 this 上下文,無需傳遞任何引數。

export default {
  fetch() {
    console.log(this)
  }
}

3. fetch 鉤子的可用性

之前 - 只有頁面(路由級)元件才允許在伺服器端獲取資料。

之後 - 現在,我們可以在任何 Vue 元件中非同步預取資料。

4. fetch 鉤子的呼叫順序

之前 - 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 功能。我期待看到您用它構建出什麼。