透過超過 100 個技巧來學習 Nuxt!

升級指南

了解如何升級到最新的 Nuxt 版本。

升級 Nuxt

最新版本

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

npx nuxi upgrade

每夜發布通道

若要使用最新的 Nuxt 建置版本並在發布前測試功能,請閱讀每夜發布通道指南。

每夜發布通道的 latest 標籤目前追蹤 Nuxt v4 分支,這表示它現在特別容易有重大變更 - 請小心!您可以使用 "nuxt": "npm:nuxt-nightly@3x" 選擇加入 3.x 分支的每夜發布版本。

測試 Nuxt 4

Nuxt 4 的發布日期將會另行公告。這取決於 Nitro 主要版本發布後是否有足夠的時間在社群中進行適當的測試。您可以在此 PR 中追蹤 Nitro 版本發布的進度。

在發布之前,可以從 Nuxt 3.12+ 版本測試 Nuxt 4 的許多重大變更。

觀看 Alexander Lichter 的影片,了解如何搶先選擇加入 Nuxt 4 的重大變更。

選擇加入 Nuxt 4

首先,將 Nuxt 升級到最新版本

然後,您可以設定您的 compatibilityVersion 以符合 Nuxt 4 的行為

目前,您需要在每個選擇加入 Nuxt 4 行為的層中定義相容性版本。Nuxt 4 發布後,這將不再需要。
nuxt.config.ts
export default 
defineNuxtConfig
({
future
: {
compatibilityVersion
: 4,
}, // To re-enable _all_ Nuxt v3 behavior, set the following options: // srcDir: '.', // dir: { // app: 'app' // }, // experimental: { // scanPageMeta: 'after-resolve', // sharedPrerenderData: false, // compileTemplate: true, // resetAsyncDataToUndefined: true, // templateUtils: true, // relativeWatchPaths: true, // normalizeComponentNames: false, // spaLoadingTemplateLocation: 'within', // defaults: { // useAsyncData: { // deep: true // } // } // }, // features: { // inlineStyles: true // }, // unhead: { // renderSSRHeadOptions: { // omitLineBreaks: false // } // } })

當您將 compatibilityVersion 設定為 4 時,您的 Nuxt 設定中的預設值將會變更以選擇加入 Nuxt v4 的行為,但您可以在測試時細緻地重新啟用 Nuxt v3 的行為,依照上面註解掉的程式碼行即可。如果發生這種情況,請提交問題,以便我們可以在 Nuxt 或生態系統中解決這些問題。

移轉到 Nuxt 4

此處將註明重大變更或重要變更,以及向後/向前相容性的移轉步驟。

在最終發布之前,此章節可能會變更,因此如果您使用 compatibilityVersion: 4 測試 Nuxt 4,請定期返回此處查看。

使用 Codemod 移轉

為了簡化升級程序,我們已與 Codemod 團隊合作,透過一些開放原始碼 codemod 自動化許多移轉步驟。

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

如需 Nuxt 4 codemod 的完整清單、每個 codemod 的詳細資訊、來源以及各種執行方式,請造訪Codemod 註冊表

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

npx codemod@latest nuxt/4/migration-recipe

此命令會依序執行所有 codemod,您可以選擇取消選取任何您不想執行的 codemod。每個 codemod 也會在下方列出,以及其各自的變更,且可以獨立執行。

新的目錄結構

🚦 影響程度:重大

Nuxt 現在預設為新的目錄結構,具有向後相容性 (因此,如果 Nuxt 偵測到您正在使用舊結構,例如頂層的 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/
server/
  api/
  middleware/
  plugins/
  routes/
  utils/
nuxt.config.ts

👉 如需更多詳細資訊,請參閱實作此變更的 PR

變更原因
  1. 效能 - 將所有程式碼放置在儲存庫的根目錄中,會導致 .git/node_modules/ 資料夾被檔案系統監看工具掃描/包含,這可能會大幅延遲非 Mac 作業系統上的啟動時間。
  2. IDE 類型安全 - server/ 和應用程式的其餘部分在兩個完全不同的內容中執行,其中可用的全域匯入不同,而且確保 server/ 不在與應用程式其餘部分相同的資料夾是確保您在 IDE 中獲得良好自動完成功能的第一步。
移轉步驟
  1. 建立一個名為 app/ 的新目錄。
  2. 將您的 assets/components/composables/layouts/middleware/pages/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 `app/router.options.ts` and `app/spa-loading-template.html`
  dir: {
    app: 'app'
  }
})

路由中繼資料的重複資料刪除

🚦 影響程度:最小

變更內容

可以使用 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-utilsfindComponent 的測試中,以及任何依賴元件名稱的 <KeepAlive> 中,使用更新後的名稱。

或者,目前您可以使用以下方式停用此行為

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

SPA 載入畫面的新 DOM 位置

🚦 影響程度:最小

變更內容

當呈現僅限於用戶端的頁面時 (使用 ssr: false),我們會選擇性地在 Nuxt 應用程式根目錄中呈現載入畫面 (來自 app/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',
} })

更細緻的內嵌樣式

🚦 影響程度:中等

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

變更原因

此功能會自動在預先渲染的頁面之間共享酬載資料。當預先渲染使用 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 來自動執行此步驟

如果您遇到任何問題,可以使用以下方式回復到先前的行為

nuxt.config.ts
export default 
defineNuxtConfig
({
experimental
: {
defaults
: {
useAsyncData
: {
value
: 'null',
errorValue
: 'null'
} } } })

如果您正在執行此操作,請回報問題,因為我們不打算將其保留為可配置的項目。

移除在 useAsyncDatauseFetch 中呼叫 refreshdedupe 選項的已棄用 boolean

🚦 影響程度:最小

變更內容

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

app.vue
const { 
refresh
} = await
useAsyncData
(async () => ({
message
: 'Hello, Nuxt 3!' }))
async function
refreshData
() {
await
refresh
({
dedupe
: true })
}
變更原因

為了更清楚起見,已移除這些別名。

在將 dedupe 作為 useAsyncData 的選項新增時出現了問題,並且我們移除了 boolean 值,因為它們最終是相反的

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。在重設/清除資料時應遵守此原則。

移轉步驟

如果您遇到任何問題,可以暫時使用以下方式回復到先前的行為

nuxt.config.ts
export default 
defineNuxtConfig
({
experimental
: {
resetAsyncDataToUndefined
: 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 模組。請參閱 issue #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)

目錄索引掃描

🚦 影響程度:中等

變更內容

也會掃描您 middleware/ 資料夾中的子資料夾以查找 index 檔案,並且這些檔案現在也會在您的專案中註冊為中介軟體。

變更原因

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

我們會掃描 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 語法的模組,但是如果您需要自行執行此操作,則您有三個向後/向前相容的替代方案

+ import { readFileSync } from 'node:fs'
+ import { template } from 'lodash-es'
  // ...
  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 來自動執行此步驟

移除實驗性功能

🚦 影響程度:最小

變更內容

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
變更原因

這些選項已設定為其目前的值一段時間,並且我們沒有理由認為它們需要保持可配置。

移轉步驟

Nuxt 2 與 Nuxt 3+

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

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

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