首页
/ Nuxt 模块作者迁移指南:让 Nuxt 2 模块兼容 Nuxt 3(@nuxt/kit、Nuxt Bridge 与 ESM 改造实践)

Nuxt 模块作者迁移指南:让 Nuxt 2 模块兼容 Nuxt 3(@nuxt/kit、Nuxt Bridge 与 ESM 改造实践)

2026-09-07 14:48:13作者:蔡怀权

模块(module)是扩展 Nuxt 能力的标准方式,而 Nuxt 3 是一次彻底重写:从 Vue 2 迁移到 Vue 3、从 webpack 4 + Babel 迁移到 Vite/webpack 5 + esbuild,更重要的是把 Nuxt 从"运行时依赖"变为"仅构建期依赖"。这篇指南以 Nuxt 官方迁移文档《Modules》为骨架,面向仍然维护着 Nuxt 2 生态模块的作者,系统讲解在保留既有代码的同时让模块兼容 Nuxt 3 的最小改造成路径:模块兼容层与兼容性边界、@nuxt/kit 对模块容器的替代、通过 Nuxt Bridge 先行验证、CommonJS 到 ESM 的迁移、插件默认导出修正,以及"避免运行时模块"的关键设计约束。读完你既能照单执行每一条改造步骤,也能理解 Nuxt 模块系统在构建期的真实运行机制。

模块兼容性(Module Compatibility)总览

Nuxt 3 为 Nuxt 2 模块提供了一个基础向后兼容层:借助 @nuxt/kit 的自动包装(auto wrapper),一部分旧的模块调用方式仍能工作。但官方明确强调:这只是兜底,通常还需要你手动做若干适配,才能真正兼容 Nuxt 3;在某些场景下,甚至必须引入 Nuxt Bridge 才能实现"同一份代码同时兼容 Nuxt 2 与 Nuxt 3"。

目前官方推荐的最优迁移路径是参考仓库中的模块编写指南(模块指南入口),使用 @nuxt/kit 提供的工具函数把模块整体重写。本迁移指南(docs/7.migration/20.module-authors.md)则提供另一套思路:如果你暂时不想完全重写,可以只做本节列出的准备工作,先让既有模块在 Nuxt 3 下可用,再逐步演进。

在动手前,请先对照下面两个兼容性边界判断你的模块受影响面:

插件兼容性(Plugin Compatibility)

Nuxt 3 插件并非与 Nuxt 2 完全向后兼容。二者的插件系统差异较大:

  • Nuxt 2 中插件通过 this.$xxx 注入到 Vue 实例,并依赖 context 参数(如 approutestore);
  • Nuxt 3 中插件接收 nuxtApp 参数(详见 plugins 目录文档),一切能力通过 nuxtApp.provide、组合式函数与运行时钩子暴露。

如果你注入的是一个"全局 Vue 插件"性质的 Nuxt 插件,除了改参数签名,还必须保证插件文件存在默认导出(详见下文"确保插件默认导出"一节)。

Vue 兼容性(Vue Compatibility)

凡是使用 Composition API 的插件或组件,都需要独占的 Vue 2 或 Vue 3 支持——Nuxt 2 构建在 Vue 2 上,Nuxt 3 构建在 Vue 3 上,两者运行时不能混用。

让同一份代码同时服务 Nuxt 2 与 Nuxt 3 的常规做法是引入 vue-demi(VueUse 团队维护的"半版本"兼容库):它会根据宿主应用实际安装的 Vue 主版本,自动切换到对应的 @vue/composition-api 或原生 Vue 3 API,从而让基于 Composition API 的插件与组件在两端行为一致。

迁移第一步:告别模块容器,拥抱 @nuxt/kit

当 Nuxt 3 用户安装你的模块时,模块将不再能访问模块容器 this.*(Nuxt 2 时代的 this.optionsthis.nuxtthis.addPlugin()this.extendBuild() 等成员方法)。作为替代,模块作者需要使用 @nuxt/kit 暴露的工具函数来访问容器提供的全部功能。

这一变化在仓库源码中有清晰印证:@nuxt/kit 的公共导出(见 packages/kit/src/index.ts)不再提供"模块容器实例"式的 API,而是提供一批独立函数,例如:

  • 模块定义:defineNuxtModuleinstallModule
  • 插件/组件注入:addPluginaddPluginTemplateaddComponentaddComponentsDiraddLayout
  • 页面与路由:extendPagesaddRouteMiddlewareextendRouteRules
  • 服务端(Nitro):addServerHandleraddDevServerHandleraddServerPlugin
  • 配置与模板:addTemplateupdateRuntimeConfigextendViteConfigextendWebpackConfig
  • 兼容性:checkNuxtCompatibilityassertNuxtCompatibilityhasNuxtCompatibility

官方强烈推荐用 defineNuxtModule 定义模块。底层模块定义本身只是一个接收 (inlineOptions, nuxt) 的函数(模块结构文档):

export default defineNuxtModule({
  meta: {
    name: '@nuxtjs/example',   // 通常是 npm 包名
    configKey: 'sample',       // nuxt.config 中承载模块选项的键
    compatibility: { nuxt: '>=3.0.0' },
  },
  defaults: {},
  setup (moduleOptions, nuxt) {
    // 模块逻辑
  },
})

packages/kit/src/module/define.ts 的实现看,defineNuxtModule 会为模块自动完成以下工作:

  • defu 合并 inlineOptionsnuxt.configconfigKey 对应的选项与 defaults(并可基于 schema 进一步校验默认值);
  • 依据 meta.name/meta.configKey 生成唯一键,通过 nuxt.options._requiredModules 保证模块只被安装一次(见 normalizedModule 中的去重分支);
  • 自动把 hooks 字段注册到 Nuxt 生命周期钩子上(nuxt.hooks.addHooks);
  • 基于 meta.compatibility 自动执行兼容性检查,不满足且开启 experimental.enforceModuleCompatibility 时直接抛出 ModuleCompatibilityError
  • setup 执行前后为 nuxt._perf 记录 module:* 性能阶段;
  • 挂载 getMeta/getOptions/getModuleDependencies 等内部辅助方法。

这些自动化行为正是 Nuxt 2 模块容器中"手写合并选项、手动防重装"等样板代码的替代品——迁移模块本质上就是把旧容器 API 的调用,逐个翻译成 @nuxt/kit 的独立函数调用。

先用 @nuxt/bridge 验证

官方把"迁移到 @nuxt/bridge"称为支持 Nuxt 3 的第一步也是最关键的一步。Nuxt Bridge 是一个前向兼容层(详见 Bridge 概览):在 Nuxt 2 项目中安装并启用 @nuxt/bridge 模块后,即可提前体验并接近 Nuxt 3 的能力,同时保留在 Nuxt 2 上运行的能力。

如果你的模块带有 fixture 或示例项目,请把 @nuxt/bridge 加入其配置。例如在示例应用的 nuxt.config.ts 中(参照 Bridge 配置示例 的"更新 nuxt.config"部分):

// nuxt.config.ts —— 使用 ESM 语法,避免 module.exports/require
import { defineNuxtConfig } from '@nuxt/bridge'

export default defineNuxtConfig({
  bridge: false, // 先关闭,验证无 Bridge 时行为不变
})

并在项目里安装依赖与切换命令:

# 安装(在 Nuxt 2 项目内执行)
npm install -D @nuxt/bridge nuxi
   "scripts": {
-    "dev": "nuxt",
+    "dev": "nuxt2",
-    "build": "nuxt build",
+    "build": "nuxt2 build",
-    "start": "nuxt start",
+    "start": "nuxt2 start"
   }

注意:Bridge 虽然提供与 Nuxt 3 几乎一致的特性,但存在限制,例如 useAsyncDatauseFetch 组合式函数不可用。把模块放进启用 Bridge 的 fixture 中跑通,能尽早暴露"只兼容 Nuxt 2、不兼容 Nuxt 3 插件/组合式 API"的问题。

从 CommonJS 迁移到 ESM

Nuxt 3 原生支持 TypeScript 与 ECMAScript Modules,模块代码必须能在一个原生 ESM 的 Node.js 上下文里被加载。这意味着:

  • 优先用静态 import / export 语法,而不是 require / module.exports
  • 不要把构建产物命名为 .esm.js.es.js 之类只对打包器有效的约定——Node.js 原生 ESM 只认 .mjs(ESM)、.cjs(CJS),以及 package.json 中声明了 "type": "module".js 文件;
  • 建议在 package.json 中使用带条件导出的 exports 字段精确声明模块系统。
{
  "name": "my-module",
  "exports": {
    ".": {
      "import": "./dist/mymodule.mjs"
    }
  }
}

在 ESM 环境下 requirerequire.resolve__filename__dirname 均不可用,需要替换为 import()import.meta.url 对应的写法。ESM 与 CJS 的完整背景、报错形式(如 SyntaxError: Unexpected token 'export'、Named export not found)以及互操作(interop)陷阱,参见仓库中的 原生 ES Modules 概念文档,其中还包含面向库作者的两条修复路径:将 ESM 文件重命名为 .mjs,或让整个库只提供 ESM 输出。

确保插件默认导出

如果你通过模块注入的 Nuxt 插件没有 export default(典型的如直接调用 Vue.use() 的全局 Vue 插件),那么这种插件在 Nuxt 3 中可能根本不会被执行——Nuxt 3 期望每个插件文件默认导出一个插件函数。修复方式是在插件文件末尾补上一个空的默认导出:

::: code-group

// ~/plugins/vuelidate.js
import Vue from 'vue'
import Vuelidate from 'vuelidate'

Vue.use(Vuelidate)
// ~/plugins/vuelidate.js
import Vue from 'vue'
import Vuelidate from 'vuelidate'

Vue.use(Vuelidate)

export default () => { }

:::

迁移到 Nuxt 3 后,这类代码还应当进一步改造成新的插件形态(接收 nuxtApp 并调用 nuxtApp.vueApp.use(Vuelidate)),完整写法参考 plugins 目录文档

避免运行时模块(Avoid Runtime Modules)

Nuxt 3 中 Nuxt 只是一个构建期依赖,模块不应试图在 Nuxt 运行时里"挂钩子"或"常驻运行"。这意味着你的模块应当做到:即使只被添加到 buildModules(而不是 modules)也能正常工作。本指南列出三类需要避开的运行时耦合,全部可以用 Nuxt 3 官方机制替代:

用 runtimeConfig 替代 process.env 读写

不要在模块构建期写 process.env、再让运行时插件读取它。环境相关的配置应交给 runtimeConfig

// 模块内(构建期)不再这样写:
// process.env.MY_FLAG = '1'

// 改为:通过 @nuxt/kit 更新 runtimeConfig
import { defineNuxtModule, updateRuntimeConfig } from '@nuxt/kit'

export default defineNuxtModule({
  setup (_options, nuxt) {
    updateRuntimeConfig({
      myModule: { flag: process.env.MY_FLAG || '0' },
    })
  },
})

runtimeConfig 的值在构建期被序列化进应用,运行时通过 useRuntimeConfig() 读取,从而切断"模块→全局进程环境→插件"这条运行时依赖链。更深入的用法见 运行时配置指南

不要依赖生产环境的运行时钩子

诸如 vue-renderer:* 之类的运行时渲染钩子仅存在于 Nuxt 2 的运行时架构中,Nuxt 3 已不存在对应的运行时容器。除非纯粹用于 nuxt dev 开发模式,并且用 if (nuxt.options.dev) { } 显式包裹(如下代码所示),否则应避免在模块中挂钩 vue-renderer:* 这类运行时钩子用于生产环境。

export default defineNuxtModule({
  setup (_options, nuxt) {
    if (nuxt.options.dev) {
      // 仅在开发模式挂接运行时相关逻辑
    }
  },
})

不要"在模块里 import" serverMiddleware,改用文件路径引用

serverMiddleware 直接 import 进模块代码会让中间件与模块的模块系统上下文(打包产物、闭包环境)强绑定,导致无法在 Nuxt 3 的独立服务端运行时中加载。正确做法是引用文件路径,让 Nuxt 在构建期自行解析与打包这些文件,从而保证服务端中间件与模块上下文解耦。

在 Nuxt 3 中更推荐直接通过 @nuxt/kit 的 Nitro 工具注册服务端逻辑(见 packages/kit/src/index.ts 中导出的 addServerHandler 等),它天然使用路径引用、完全构建期化。

上述例外规则((*))统一为:除非仅用于 nuxt dev 且以 if (nuxt.options.dev) { } 守卫,否则一律避免。

(可选)迁移到 TypeScript

这一步骤不是强制项,但 Nuxt 生态正在整体转向 TypeScript,官方强烈建议考虑迁移,理由很实在:

  • 可以直接把 .js 文件改名为 .ts 开始——TypeScript 是渐进式的,现有代码几乎不需要改动即可逐步获得类型检查;
  • Nuxt 2 与 Nuxt 3 的模块与插件都支持直接书写 TypeScript 语法,无需额外依赖即可编写和发布带类型的模块代码(配合 defineNuxtModule 的泛型推断,模块选项也能获得自动补全与类型校验)。

仓库本身的强类型实现就是现成参照:例如 defineNuxtModule 的类型签名定义于 packages/kit/src/module/define.ts,模块的 meta、选项、运行时钩子等类型则沉淀在 packages/schema/src/types/module.ts

兼容性检查:模块 meta 背后的机制

最后补充一个贯穿全文的机制细节:meta.compatibility 并非摆设。在 packages/kit/src/compatibility.tscheckNuxtCompatibility 实现中,Nuxt 会用 verkitsatisfiesmeta.compatibility 声明的约束做语义化版本匹配(并自动剔除 nightly 的边缘版本前缀),同时支持对 Nuxt、builder(@nuxt/vite-builder/@nuxt/webpack-builder/@nuxt/rspack-builder)与 Nitro 三个维度做版本约束检查。这与 define.ts 中"兼容性不满足则禁用模块或抛出 ModuleCompatibilityError"的逻辑闭环——所以给模块正确声明 compatibility: { nuxt: '>=2.0.0' } 之类约束,是实现 Nuxt 2/Nuxt 3 双端兼容声明的一部分。

小结与推荐阅读

把 Nuxt 2 模块改造成 Nuxt 3 兼容版本,核心可以归纳为五步行动清单:

  1. 引入 @nuxt/bridge 到你的 fixture/示例项目,先跑通双版本验证环境(Bridge 概览);
  2. @nuxt/kit 替代模块容器 this.*,用 defineNuxtModule 重新组织模块定义;
  3. 完成 ESM 迁移原生 ESM 指南),调整 package.json 导出与 Node 兼容写法;
  4. 为所有注入的 Nuxt 插件补上默认导出
  5. 消除运行时依赖process.envruntimeConfig、去掉生产环境的 vue-renderer:* 钩子、serverMiddleware 改为路径引用,并用 nuxt.options.dev 守卫仅限开发的功能。

新模块的完整开发流程(模板初始化、playground 调试、测试、打包与发布)可继续阅读 创建你的第一个模块;模块的目录组织、runtime/ 目录职责、注入运行时资产(组件、组合式函数、插件、Nitro 路由与中间件)等,参见 模块结构解析。希望这套"先兼容、再重写"的策略能帮你平稳度过 Nuxt 2 → Nuxt 3 的生态切换期。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388