升級指南

瞭解如何升級到最新版 Nuxt。

升級 Nuxt

最新版本

要將 Nuxt 升級到最新版本,請使用 nuxt upgrade 命令。

npx nuxt upgrade

夜間釋出渠道

要在 Nuxt 功能釋出前使用最新構建版本並測試其功能,請閱讀夜間釋出通道指南。

遷移到 Nuxt 4

Nuxt 4 包含了顯著的改進和變更。本指南將幫助您將現有的 Nuxt 3 應用程式遷移到 Nuxt 4。

首先,升級到 Nuxt 4

npm install nuxt@^4.0.0

升級後,大多數 Nuxt 4 的行為現在都是預設的。但是,如果您的遷移需要保持向後相容性,某些功能仍然可以配置。

以下部分詳細介紹了升級到 Nuxt 4 時需要進行的關鍵變更和遷移。

突破性或重大變更已在下面記錄,並附有遷移步驟和可用的配置選項。

使用 Codemods 進行遷移

為了方便升級過程,我們與Codemod團隊合作,透過一些開源 codemods 自動化了許多遷移步驟。

如果您遇到任何問題,請使用 npx codemod feedback 向 Codemod 團隊報告 🙏

有關 Nuxt 4 codemods 的完整列表、詳細資訊、其來源以及執行它們的各種方式,請訪問Codemod 登錄檔.

您可以使用以下 codemod 配方執行本指南中提到的所有 codemods

npx codemod@latest nuxt/4/migration-recipe

此命令將按順序執行所有 codemods,您可以選擇取消選擇任何不想執行的 codemod。每個 codemod 及其各自的變更也列在下面,並且可以獨立執行。

新目錄結構

🚦 影響級別:重大

Nuxt 現在預設使用新的目錄結構,並向後相容(因此如果 Nuxt 檢測到您正在使用舊結構,例如頂層 app/pages/ 目錄,則此新結構將不適用)。

👉檢視完整 RFC

變更內容

  • 新的 Nuxt 預設 srcDir 預設為 app/,大多數內容都從那裡解析。
  • serverDir 現在預設為 <rootDir>/server,而不是 <srcDir>/server
  • layers/modules/public/ 預設相對於 <rootDir> 解析
  • 如果使用Nuxt Content v2.13+content/ 相對於 <rootDir> 解析
  • 新增了 dir.app,它是我們查詢 router.options.tsspa-loading-template.html 的目錄 - 預設為 <srcDir>/
v4 資料夾結構示例。
.output/
.nuxt/
app/
  assets/
  components/
  composables/
  layouts/
  middleware/
  pages/
  plugins/
  utils/
  app.config.ts
  app.vue
  router.options.ts
content/
layers/
modules/
node_modules/
public/
shared/
server/
  api/
  middleware/
  plugins/
  routes/
  utils/
nuxt.config.ts
有了這個新結構,~ 別名現在預設指向 app/ 目錄(您的 srcDir)。這意味著 ~/components 解析為 app/components/~/pages 解析為 app/pages/ 等。

👉 欲瞭解更多詳情,請參見實現此更改的 PR.

變更原因

  1. 效能 - 將所有程式碼放在倉庫根目錄會導致 .git/node_modules/ 資料夾被 FS 觀察器掃描/包含,這會顯著延遲非 Mac OSes 上的啟動。
  2. IDE 型別安全 - server/ 和您應用程式的其餘部分在兩個完全不同的上下文中執行,具有不同的全域性匯入可用,確保 server/ 不在您應用程式的同一資料夾中是確保您在 IDE 中獲得良好自動補全的重要第一步。

遷移步驟

  1. 建立一個名為 app/ 的新目錄。
  2. 將您的 assets/components/composables/app/layouts/app/middleware/app/pages/app/plugins/utils/ 資料夾,以及 app.vueerror.vueapp.config.ts 移動到其中。如果您有 app/router-options.tsapp/spa-loading-template.html,這些路徑保持不變。
  3. 確保您的 nuxt.config.tscontent/layers/modules/public/server/ 資料夾保留在 app/ 資料夾之外,位於專案根目錄中。
  4. 請記住更新任何第三方配置檔案以適應新的目錄結構,例如您的 tailwindcsseslint 配置(如果需要 - @nuxtjs/tailwindcss 應該會自動正確配置 tailwindcss)。
您可以透過執行 npx codemod@latest nuxt/4/file-structure 自動化此遷移

但是,遷移是非必需的。如果您希望保留當前的資料夾結構,Nuxt 應該會自動檢測到它。(如果未檢測到,請提出問題。)唯一的例外是,如果您已經有自定義的 srcDir。在這種情況下,您應該知道您的 modules/public/server/ 資料夾將從您的 rootDir 而不是您的自定義 srcDir 解析。如果需要,您可以透過配置 dir.modulesdir.publicserverDir 來覆蓋此設定。

您還可以使用以下配置強制使用 v3 資料夾結構

nuxt.config.ts
export default defineNuxtConfig({
  // This reverts the new srcDir default from `app` back to your root directory
  srcDir: '.',
  // This specifies the directory prefix for `router.options.ts` and `spa-loading-template.html`
  dir: {
    app: 'app',
  },
})

單例資料獲取層

🚦 影響級別:中等

變更內容

Nuxt 的資料獲取系統(useAsyncDatauseFetch)已進行重大重組,以提高效能和一致性

  1. 相同鍵的共享引用:所有使用相同鍵呼叫 useAsyncDatauseFetch 的函式現在共享相同的 dataerrorstatus 引用。這意味著所有帶有顯式鍵的呼叫都不能有衝突的 deeptransformpickgetCachedDatadefault 選項。
  2. getCachedData 的更多控制getCachedData 函式現在每次獲取資料時都會被呼叫,即使這是由觀察器或呼叫 refreshNuxtData 引起的。(以前,總是獲取新資料,並且在這種情況下不呼叫此函式。)為了更好地控制何時使用快取資料以及何時重新獲取,該函式現在接收一個包含請求原因的上下文物件。
  3. 響應式鍵支援:您現在可以使用計算引用、普通引用或 getter 函式作為鍵,這使得資料能夠自動重新獲取(並單獨儲存資料)。
  4. 資料清理:當使用 useAsyncData 獲取資料的最後一個元件被解除安裝時,Nuxt 將刪除該資料,以避免記憶體使用量不斷增長。

變更原因

這些更改旨在改善記憶體使用並提高 useAsyncData 呼叫之間的載入狀態一致性。

遷移步驟

  1. 檢查不一致的選項:檢查所有使用相同鍵但選項或獲取函式不同的元件。
    // This will now trigger a warning
    const { data: users1 } = useAsyncData('users', () => $fetch('/api/users'), { deep: false })
    const { data: users2 } = useAsyncData('users', () => $fetch('/api/users'), { deep: true })
    

    將任何共享顯式鍵(並具有自定義選項)的 useAsyncData 呼叫提取到它們自己的組合函式中可能是有益的
    app/composables/useUserData.ts
    export function useUserData (userId: string) {
      return useAsyncData(
        `user-${userId}`,
        () => fetchUser(userId),
        {
          deep: true,
          transform: user => ({ ...user, lastAccessed: new Date() }),
        },
      )
    }
    
  2. 更新 getCachedData 實現:
    useAsyncData('key', fetchFunction, {
    -  getCachedData: (key, nuxtApp) => {
    -    return cachedData[key]
    -  }
    +  getCachedData: (key, nuxtApp, ctx) => {
    +    // ctx.cause - can be 'initial' | 'refresh:hook' | 'refresh:manual' | 'watch'
    +    
    +    // Example: Don't use cache on manual refresh
    +    if (ctx.cause === 'refresh:manual') return undefined
    +    
    +    return cachedData[key]
    +  }
    })
    

或者,現在您可以停用此行為

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    granularCachedData: false,
    purgeCachedData: false,
  },
})

層中模組載入順序已更正

🚦 影響級別:最小

變更內容

使用Nuxt 層時,模組的載入順序已更正。以前,專案根目錄中的模組在擴充套件層中的模組之前載入,這與預期行為相反。

現在模組按正確順序載入

  1. 層模組優先(按擴充套件順序 - 較深層優先)
  2. 專案模組最後(最高優先順序)

這會影響

  • nuxt.config.ts 中的 modules 陣列中定義的模組
  • modules/ 目錄自動發現的模組

變更原因

此更改確保

  • 擴充套件層的優先順序低於消費專案
  • 模組執行順序與直觀的層繼承模式匹配
  • 模組配置和鉤子在多層設定中按預期工作

遷移步驟

大多數專案不需要更改,因為這會更正載入順序以匹配預期行為。

但是,如果您的專案依賴於以前不正確的順序,您可能需要

  1. 審查模組依賴關係:檢查是否有任何模組依賴於特定的載入順序
  2. 調整模組配置:如果模組是為解決不正確的順序而配置的
  3. 徹底測試:確保所有功能在更正的順序下按預期工作

新正確順序的示例

// Layer: my-layer/nuxt.config.ts
export default defineNuxtConfig({
  modules: ['layer-module-1', 'layer-module-2'],
})

// Project: nuxt.config.ts
export default defineNuxtConfig({
  extends: ['./my-layer'],
  modules: ['project-module-1', 'project-module-2'],
})

// Loading order (corrected):
// 1. layer-module-1
// 2. layer-module-2
// 3. project-module-1 (can override layer modules)
// 4. project-module-2 (can override layer modules)

如果您因需要註冊鉤子而遇到模組順序依賴問題,請考慮對需要呼叫鉤子的模組使用 modules:done 鉤子。此鉤子在所有其他模組載入後執行,這意味著它是安全使用的。

👉 參閱PR #31507等等問題 #25719瞭解更多詳情。

路由元資料去重

🚦 影響級別:最小

變更內容

可以使用 definePageMeta 設定一些路由元資料,例如 namepath 等。以前,這些在路由和路由元資料上都可用(例如,route.nameroute.meta.name)。

現在,它們只能在路由物件上訪問。

變更原因

這是預設啟用 experimental.scanPageMeta 的結果,是一種效能最佳化。

遷移步驟

遷移應該很簡單

  const route = useRoute()
  
- console.log(route.meta.name)
+ console.log(route.name)

規範化元件名稱

🚦 影響級別:中等

Vue 現在將生成與 Nuxt 元件命名模式匹配的元件名稱。

變更內容

預設情況下,如果您沒有手動設定,Vue 將分配一個與元件檔名匹配的元件名稱。

目錄結構
├─ components/
├─── SomeFolder/
├───── MyComponent.vue

在這種情況下,元件名稱將是 MyComponent,就 Vue 而言。如果您想將其與 <KeepAlive> 一起使用,或者在 Vue DevTools 中識別它,您將需要使用此名稱。

但為了自動匯入它,您需要使用 SomeFolderMyComponent

透過此更改,這兩個值將匹配,Vue 將生成一個與 Nuxt 元件命名模式匹配的元件名稱。

遷移步驟

確保在任何使用 @vue/test-utils 中的 findComponent 的測試中以及任何依賴於元件名稱的 <KeepAlive> 中使用更新後的名稱。

或者,現在您可以停用此行為

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    normalizeComponentNames: false,
  },
})

Unhead v2

🚦 影響級別:最小

變更內容

Unhead,用於生成 <head> 標籤,已更新到版本 2。雖然大部分相容,但它包含幾個對低階 API 的重大更改。

  • 已刪除的 props:vmidhidchildrenbody
  • 不再支援 Promise 輸入。
  • 標籤現在預設使用 Capo.js 排序。

遷移步驟

以上更改對您的應用程式影響極小。

如果您遇到問題,您應該驗證

  • 您沒有使用任何已刪除的 props。
useHead({
  meta: [{ 
    name: 'description', 
    // meta tags don't need a vmid, or a key    
-   vmid: 'description' 
-   hid: 'description'
  }]
})
import { AliasSortingPlugin, TemplateParamsPlugin } from '@unhead/vue/plugins'

export default defineNuxtPlugin({
  setup () {
    const unhead = injectHead()
    unhead.use(TemplateParamsPlugin)
    unhead.use(AliasSortingPlugin)
  },
})

雖然不是必需的,但建議將所有從 @unhead/vue#importsnuxt/app 的匯入進行更新。

-import { useHead } from '@unhead/vue'
+import { useHead } from '#imports'

如果您仍然遇到問題,可以透過啟用 head.legacy 配置來恢復 v1 行為。

export default defineNuxtConfig({
  unhead: {
    legacy: true,
  },
})

SPA 載入螢幕的新 DOM 位置

🚦 影響級別:最小

變更內容

當渲染僅客戶端頁面(使用 ssr: false)時,我們選擇性地在 Nuxt 應用程式根目錄下渲染一個載入螢幕(來自 ~/app/spa-loading-template.html - 請注意,在 Nuxt 4 中這也已更改為 ~/spa-loading-template.html

<div id="__nuxt">
  <!-- spa loading template -->
</div>

現在,我們預設在 Nuxt 應用根目錄旁邊渲染模板

<div id="__nuxt"></div>
<!-- spa loading template -->

變更原因

這允許 spa 載入模板保留在 DOM 中,直到 Vue 應用程式的 suspense 解決,從而防止閃白。

遷移步驟

如果您正在使用 CSS 或 document.queryElement 定位 spa 載入模板,則需要更新您的選擇器。為此,您可以使用新的 app.spaLoaderTagapp.spaLoaderAttrs 配置選項。

或者,您可以透過以下方式恢復以前的行為

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    spaLoadingTemplateLocation: 'within',
  },
})

已解析的 error.data

🚦 影響級別:最小

以前可以丟擲帶有 data 屬性的錯誤,但此屬性未被解析。現在,它被解析並可在 error 物件中訪問。儘管這是一個修復,但如果您依賴於以前的行為並手動解析它,這在技術上是一個重大更改。

遷移步驟

更新您的自定義 error.vue 以刪除對 error.data 的任何額外解析

  <script setup lang="ts">
  import type { NuxtError } from '#app'

  const props = defineProps({
    error: Object as () => NuxtError
  })

- const data = JSON.parse(error.data)
+ const data = error.data
  </script>

更精細的內聯樣式

🚦 影響級別:中等

Nuxt 現在將只內聯 Vue 元件的樣式,而不是全域性 CSS。

變更內容

以前,Nuxt 會內聯所有 CSS,包括全域性樣式,並刪除 <link> 元素以分離 CSS 檔案。現在,Nuxt 將只對 Vue 元件執行此操作(以前會生成單獨的 CSS 塊)。我們認為這更好地平衡了減少單獨的網路請求(就像以前一樣,初始載入時不會為每個頁面或每個元件的單獨 .css 檔案發出單獨的請求),以及允許快取單個全域性 CSS 檔案並減少初始請求的文件下載大小。

遷移步驟

此功能完全可配置,您可以透過將 inlineStyles: true 設定為內聯全域性 CSS 以及元件內 CSS 來恢復以前的行為。

nuxt.config.ts
export default defineNuxtConfig({
  features: {
    inlineStyles: true,
  },
})

解析後掃描頁面元資料

🚦 影響級別:最小

變更內容

我們現在在呼叫 pages:extend 鉤子之後而不是之前掃描頁面元資料(在 definePageMeta 中定義)。

變更原因

這是為了允許掃描使用者希望在 pages:extend 中新增的頁面的元資料。我們仍然提供了一個在新 pages:resolved 鉤子中更改或覆蓋頁面元資料的機會。

遷移步驟

如果您想覆蓋頁面元資料,請在 pages:resolved 中進行,而不是在 pages:extend 中進行。

  export default defineNuxtConfig({
    hooks: {
-     'pages:extend'(pages) {
+     'pages:resolved'(pages) {
        const myPage = pages.find(page => page.path === '/')
        myPage.meta ||= {}
        myPage.meta.layout = 'overridden-layout'
      }
    }
  })

或者,您可以透過以下方式恢復以前的行為

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    scanPageMeta: true,
  },
})

共享預渲染資料

🚦 影響級別:中等

變更內容

我們啟用了以前的實驗性功能,以在不同頁面之間共享來自 useAsyncDatauseFetch 呼叫的資料。請參見原始 PR.

變更原因

此功能自動在預渲染的頁面之間共享 payload 資料。這可以在預渲染使用 useAsyncDatauseFetch 並在不同頁面中獲取相同資料的站點時顯著提高效能。

例如,如果您的站點需要為每個頁面呼叫 useFetch(例如,從 CMS 獲取選單的導航資料或站點設定),則在預渲染第一個使用它的頁面時,此資料只會獲取一次,然後快取以供預渲染其他頁面時使用。

遷移步驟

確保您的資料的任何唯一鍵始終解析為相同的資料。例如,如果您使用 useAsyncData 獲取與特定頁面相關的資料,則應提供唯一匹配該資料的鍵。(useFetch 應該會自動為您執行此操作。)

app/pages/test/[slug].vue
// This would be unsafe in a dynamic page (e.g. `[slug].vue`) because the route slug makes a difference
// to the data fetched, but Nuxt can't know that because it's not reflected in the key.
const route = useRoute()
const { data } = await useAsyncData(async () => {
  return await $fetch(`/api/my-page/${route.params.slug}`)
})
// Instead, you should use a key that uniquely identifies the data fetched.
const { data } = await useAsyncData(route.params.slug, async () => {
  return await $fetch(`/api/my-page/${route.params.slug}`)
})

或者,您可以透過以下方式停用此功能

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    sharedPrerenderData: false,
  },
})

useAsyncDatauseFetch 中的預設 dataerror

🚦 影響級別:最小

變更內容

useAsyncData 返回的 dataerror 物件現在將預設為 undefined

變更原因

以前 data 初始化為 null,但在 clearNuxtData 中重置為 undefinederror 初始化為 null。此更改是為了提高一致性。

遷移步驟

如果您以前檢查 data.valueerror.value 是否為 null,現在可以更新這些檢查以檢查 undefined

您可以透過執行 npx codemod@latest nuxt/4/default-data-error-value 自動化此步驟

useAsyncDatauseFetch 中呼叫 refresh 時,移除了 dedupe 選項的已棄用 boolean

🚦 影響級別:最小

變更內容

以前可以將 dedupe: boolean 傳遞給 refresh。這些是 cancel (true) 和 defer (false) 的別名。

app/app.vue
const { refresh } = await useAsyncData(() => Promise.resolve({ message: 'Hello, Nuxt!' }))

async function refreshData () {
  await refresh({ dedupe: true })
}

變更原因

為了提高畫質晰度,這些別名已被移除。

當在 useAsyncData 的選項中新增 dedupe 時,出現了問題,我們刪除了布林值,因為它們最終是相反的

refresh({ dedupe: false }) 表示不要取消現有請求而支援新請求。但在 useAsyncData 的選項中傳遞 dedupe: true 表示如果存在現有掛起請求,則不要發出任何新請求。(參見PR.)

遷移步驟

遷移應該很簡單

  const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt 3!' }))
  
  async function refreshData () {
-   await refresh({ dedupe: true })
+   await refresh({ dedupe: 'cancel' })

-   await refresh({ dedupe: false })
+   await refresh({ dedupe: 'defer' })
  }
您可以透過執行 npx codemod@latest nuxt/4/deprecated-dedupe-value 自動化此步驟

useAsyncDatauseFetch 中清除 data 時遵循預設值

🚦 影響級別:最小

變更內容

如果您為 useAsyncData 提供自定義 default 值,則在呼叫 clearclearNuxtData 時將使用此值,並且它將重置為預設值而不是簡單地取消設定。

變更原因

使用者通常會設定一個適當的空值,例如一個空陣列,以避免在迭代時檢查 null/undefined。在重置/清除資料時應尊重此設定。

useAsyncDatauseFetchpending 值對齊

🚦 影響級別:中等

useAsyncDatauseFetchuseLazyAsyncDatauseLazyFetch 返回的 pending 物件現在是一個計算屬性,僅當 status 也處於 pending 狀態時才為 true

變更內容

現在,當傳遞 immediate: false 時,pending 將在首次請求之前為 false。這與之前的行為不同,之前 pending 始終為 true,直到首次請求。

變更原因

這使 pending 的含義與 status 屬性保持一致,後者在請求進行中時也處於 pending 狀態。

遷移步驟

如果您依賴 pending 屬性,請確保您的邏輯考慮了新行為,即 pending 僅當狀態也處於 pending 狀態時才為 true

  <template>
-   <div v-if="!pending">
+   <div v-if="status === 'success'">
      <p>Data: {{ data }}</p>
    </div>
    <div v-else>
      <p>Loading...</p>
    </div>
  </template>
  <script setup lang="ts">
  const { data, pending, execute, status } = await useAsyncData(() => fetch('/api/data'), {
    immediate: false
  })
  onMounted(() => execute())
  </script>

或者,您可以暫時恢復到以前的行為

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    pendingWhenIdle: true,
  },
})

useAsyncDatauseFetch 中的鍵更改行為

🚦 影響級別:中等

變更內容

useAsyncDatauseFetch 中使用響應式鍵時,Nuxt 會在鍵更改時自動重新獲取資料。當設定 immediate: false 時,useAsyncData 僅在資料已獲取一次後才在鍵更改時獲取資料。

以前,useFetch 的行為略有不同。它總是在鍵更改時獲取資料。

現在,useFetchuseAsyncData 的行為保持一致——僅在資料已獲取一次後才在鍵更改時獲取資料。

變更原因

這確保了 useAsyncDatauseFetch 之間行為的一致性,並防止意外獲取。如果您已設定 immediate: false,則必須呼叫 refreshexecute,否則 useFetchuseAsyncData 中的資料將永遠不會被獲取。

遷移步驟

此更改通常會改善預期行為,但如果您期望更改非即時 useFetch 的鍵或選項,您現在將需要首次手動觸發它。

  const id = ref('123')
  const { data, execute } = await useFetch('/api/test', {
    query: { id },
    immediate: false
  )
+ watch(id, () => execute(), { once: true })

要退出此行為

// Or globally in your Nuxt config
export default defineNuxtConfig({
  experimental: {
    alwaysRunFetchOnKeyChange: true,
  },
})

useAsyncDatauseFetch 中的淺層資料響應性

🚦 影響級別:最小

useAsyncDatauseFetchuseLazyAsyncDatauseLazyFetch 返回的 data 物件現在是 shallowRef 而不是 ref

變更內容

當獲取新資料時,任何依賴於 data 的內容仍將是響應式的,因為整個物件被替換。但如果您的程式碼更改了該資料結構內部的屬性,這不會觸發應用程式中的任何響應性。

變更原因

這為深度巢狀的物件和陣列帶來了顯著的效能改進,因為 Vue 不需要監視每個屬性/陣列的修改。在大多數情況下,data 也應該是不可變的。

遷移步驟

在大多數情況下,不需要遷移步驟,但如果您依賴於資料物件的響應性,則有兩種選擇

  1. 您可以按組合函式粒度選擇性地啟用深度響應性
    - const { data } = useFetch('/api/test')
    + const { data } = useFetch('/api/test', { deep: true })
    
  2. 您可以更改專案範圍的預設行為(不推薦)
    nuxt.config.ts
    export default defineNuxtConfig({
      experimental: {
        defaults: {
          useAsyncData: {
            deep: true,
          },
        },
      },
    })
    
如果需要,您可以透過執行 npx codemod@latest nuxt/4/shallow-function-reactivity 自動化此步驟

builder:watch 中的絕對監聽路徑

🚦 影響級別:最小

變更內容

Nuxt builder:watch 鉤子現在會發出一個絕對路徑,而不是相對於您的專案 srcDir 的路徑。

變更原因

這允許我們支援監聽 srcDir 之外的路徑,併為層和其他更復雜的模式提供更好的支援。

遷移步驟

我們已經主動遷移了我們知道使用此鉤子的公共 Nuxt 模組。請參閱問題 #25339.

但是,如果您是模組作者,正在使用 builder:watch 鉤子並希望保持向後/向前相容,您可以使用以下程式碼確保您的程式碼在 Nuxt v3 和 Nuxt v4 中工作方式相同

+ import { relative, resolve } from 'node:fs'
  // ...
  nuxt.hook('builder:watch', async (event, path) => {
+   path = relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path))
    // ...
  })
您可以透過執行 npx codemod@latest nuxt/4/absolute-watch-path 自動化此步驟

刪除 window.__NUXT__ 物件

變更內容

應用程式完成水合後,我們將刪除全域性 window.__NUXT__ 物件。

變更原因

這為多應用模式(#21635)鋪平了道路,並使我們能夠專注於訪問 Nuxt 應用資料的一種方式 - useNuxtApp()

遷移步驟

資料仍然可用,但可以透過 useNuxtApp().payload 訪問

- console.log(window.__NUXT__)
+ console.log(useNuxtApp().payload)

目錄索引掃描

🚦 影響級別:中等

變更內容

您的 app/middleware/ 資料夾中的子資料夾也已掃描 index 檔案,這些檔案現在也作為中介軟體註冊到您的專案中。

變更原因

Nuxt 會自動掃描多個資料夾,包括 app/middleware/app/plugins/

您的 app/plugins/ 資料夾中的子資料夾已掃描 index 檔案,我們希望在掃描目錄之間保持此行為一致。

遷移步驟

可能不需要遷移,但如果您希望恢復到以前的行為,您可以新增一個鉤子來過濾掉這些中介軟體

export default defineNuxtConfig({
  hooks: {
    'app:resolve' (app) {
      app.middleware = app.middleware.filter(mw => !/\/index\.[^/]+$/.test(mw.path))
    },
  },
})

模板編譯更改

🚦 影響級別:最小

變更內容

以前,Nuxt 使用 lodash/template 編譯位於檔案系統上使用 .ejs 檔案格式/語法的模板。

此外,我們提供了一些模板工具(serializeimportNameimportSources),這些工具可用於在這些模板中生成程式碼,現在這些工具已被移除。

變更原因

在 Nuxt v3 中,我們轉向了一種“虛擬”語法,其中包含一個 getContents() 函式,該函式更靈活、效能更高。

此外,lodash/template 曾出現過一系列安全問題。這些問題實際上不適用於 Nuxt 專案,因為它在構建時而非執行時使用,並且由受信任的程式碼使用。但是,它們仍然出現在安全審計中。此外,lodash 是一個龐大的依賴項,並且大多數專案未使用。

最後,直接在 Nuxt 中提供程式碼序列化函式並不理想。相反,我們維護像unjs/knitwork這樣的專案,它們可以是您專案的依賴項,並且可以在其中直接報告/解決安全問題,而無需升級 Nuxt 本身。

遷移步驟

我們已經提出了 PR 來更新使用 EJS 語法的模組,但如果您需要自己執行此操作,您有三種向後/向前相容的替代方案

  • 將您的字串插值邏輯直接移入 getContents()
  • 使用自定義函式處理替換,例如在https://github.com/nuxt-modules/color-mode/pull/240.
  • 使用 es-toolkit/compat(lodash 模板的直接替代品)作為專案的依賴項而不是 Nuxt 的依賴項
+ import { readFileSync } from 'node:fs'
+ import { template } from 'es-toolkit/compat'
  // ...
  addTemplate({
    fileName: 'appinsights-vue.js'
    options: { /* some options */ },
-   src: resolver.resolve('./runtime/plugin.ejs'),
+   getContents({ options }) {
+     const contents = readFileSync(resolver.resolve('./runtime/plugin.ejs'), 'utf-8')
+     return template(contents)({ options })
+   },
  })

最後,如果您正在使用模板工具(serializeimportNameimportSources),您可以將它們替換為 knitwork 中的工具,如下所示

import { genDynamicImport, genImport, genSafeVariableName } from 'knitwork'

const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"\{(.+)\}"(?=,?$)/gm, r => JSON.parse(r).replace(/^\{(.*)\}$/, '$1'))

const importSources = (sources: string | string[], { lazy = false } = {}) => {
  return toArray(sources).map((src) => {
    if (lazy) {
      return `const ${genSafeVariableName(src)} = ${genDynamicImport(src, { comment: `webpackChunkName: ${JSON.stringify(src)}` })}`
    }
    return genImport(src, genSafeVariableName(src))
  }).join('\n')
}

const importName = genSafeVariableName
您可以透過執行 npx codemod@latest nuxt/4/template-compilation-changes 自動化此步驟

預設 TypeScript 配置更改

🚦 影響級別:最小

變更內容

compilerOptions.noUncheckedIndexedAccess 現在是 true 而不是 false

變更原因

此更改是先前3.12 配置更新的後續,其中我們改進了預設值,主要遵循TotalTypeScript 的建議.

遷移步驟

有兩種方法

  1. 對您的應用程式執行型別檢查並修復任何新錯誤(推薦)。
  2. 在您的 nuxt.config.ts 中覆蓋新預設值
    export default defineNuxtConfig({
      typescript: {
        tsConfig: {
          compilerOptions: {
            noUncheckedIndexedAccess: false,
          },
        },
      },
    })
    

TypeScript 配置拆分

🚦 影響級別:最小

變更內容

Nuxt 現在為不同的上下文生成單獨的 TypeScript 配置,以提供更好的型別檢查體驗

  1. 新的 TypeScript 配置檔案:Nuxt 現在生成額外的 TypeScript 配置
    • .nuxt/tsconfig.app.json - 用於您的應用程式程式碼(Vue 元件、組合式函式等)
    • .nuxt/tsconfig.server.json - 用於您的伺服器端程式碼(Nitro/server 目錄)
    • .nuxt/tsconfig.node.json - 用於您的構建時程式碼(模組、nuxt.config.ts 等)
    • .nuxt/tsconfig.shared.json - 用於應用程式和伺服器上下文之間共享的程式碼(如型別和非環境特定工具)
    • .nuxt/tsconfig.json - 用於向後相容的舊配置
  2. 向後相容性:現有擴充套件 .nuxt/tsconfig.json 的專案將繼續按以前的方式工作。
  3. 選擇性專案引用:新專案或需要更好型別檢查的專案可以採用 TypeScript 的專案引用功能。
  4. 上下文特定的型別檢查:每個上下文現在都具有適合其特定環境的編譯器選項和包含/排除項。
  5. 新的 typescript.nodeTsConfig 選項:您現在可以自定義 Node.js 構建時程式碼的 TypeScript 配置。

變更原因

此更改提供了以下幾點優勢

  1. 更好的型別安全:每個上下文(應用程式、伺服器、構建時)都透過上下文特定的全域性變數和 API 獲得適當的型別檢查。
  2. 改進的 IDE 體驗:為程式碼庫的不同部分提供更好的智慧感知和錯誤報告。
  3. 更清晰的分離:伺服器程式碼不會錯誤地建議客戶端 API,反之亦然。
  4. 效能:TypeScript 可以使用適當範圍的配置更有效地檢查程式碼。

例如,在您的 nuxt.config.ts 中無法使用自動匯入(但以前 TypeScript 不會標記此問題)。雖然 IDE 識別出您的 server/ 目錄中的 tsconfig.json 所暗示的獨立上下文,但這並未反映在型別檢查中(需要單獨的步驟)。

遷移步驟

無需遷移 - 現有專案將繼續按以前的方式工作。

但是,為了利用改進的型別檢查,您可以選擇加入新的專案引用方法

  1. 更新您的根 tsconfig.json 以使用專案引用
    {
      "files": [],
      "references": [
        { "path": "./.nuxt/tsconfig.app.json" },
        { "path": "./.nuxt/tsconfig.server.json" },
        { "path": "./.nuxt/tsconfig.shared.json" },
        { "path": "./.nuxt/tsconfig.node.json" }
      ]
    }
    
  2. 刪除任何手動伺服器 tsconfig.json 檔案(如 server/tsconfig.json),這些檔案擴充套件了 .nuxt/tsconfig.server.json
  3. 更新您的型別檢查指令碼以使用專案引用的構建標誌
    - "typecheck": "nuxt prepare && vue-tsc --noEmit"
    + "typecheck": "nuxt prepare && vue-tsc -b --noEmit"
    
  4. 將所有型別增強移動到其適當的上下文:
    • 如果您正在增強應用程式上下文的型別,請將檔案移動到 app/ 目錄。
    • 如果您正在增強伺服器上下文的型別,請將檔案移動到 server/ 目錄。
    • 如果您正在增強在應用程式和伺服器之間共享的型別,請將檔案移動到 shared/ 目錄。
    app/server/shared/ 目錄外部增強型別將不適用於新的專案引用設定。
  5. 根據需要配置 Node.js TypeScript 選項
    export default defineNuxtConfig({
      typescript: {
        // Customize app/server TypeScript config
        tsConfig: {
          compilerOptions: {
            strict: true,
          },
        },
        // Customize build-time TypeScript config
        nodeTsConfig: {
          compilerOptions: {
            strict: true,
          },
        },
      },
    })
    
  6. 更新任何執行 TypeScript 檢查的 CI/構建指令碼,以確保它們使用新的專案引用方法。

新配置為選擇加入的專案提供了更好的型別安全性和智慧感知,同時保持了現有設定的完全向後相容性。

移除實驗性功能

🚦 影響級別:最小

變更內容

Nuxt 4 中有四個實驗性功能不再可配置

  • experimental.treeshakeClientOnly 將為 true(自 v3.0 起預設)
  • experimental.configSchema 將為 true(自 v3.3 起預設)
  • experimental.polyfillVueUseHead 將為 false(自 v3.4 起預設)
  • experimental.respectNoSSRHeader 將為 false(自 v3.4 起預設)
  • vite.devBundler 不再可配置 - 預設將使用 vite-node

變更原因

這些選項已設定為其當前值一段時間,我們沒有理由相信它們需要保持可配置。

遷移步驟

移除頂層 generate 配置

🚦 影響級別:最小

變更內容

Nuxt 4 中不再提供頂層 generate 配置選項。這包括其所有屬性

  • generate.exclude - 用於從預渲染中排除路由
  • generate.routes - 用於指定要預渲染的路由

變更原因

頂層 generate 配置是 Nuxt 2 的遺留。我們已經支援 nitro.prerender 一段時間了,它是 Nuxt 3+ 中配置預渲染的首選方式。

遷移步驟

generate 配置替換為相應的 nitro.prerender 選項

export default defineNuxtConfig({
- generate: {
-   exclude: ['/admin', '/private'],
-   routes: ['/sitemap.xml', '/robots.txt']
- }
+ nitro: {
+   prerender: {
+     ignore: ['/admin', '/private'],
+     routes: ['/sitemap.xml', '/robots.txt']
+   }
+ }
})
閱讀有關 Nitro 預渲染配置選項的更多資訊。

Nuxt 2 與 Nuxt 3+

下表快速比較了 Nuxt 的 3 個版本

功能/版本Nuxt 2Nuxt BridgeNuxt 3+
Vue223
穩定性😊 穩定😊 穩定😊 穩定
效能🏎 快速✈️ 更快🚀 最快
Nitro 引擎
ESM 支援🌙 部分👍 更好
TypeScript☑️ 選擇啟用🚧 部分
組合式 API🚧 部分
Options API
元件自動匯入
<script setup> 語法🚧 部分
自動匯入
webpack445
Vite⚠️ 部分🚧 部分
Nuxt CLI❌ 舊版✅ nuxt✅ nuxt
靜態站點

Nuxt 2 到 Nuxt 3+

遷移指南提供了 Nuxt 2 功能到 Nuxt 3+ 功能的逐步比較以及調整當前應用程式的指導。

查閱 從 Nuxt 2 遷移到 Nuxt 3 的指南

Nuxt 2 到 Nuxt Bridge

如果您希望逐步將 Nuxt 2 應用程式遷移到 Nuxt 3,您可以使用 Nuxt Bridge。Nuxt Bridge 是一個相容層,允許您透過選擇性機制在 Nuxt 2 中使用 Nuxt 3+ 功能。

從 Nuxt 2 遷移到 Nuxt Bridge