useNuxtApp

原始檔
訪問 Nuxt 應用程式的共享執行時上下文。

useNuxtApp 是一個內建的組合式函式,它提供了一種訪問 Nuxt 共享執行時上下文的方法,也稱為 Nuxt 上下文,它在客戶端和伺服器端(但不在 Nitro 路由中)都可用。它幫助你訪問 Vue 應用程式例項、執行時鉤子、執行時配置變數和內部狀態,例如 ssrContextpayload

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

如果你的作用域中沒有執行時上下文,呼叫 useNuxtApp 將會丟擲異常。你可以使用 tryUseNuxtApp 來代替那些不需要 nuxtApp 的組合式函式,或者只是檢查上下文是否可用而不丟擲異常。

方法

provide (name, value)

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(name, cb)

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

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

請參閱 執行時鉤子 以瞭解 Nuxt 呼叫的可用執行時鉤子。

app/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(name, ...args)

當使用任何現有鉤子呼叫時,callHook 會返回一個 Promise。

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

屬性

useNuxtApp() 暴露了以下屬性,你可以使用它們來擴充套件和自定義你的應用程式,並共享狀態、資料和變數。

vueApp

vueApp 是全域性的 Vue.js應用程式例項你可以透過 nuxtApp 訪問。

一些有用的方法

  • component()- 如果同時傳遞名稱字串和元件定義,則註冊一個全域性元件;如果只傳遞名稱,則檢索已註冊的元件。
  • directive()- 如果同時傳遞名稱字串和指令定義,則註冊一個全域性自定義指令;如果只傳遞名稱,則檢索已註冊的指令(示例)
  • use()- 安裝一個**Vue.js 外掛** (示例)
閱讀更多內容請訪問 https://vuejs.tw/api/application.html#application-api

ssrContext

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

Nuxt 透過 ssrContext 暴露以下屬性

  • url (字串) - 當前請求 URL。
  • 事件 (h3js/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.[name-of-your-state] 訪問。
    app/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。

    在下面的示例中,我們使用 payload 外掛為LuxonDateTime 類定義了一個 reducer(或序列化器)和一個 reviver(或反序列化器)。
    app/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 應用程式是否正在客戶端進行水合。

app/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 場景時,你可能會遇到在非同步呼叫後當前例項已被取消設定的情況。

app/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 (comment).

解決方案

這就是 runWithContext 可以用來恢復上下文的地方,類似於 <script setup> 的工作方式。

Nuxt 內部使用unjs/unctx來支援類似於 Vue 的外掛和中介軟體的組合式函式。這使得像 navigateTo() 這樣的組合式函式可以在不直接向它們傳遞 nuxtApp 的情況下工作——將組合式 API 的開發體驗和效能優勢帶到整個 Nuxt 框架。

Nuxt 組合式函式與 Vue 組合式 API 具有相同的設計,因此需要類似的解決方案來“神奇地”實現這種轉換。請檢視unjs/unctx#2(提案),unjs/unctx#4(轉換實現),以及nuxt/framework#3884(整合到 Nuxt)。

Vue 目前只支援 <script setup> 的非同步上下文恢復,用於 async/await 用法。在 Nuxt 中,為 defineNuxtPlugin()defineNuxtRouteMiddleware() 添加了轉換支援,這意味著當你使用它們時,Nuxt 會自動使用上下文恢復對其進行轉換。

剩餘問題

在包含 awaittry/catch 語句中,unjs/unctx 轉換自動恢復上下文似乎存在 bug,這最終需要解決,以便消除上述建議的 workaround 的要求。

原生非同步上下文

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

原生非同步上下文支援目前在 Bun 和 Node 中有效。
Docs > 4 X > Guide > Going Further > Experimental Features#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
  }
}