透過超過 100 個技巧的集合來學習 Nuxt!
發布·  

了解 Nuxt 2.12 中 fetch 的運作方式

探索 fetch hook 的不同功能,並學習將資料帶入 Nuxt 應用程式的全新方式。

Nuxt 在最新發布的 2.12 版本中引入了新的 fetch。Fetch 提供了一種將資料帶入 Nuxt 應用程式的全新方式。

在這篇文章中,我們將探索 fetch hook 的不同功能,並嘗試了解它的運作方式。

Fetch Hook 與 Nuxt 生命周期

就 Nuxt 生命周期 hooks 而言,fetch 位於 Vue 生命周期中 created hook 之後。正如我們已經知道的那樣,所有 Vue 生命周期 hooks 都使用它們的 this context 呼叫。fetch hook 也適用相同規則。

New fetch in Nuxt lifecycle

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,

  1. Pending - 讓您在 client-side 呼叫 fetch 時顯示 placeholder
  2. Error - 讓您顯示錯誤訊息
  3. 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。

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

此外,我們可以將 :keep-alive-props 傳遞給 <nuxt /> component,以快取一定數量的 pages 以及它們 fetch 的資料。

:keep-alive-props prop 允許我們指示在我們在網站內其他地方導航時,應該在記憶體中保留的最大 pages 數量。

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

以上是一種提升 page 效能的方法,它更為高階和通用,而下一個方法則深入研究,透過使用 $fetchStatetimestamp 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

  1. asyncData 僅限於 page-level components
  2. this context 不可用
  3. 透過 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

  1. fetch 在所有 Vue components 中都可用
  2. this context 可用
  3. 簡單地 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 功能。我很想看看您用它構建了什麼。