首页
Preview

从Nuxt3迁移到Nuxt4的升级指南以及Nuxt4的变动

虽然Nuxt4还没有正式发布,但是Nuxt的官方文档中已经更新了很多关于Nuxt4的内容,结合官方的文档和所有合并的PR (可以在改PR中随时关注发布进程),我们可以做一个从Nuxt3到Nuxt4的迁移,有兴趣的小伙伴一起来看看吧。

迁移准备

在迁移到Nuxt4之前,我们需要先将项目更新到最新的Nuxt3版本,这样有利于减少兼容性问题。使用以下更新命令进行更新:

npx nuxi@latest upgrade --force

该命令会在升级前删除node_modules目录和锁定文件。

启用Nuxt 4兼容模式

兼容模式旨在帮助你在不立即破坏Nuxt 3代码的情况下过渡到Nuxt 4。启用它可以让你逐步调整代码。

  • 步骤1:修改nuxt.config.ts文件,启用Nuxt 4兼容模式:
export default defineNuxtConfig({
  future: {
    compatibilityVersion: 4,
  },
})

compatibilityVersion设置为4,将允许Nuxt运行在兼容层中,确保你为Nuxt 4所做的更改不会在应用中引发意外问题。

如果要回退到Nuxt3,可以在nuxt.config.ts文件中更新以下配置:

export default defineNuxtConfig({
  future: {
    compatibilityVersion: 4,
  },
  // 要恢复所有Nuxt v3行为,请设置以下选项:
  srcDir: ".",
  dir: {
    app: "app",
  },
  experimental: {
    scanPageMeta: "after-resolve",
    sharedPrerenderData: false,
    compileTemplate: true,
    resetAsyncDataToUndefined: true,
    templateUtils: true,
    relativeWatchPaths: true,
    normalizeComponentNames: false,
    defaults: {
      useAsyncData: {
        deep: true,
      },
    },
  },
  unhead: {
    renderSSRHeadOptions: {
      omitLineBreaks: false,
    },
  },
})

通过上述配置,项目将会兼容Nuxt 4,也支持Nuxt 3。

运行Nuxt Codemod进行自动化迁移

Nuxt提供了一个codemod工具,可以自动化一些重复的迁移任务,如更新API调用和重新组织文件。

Codemod是一种自动化脚本,用于扫描代码库并进行特定更新,常用于框架升级中处理破坏性更改和语法调整。Codemod旨在简化迁移过程,避免手动重写重复或模板化的代码。

对于Nuxt 4升级,Nuxt团队提供了专用的codemod来帮助自动化几个关键的转换。

运行Nuxt 4 codemod的命令如下:

npx codemod@latest nuxt/4/migration-recipe

运行此命令后,系统会提示你安装codemod的最新版本。

image.png Codemod将执行以下操作:

  • 调整绝对路径的监视。
  • 将深度响应状态转换为Nuxt 4的浅响应模型。
  • 处理废弃和更新的导入。 除了其他一些变化外,Codemod还会列出将要执行的所有更改,你可以按需勾选或取消勾选。如下图:

image.png

具体如下:

  1. nuxt/4/absolute-watch-path:可能涉及到文件观察路径的变化,确保在开发过程中文件监视机制能够正确识别路径。
  2. nuxt/4/default-data-error-value:可能与 Nuxt 4 中数据错误处理相关,可能会设置一个默认值来处理数据获取失败或出错的情况。
  3. nuxt/4/deprecated-dedupe-value:可能与某些与去重(去除重复数据或请求)相关的 API 特性或选项的弃用有关,需要在代码中做出相应更新。
  4. nuxt/4/file-structure:可能与 Nuxt 4 中的文件结构变动有关,可能需要根据新的约定调整文件的组织方式。
  5. nuxt/4/shallow-function-reactivity:可能涉及与响应式行为相关的变化,特别是对于“浅响应函数”的处理。浅响应意味着只有对象的顶层属性是响应式的,这可能会影响状态变化如何触发组件更新。
  6. nuxt/4/template-compilation-changes:可能涉及 Nuxt 4 中模板编译的变化,可能包括性能提升或语法/结构的变化。

虽然Codemod自动化了许多迁移任务,但对于大型项目,可能还需要进行一些手动调整。目前来看我们还是手动迁移比较好。

下面一起来看看Nuxt4更新的新特性,做了哪些优化。

Nuxt4的目录和文件结构

Nuxt 4 引入了新的目录结构,但仍兼容之前的,无需担心。如果你使用的是传统的目录结构(例如根目录下的 pages/ 目录),Nuxt 会自动检测并顺利运行。这意味着,目录结构的改变是可选的。

如果采用Nuxt4的目录结构:

my-project/
├── app/
│   ├── assets/
│   ├── components/
│   ├── composables/
│   ├── layouts/
│   ├── middleware/
│   ├── pages/
│   ├── plugins/
│   ├── utils/
│   └── app.vue
├── layers/
├── modules/
├── public/
├── server/
└── nuxt.config.ts

主要有以下几点变化:

  • srcDir :从项目根目录(.)移至新的 app/ 目录。需要在项目根目录创建此 app/ 目录。
  • server文件:即服务端代码所在的位置放在根目录下面,位于 app/ 目录之外。
  • layers、modules 和 public 目录:三个目录将继续保持在项目根目录,位于 app/ 目录之外。
  • Nuxt 配置nuxt.config.ts 文件仍会保留在项目根目录。
  • app 目录内容:所有其他 Nuxt 目录和文件将移至新的 app/ 目录,包括 assets/components/composables/layouts/middleware/pages/plugins/utils/app.vue

最明显且重要的就是将 server/ 目录与app代码分开,能确保更清晰的前后端边界。

Nuxt4组件名称标准化

Nuxt 4 对 Vue 组件名称的生成方式进行了调整,使其与 Nuxt 推荐的命名约定保持一致。

默认情况下,如果您没有显式设置组件名称,Vue 将会自动为组件分配一个与文件名匹配的名称。

例如,以下目录结构:

├─ components/
├─── ParentFolder/
├───── MyComponent.vue

在 Vue.js 中,之前该组件的名称是 MyComponent,而在 Nuxt 4 中,名称会自动按照 Nuxt 样式进行规范化,变为 ParentFolderMyComponent。如果你不想用这样的命名方式(恢复到以前的组件命名行为),可以在配置中禁用该功能:

export default defineNuxtConfig({
  experimental: {
    normalizeComponentNames: false
  }
})

Nuxt4 路由元数据去重

在 Nuxt 4 中,访问某些页面元数据的方式略有变化。以前,通过 definePageMeta 定义的元数据可以同时在 route 对象和 route.meta 对象中访问。而现在,这些值只能直接通过 route 对象访问。

例如,以下代码:

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

需要更新为:

const route = useRoute()
console.log(route.name)

此更新是由于 Nuxt 4 默认启用了 experimental.scanPageMeta 功能,这是一项性能优化,可以更高效地扫描和处理页面元数据。

Nuxt4 页面元数据扫描时机调整

Nuxt 4 调整了页面元数据(通过 definePageMeta 定义)扫描和处理的时机。 以前,Nuxt 会在调用 pages:extend 钩子之前扫描页面元数据。现在,扫描会发生在pages:extend钩子之后。

如果需要覆盖已经设置的页面元数据,应在 pages:resolved 钩子中进行修改,而不是 pages:extend 钩子。例如:

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

如果你还是想Nuxt3的写法,可以通过在 nuxt.config.ts 中设置 experimental.scanPageMeta 为 true 来实现:

export default defineNuxtConfig({
  experimental: {
    scanPageMeta: true
  }
})

访问 Nuxt应用数据

在 Nuxt 4 中,是通过 useNuxtApp() composable 来访问 Nuxt 应用数据。比如:

// Nuxt 4
console.log(useNuxtApp().payload)

Nuxt4 数据获取变化

在 Nuxt 4 中,数据获取机制经历了显著的变化,改进了数据管理和获取的方式。我们将介绍这些更新、新的实践以及如何升级代码以适应 Nuxt 4 中的数据获取方法。

1.使用 useFetchuseAsyncData 共享预渲染数据

在 Nuxt 4 中,原先作为实验性功能的 sharedPrerenderData 被升级为稳定版本,并成为静态生成站点的默认设置。

sharedPrerenderData 使得通过 useAsyncDatauseFetch 获取的数据能够在预渲染的页面之间共享。它现在自动共享预渲染页面之间的有效负载数据,从而在多个页面中获取相同数据时提高性能。

在此更改之前,如果你的站点页面被预渲染并且每个页面都像下面这样获取全站设置:

const { data } = await useFetch('/api/settings')

那么每次页面导航时,useFetch 都会发起请求到 API 端点。但在 Nuxt 4 中,数据只会获取一次并缓存,以便在所有其他预渲染页面中使用。

为了确保此功能正常工作,需要确保在 useAsyncDatauseFetch 中使用的键唯一标识正在获取的数据。这对动态页面尤其重要,应包括路由参数作为键的一部分:

// 更改前(不适用于动态页面)
const { data } = await useAsyncData(async () => {
  return await $fetch(`/api/settings/page/${route.params.slug}`)
})

// 更改后(使用唯一的键)
const { data } = await useAsyncData(route.params.slug, async () => {
  return await $fetch(`/api/settings/page/${route.params.slug}`)
})

如果使用的是 useFetch,以下是提供键的方法:

const { data } = await useFetch(`/api/settings/page/${route.params.slug}`, 
  { key: route.params.slug }
)

如果需要,可以通过在 nuxt.config.ts 中将 experimental.sharedPrerenderData 设置为 false 来禁用此共享预渲染数据功能:

export default defineNuxtConfig({
  experimental: {
    sharedPrerenderData: false
  }
})

2. Nuxt 4 中的默认数据和错误值

以前,从 useFetchuseAsyncData 返回的 dataerror 对象初始化为 null。在 Nuxt 4 中,dataerror 对象的默认值变为 undefined

如果你的代码检查 data.valueerror.value 是否为 null,应将这些检查更新为检查 undefined

也可以通过以下配置将默认值恢复为 null

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

3. 配置 Nuxt 4 中 useAsyncData 和 useFetch 的去重行为

Nuxt 的 useAsyncDatauseFetch 函数中的 dedupe 选项允许你控制 Nuxt 如何处理重复的数据获取请求。这在有多个组件或页面需要获取相同数据时特别有用。

在 Nuxt 4 中,配置去重行为的方式有所更新。之前,可以在调用 refresh 方法时传递布尔值来配置去重选项:

// Nuxt 3
const { refresh } = await useAsyncData(() => $fetch('/api/endpoint'))

async function refreshData() {
  await refresh({ dedupe: true }) // 等同于 'cancel'
  await refresh({ dedupe: false }) // 等同于 'defer'
}

但是,这种布尔值语法容易引起混淆,因为它们表示的是与 dedupe 选项的含义相反的操作。为了提高清晰度,Nuxt 4 移除了这些布尔别名。

现在,你可以使用以下方式明确指定去重行为:

// Nuxt 4
const { refresh } = await useAsyncData(() => $fetch('/api/endpoint'))

async function refreshData() {
  await refresh({ dedupe: 'cancel' }) // 取消现有请求
  await refresh({ dedupe: 'defer' }) // 等待现有请求完成后再执行新的请求
}

4. 在 Nuxt 4 中清除数据时保留默认值

以前,当调用 clearclearNuxtData 来清除通过 useAsyncDatauseFetch 获取的数据时,数据会被重置为 undefined,无论你为其指定了什么默认值。

// Nuxt 3
const { data, clear } = await useAsyncData("data-key", () => 
    $fetch("api/greeting"),
    { default: () => ({ message: "Default message" }) });

clear(); // 数据现在是 undefined

现在,如果你在使用 useAsyncData 时提供了自定义的默认值,则在调用 clearclearNuxtData 时会使用该值。这确保了数据会重置为一个适当的预定义状态,而不是简单地被清除。

// Nuxt 4
const { data, clear } = await useAsyncData("data-key", () => 
    $fetch("api/greeting"),
    { default: () => ({ message: "Default message" }) });

clear(); // 数据重置为 { message: 'Default message' }

这种行为有助于避免在遍历数据时需要检查 nullundefined

在大多数情况下,这一变化无需进行任何迁移。现有的代码在清除通过 useAsyncDatauseFetch 获取的数据时应该会继续按预期工作。

如果遇到问题,你可以通过在 nuxt.config.ts 中添加以下配置来恢复之前的行为(重置为 undefined):

export default defineNuxtConfig({
  experimental: {
    resetAsyncDataToUndefined: true
  }
})

5. Nuxt 4 中的浅数据响应性

以前,通过 useAsyncDatauseFetchuseLazyAsyncDatauseLazyFetch 获取的 data 对象是一个 ref,而 ref 默认是深度响应的。这意味着对数据结构的任何更改都会触发应用中的响应更新。

// Nuxt 3
const { data } = await useFetch('/api/test')
data.value.greeting.message = 'Updated message' // 触发响应性

在 Nuxt 4 中,data 对象变成了 shallowRef,这意味着对数据结构中的嵌套属性的更改不会触发响应性。

// Nuxt 4
const { data } = await useFetch('/api/test')
data.value.greeting.message = 'Updated message' // 不触发响应性

这一变化显著提高了性能,尤其是在处理深度嵌套的数据结构时。使用 shallowRef 后,Vue 不再需要监视每一个属性或数组元素的修改,从而减少了整体开销。

如果你的代码依赖于 data 对象的深度响应性,你有两种选择:

  • 在每个请求中启用深度响应性:
const { data } = useFetch('/api/test', { deep: true })
data.value.message = 'Updated message' // 触发响应性
  • 全局更改项目的默认行为(不推荐)
// nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    defaults: {
      useAsyncData: {
        deep: true
      }
    }
  }
})

Nuxt 4 中实验性功能的移除

在从 Nuxt 3 升级到 Nuxt 4 时,需要了解一些实验性功能的变化。这些功能要么已经被正式采纳,要么被弃用,因此不再可配置。这些改动旨在简化 Nuxt 的配置并提升一致性,尤其是因为这些实验性功能在 Nuxt 3 的多个版本中已经逐步趋于稳定。

以下是已经标准化、且在 Nuxt 4 中不可调整的实验性功能:

  1. experimental.treeshakeClientOnly:现在始终为 true(自 Nuxt 3.0 起默认如此)。这可以确保仅限客户端使用的组件会被“树摇优化”,从而提升构建性能。

  2. experimental.configSchema:默认设置为 true(自 Nuxt 3.3 起)。这会强制使用配置模式以更好地验证配置并处理错误。

  3. experimental.polyfillVueUseHead:默认设置为 false(自 Nuxt 3.4 起)。这个变化影响了 Vue 的 useHead 填充功能(polyfill)。

  4. experimental.respectNoSSRHeader:默认设置为 false(自 Nuxt 3.4 起)。这个功能与处理 SSR(服务端渲染)头信息有关,现在已不可配置。

  5. vite.devBundlervite-node 捆绑器现在是唯一的选项,因此不需要再为开发环境的捆绑配置额外设置。

这些改动表明,Nuxt 4 更加注重稳定性和一致性,同时也简化了配置流程。

注意事项

如果你的项目使用了 Nuxt 3 的模块或层(Modules 和 Layers),升级到 Nuxt 4 后可能需要进行一些调整,因为 Nuxt 4 在核心功能、组合式 API(composables)等方面引入了新变化。

总结

以上就是从 Nuxt 3 迁移Nuxt 4 的步骤以及Nuxt 4 的一些新特性,包括新的目录结构、组件名称标准化、路由元数据访问方式调整、页面元数据扫描时机优化以及更高效的数据获取机制。这些改动旨在提升项目的可维护性和性能,同时为开发者提供更清晰的代码组织方式。

最后让我们一起期待Nuxt4的发布吧~~

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
Hedy
大家好!我是一位前端开发工程师,拥有6年以上的前端开发经验。我熟练掌握HTML、CSS、JavaScript等语言,能够灵活运用各种前端框架,如Vue、React、Uniapp、Flutter等。我注重理论与实践相结合,能够为学员提供丰富的案例和实践项目,并以生动、易懂的语言为学员讲解前端开发的核心知识和技能。我不仅注重传授技能,更关注学员的职业发展,希望通过我的教学,帮助学员成为一名优秀的前端开发工程师。

评论(0)

添加评论