透過 100 多個技巧的集合來學習 Nuxt!

useNuxtApp

存取 Nuxt 應用程式的共用執行階段環境。

useNuxtApp 是一個內建的組合式函式,提供一種存取 Nuxt 的共用執行階段環境的方式,也稱為 Nuxt 環境,它在客戶端和伺服器端都可用(但不在 Nitro 路由中)。它可以幫助您存取 Vue 應用程式實例、執行階段鉤子、執行階段配置變數和內部狀態,例如 ssrContextpayload

app.vue
<script setup lang="ts">
const nuxtApp = useNuxtApp()
</script>

如果執行階段環境在您的作用域中不可用,則呼叫 useNuxtApp 時將會拋出例外。您可以改用 tryUseNuxtApp 來取得不需要 nuxtApp 的組合式函式,或者只是檢查環境是否可用而不會拋出例外。

方法

provide (名稱, 值)

nuxtApp 是一個執行階段環境,您可以使用 Nuxt 外掛程式 來擴充。使用 provide 函式建立 Nuxt 外掛程式,以在所有組合式函式和元件中,讓值和輔助方法在您的 Nuxt 應用程式中可用。

provide 函式接受 namevalue 參數。

const nuxtApp = useNuxtApp()
nuxtApp.provide('hello', (name) => `Hello ${name}!`)

// Prints "Hello name!"
console.log(nuxtApp.$hello('name'))

如您在上面的範例中所見,$hello 已成為 nuxtApp 環境的新增和自訂部分,並且可以在 nuxtApp 可存取的所有位置中使用。

hook(名稱, cb)

nuxtApp 中可用的鉤子允許您自訂 Nuxt 應用程式的執行階段方面。您可以在 Vue.js 組合式函式和 Nuxt 外掛程式中使用執行階段鉤子來掛接到渲染生命週期。

hook 函式對於透過在特定點掛接到渲染生命週期來新增自訂邏輯非常有用。hook 函式主要用於建立 Nuxt 外掛程式時。

請參閱 執行階段鉤子,以了解 Nuxt 呼叫的可用的執行階段鉤子。

plugins/test.ts
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hook('page:start', () => {
    /* your code goes here */
  })
  nuxtApp.hook('vue:error', (..._args) => {
    console.log('vue:error')
    // if (import.meta.client) {
    //   console.log(..._args)
    // }
  })
})

callHook(名稱, ...args)

當呼叫現有的任何鉤子時,callHook 會傳回一個 Promise。

await nuxtApp.callHook('my-plugin:init')

屬性

useNuxtApp() 會公開下列屬性,您可以使用這些屬性來擴充和自訂您的應用程式,並共用狀態、資料和變數。

vueApp

vueApp 是全域 Vue.js 應用程式實例,您可以使用 nuxtApp 來存取它。

一些有用的方法

  • component() - 如果傳遞名稱字串和元件定義,則註冊全域元件;如果僅傳遞名稱,則擷取已註冊的元件。
  • directive() - 如果傳遞名稱字串和指令定義,則註冊全域自訂指令;如果僅傳遞名稱,則擷取已註冊的指令(範例)
  • use() - 安裝一個 Vue.js 外掛程式 (範例)
請在 https://vuejs.org/api/application.html#application-api 中閱讀更多資訊。

ssrContext

ssrContext 是在伺服器端渲染期間產生,並且僅在伺服器端可用。

Nuxt 透過 ssrContext 公開下列屬性

  • url (字串) - 目前的請求 URL。
  • event (unjs/h3 請求事件) - 存取目前路由的請求和回應。
  • payload (物件) - NuxtApp 酬載物件。

payload

payload 會公開從伺服器端到客戶端的資料和狀態變數。下列金鑰在從伺服器端傳遞後,將在客戶端上可用

  • serverRendered (布林值) - 指示回應是否為伺服器端渲染。
  • data (物件) - 當您使用 useFetchuseAsyncData 從 API 端點擷取資料時,可以從 payload.data 存取產生的酬載。此資料會被快取,並協助您避免在多次發出相同請求時擷取相同資料。
    <script setup lang="ts">
    const { data } = await useAsyncData('count', () => $fetch('/api/count'))
    </script>
    

    在上面的範例中,使用 useAsyncData 擷取 count 的值之後,如果您存取 payload.data,您會看到 { count: 1 } 記錄在那裡。
    當從 ssrcontext 存取相同的 payload.data 時,您也可以在伺服器端存取相同的值。
  • state (物件) - 當您在 Nuxt 中使用 useState 組合式函式來設定共用狀態時,會透過 payload.state.[您狀態的名稱] 來存取此狀態資料。
    plugins/my-plugin.ts
    export const useColor = () => useState<string>('color', () => 'pink')
    
    export default defineNuxtPlugin((nuxtApp) => {
      if (import.meta.server) {
        const color = useColor()
      }
    })
    

    也可以使用更進階的類型,例如 refreactiveshallowRefshallowReactiveNuxtError
    自從 Nuxt v3.4 開始,可以為 Nuxt 不支援的類型定義自己的 reducer/reviver。
    觀看 Alexander Lichter 關於序列化酬載的影片,特別是有關類別的部分。

    在下面的範例中,我們使用酬載外掛程式為 Luxon DateTime 類別定義 reducer (或序列化器) 和 reviver (或反序列化器)。
    plugins/date-time-payload.ts
    /**
     * This kind of plugin runs very early in the Nuxt lifecycle, before we revive the payload.
     * You will not have access to the router or other Nuxt-injected properties.
     *
     * Note that the "DateTime" string is the type identifier and must
     * be the same on both the reducer and the reviver.
     */
    export default definePayloadPlugin((nuxtApp) => {
      definePayloadReducer('DateTime', (value) => {
        return value instanceof DateTime && value.toJSON()
      })
      definePayloadReviver('DateTime', (value) => {
        return DateTime.fromISO(value)
      })
    })
    

isHydrating

使用 nuxtApp.isHydrating (布林值) 檢查 Nuxt 應用程式是否在客戶端上進行水合作用。

components/nuxt-error-boundary.ts
export default defineComponent({
  setup (_props, { slots, emit }) {
    const nuxtApp = useNuxtApp()
    onErrorCaptured((err) => {
      if (import.meta.client && !nuxtApp.isHydrating) {
        // ...
      }
    })
  }
})

runWithContext

您可能在這裡是因為您收到了「Nuxt 實例不可用」的訊息。請謹慎使用此方法,並報告導致問題的範例,以便最終可以在框架層級解決。

runWithContext 方法旨在用於呼叫函式,並為其提供明確的 Nuxt 環境。通常,Nuxt 環境會隱含地傳遞,您無需擔心。但是,當在中間件/外掛程式中使用複雜的 async/await 情境時,您可能會遇到在非同步呼叫後,目前實例已取消設定的情況。

middleware/auth.ts
export default defineNuxtRouteMiddleware(async (to, from) => {
  const nuxtApp = useNuxtApp()
  let user
  try {
    user = await fetchUser()
    // the Vue/Nuxt compiler loses context here because of the try/catch block.
  } catch (e) {
    user = null
  }
  if (!user) {
    // apply the correct Nuxt context to our `navigateTo` call.
    return nuxtApp.runWithContext(() => navigateTo('/auth'))
  }
})

用法

const result = nuxtApp.runWithContext(() => functionWithContext())
  • functionWithContext:任何需要目前 Nuxt 應用程式環境的函式。此環境將會自動正確套用。

runWithContext 將會傳回 functionWithContext 傳回的任何值。

對環境的深入說明

Vue.js 組合 API (以及類似的 Nuxt 組合式函式) 的運作方式是依賴隱含的環境。在生命週期期間,Vue 會將目前元件的暫時實例 (以及 Nuxt 的 nuxtApp 暫時實例) 設定為全域變數,並在相同的刻度中取消設定。在伺服器端渲染時,會有來自不同使用者的多個請求,並且 nuxtApp 在相同的全域環境中執行。因此,Nuxt 和 Vue 會立即取消設定此全域實例,以避免洩漏兩個使用者或元件之間的共用參考。

這意味著什麼?組合 API 和 Nuxt 組合式函式僅在生命週期期間以及在任何非同步操作之前的相同刻度中可用

// --- Vue internal ---
const _vueInstance = null
const getCurrentInstance = () => _vueInstance
// ---

// Vue / Nuxt sets a global variable referencing to current component in _vueInstance when calling setup()
async function setup() {
  getCurrentInstance() // Works
  await someAsyncOperation() // Vue unsets the context in same tick before async operation!
  getCurrentInstance() // null
}

解決此問題的經典方法是在首次呼叫時將目前實例快取到本機變數中,例如 const instance = getCurrentInstance(),並在下一個組合式呼叫中使用它,但問題是任何巢狀組合式呼叫現在都需要明確接受實例作為引數,而不是依賴組合 API 的隱含環境。這是組合式函式的設計限制,而不是問題本身。

為了克服此限制,Vue 在編譯我們的應用程式程式碼時會在幕後進行一些工作,並在每次呼叫 <script setup> 後還原環境

const __instance = getCurrentInstance() // Generated by Vue compiler
getCurrentInstance() // Works!
await someAsyncOperation() // Vue unsets the context
__restoreInstance(__instance) // Generated by Vue compiler
getCurrentInstance() // Still works!

如需 Vue 實際執行的更詳細說明,請參閱 unjs/unctx#2 (評論)

解決方案

這是可以使用 runWithContext 還原環境的地方,類似於 <script setup> 的運作方式。

Nuxt 在內部使用 unjs/unctx 來支援與 Vue 類似的外掛程式和中間件的組合式函式。這使得 navigateTo() 等組合式函式能夠在無需直接將 nuxtApp 傳遞給它們的情況下運作 - 將組合 API 的 DX 和效能優勢帶給整個 Nuxt 框架。

Nuxt 組合式函式的設計與 Vue 組合 API 相同,因此需要類似的解決方案才能神奇地完成此轉換。請查看 unjs/unctx#2 (提案)、unjs/unctx#4 (轉換實作) 和 nuxt/framework#3884 (與 Nuxt 的整合)。

目前 Vue 僅針對使用 async/await 的情況,在 <script setup> 中支援非同步上下文還原。在 Nuxt 3 中,加入了對 defineNuxtPlugin()defineNuxtRouteMiddleware() 的轉換支援,這表示當您使用它們時,Nuxt 會自動轉換它們並進行上下文還原。

仍存在的問題

unjs/unctx 自動還原上下文的轉換,在包含 awaittry/catch 語句中似乎存在錯誤,最終需要解決這個問題,才能移除上述建議的變通方案的需求。

原生非同步上下文

透過使用新的實驗性功能,可以使用 Node.js 的 AsyncLocalStorage 和新的 unctx 支援來啟用原生非同步上下文支援,使非同步上下文原生地可供任何巢狀的非同步組合式函式使用,而無需轉換或手動傳遞/呼叫上下文。

原生非同步上下文支援目前在 Bun 和 Node 中運作。
請參閱 文件 > 指南 > 進階 > 實驗性功能#asynccontext 以了解更多資訊。

tryUseNuxtApp

此函式的功能與 useNuxtApp 完全相同,但如果上下文不可用,則會返回 null,而不是拋出例外。

您可以將其用於不需要 nuxtApp 的組合式函式,或者只是在沒有例外的情況下檢查上下文是否可用。

使用範例

composable.ts
export function useStandType() {
  // Always works on the client
  if (tryUseNuxtApp()) {
    return useRuntimeConfig().public.STAND_TYPE
  } else {
    return process.env.STAND_TYPE
  }
}