news 2026/9/6 23:16:32

Nuxt 4 中 shared/ 目录深度解析:在 Vue 应用与 Nitro 服务器之间共享工具与类型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Nuxt 4 中 shared/ 目录深度解析:在 Vue 应用与 Nitro 服务器之间共享工具与类型

Nuxt 4 中 shared/ 目录深度解析:在 Vue 应用与 Nitro 服务器之间共享工具与类型

【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt

本文围绕 Nuxt 的shared/目录展开:它是自 Nuxt v3.14+ 起引入的官方目录约定,让你把可以被Vue 应用(客户端 + 服务端渲染)和 Nitro 服务器(API 路由、服务端中间件、服务端插件)同时使用的工具函数与类型放在同一处,并在两侧自动导入。读完本文,你将理解shared/的目录扫描规则、#shared别名机制、共享代码的导入边界(为什么不能混用 Vue 与 Nitro 代码),以及多层(layers)场景下的共享目录行为——所有结论均对应 Nuxt 开源仓库中的实际源码与测试。

shared/ 目录解决什么问题

Nuxt 在构建时会产出两个独立的 bundle

  • Vue 应用:包括客户端代码与服务端渲染(SSR)代码,依赖 Vue 运行时和 Nuxt 上下文;
  • Nitro 服务器:承载server/api路由、server/middlewareserver/plugins等,运行在服务端 Node 环境。

两者独立打包、运行在不同上下文。过去如果你想在应用和服务器间共用一段纯函数或类型,只能手动重复维护或用别名互相引用。shared/目录就是为此设计的共享边界:其中的代码可以被两个 bundle 同时使用,但它自身不能从任何一个 bundle 导入任何东西

// 一个典型的共享工具:无 Vue、无 Nitro 依赖的纯函数 export const capitalize = (input: string) => { return input[0] ? input[0].toUpperCase() + input.slice(1) : '' }

为什么不能混用 Vue 与 Nitro 代码

这是使用shared/前必须理解的硬性约束:shared/目录中的代码不能导入任何 Vue 或 Nitro 代码。官方文档(docs/2.directory-structure/1.shared.md)给出的理由在源码层面有明确支撑。

把 Vue 应用代码带进 Nitro

组件与 composables 依赖 Vue 应用运行时和 Nuxt 上下文(如useNuxtApp()useRoute()),这些在 Nitro 环境中都不存在。把它们导入服务器代码会导致构建或运行时错误,还可能把 Vue 应用的依赖拉进服务器 bundle。

把 Nitro 代码带进 Vue 应用

服务端专用代码(Node API、Nitro 工具、服务器路由处理器)绝不能跑在浏览器里。把它们导入应用会破坏客户端构建、在浏览器中引发运行时错误,或让服务端逻辑泄漏进客户端 bundle。

类型导入的边界

import type会在编译期被擦除,不会把运行时代码带入另一个 bundle,因此跨边界"只导入类型"表面上可以工作。但 Nuxt 仍建议把共享类型(如 API 响应类型)放进shared/types/——它们会在两个上下文中被自动导入。这样既保持边界清晰、避免日后把类型导入误改为值导入,也与 Nuxt 为 app、server、shared 代码区分的独立类型上下文(见 docs/3.guide/1.concepts/8.typescript.md)保持一致。

只被单侧使用的类型应放在对侧旁边:app/types/仅在 Vue 应用中被自动导入,server/types/(见 docs/2.directory-structure/1.server.md)仅在 Nitro 服务器中被自动导入;只有两侧都需要时才用shared/types/

源码级证据:导入保护(import protection)

Nuxt 并非只靠文档约束这一边界,而是在构建层面强制执行。从源码结构看,packages/nuxt/src/core/plugins/import-protection.ts 定义了三种上下文:'nuxt-app' | 'nitro-app' | 'shared',并为shared上下文生成双向禁止规则:

  • shared代码中禁止导入 Vue 应用别名#app#build("Vue app aliases are not allowed in the #shared directory.");
  • shared代码中禁止从server/apiserver/routesserver/middlewareserver/plugins#server别名导入("Server aliases are not allowed in the #shared directory."),并提示改用$fetch()/useFetch()调用服务端端点,或"把共享逻辑移到shared/目录"。

该保护器通过ImpoundPlugin挂入构建管线,分别匹配#shared/shared绝对路径与相对路径三种写法(见 packages/nuxt/src/core/nuxt.ts 中的addBuildPlugin调用),因此无论你的共享文件用哪种方式引用外部代码,越界都会在构建期被拦截,而不只是文档上的一行警告。

基本用法:命名导出与默认导出

shared/utils/(或shared/types/)下创建工具文件,Nuxt 会在应用与服务器两侧自动导入其导出,无需手动 import。文档给出两种写法:

方式一:命名导出

export const capitalize = (input: string) => { return input[0] ? input[0].toUpperCase() + input.slice(1) : '' }

方式二:默认导出

export default function (input: string) { return input[0] ? input[0].toUpperCase() + input.slice(1) : '' }

创建后,你可以直接在 Nuxt 应用和server/目录中使用这个自动导入的工具:

<script setup lang="ts"> const hello = capitalize('hello') </script> <template> <div> {{ hello }} </div> </template>
export default defineEventHandler((event) => { return { hello: capitalize('hello'), } })

自动导入的底层扫描逻辑

"两侧自动导入"是两套扫描共同作用的结果,从源码看可以确认:

  • 应用侧:packages/nuxt/src/imports/module.ts 在收集自动导入目录时,遍历所有 layer,把shared/utilsshared/types一并推入composablesDirs(与composablesutilstypes同列),随后通过imports:dirs钩子交由 auto-imports 机制处理。因此shared/utils/shared/types的扫描方式与app/composables/app/utils/完全一致(详见 docs/3.guide/1.concepts/3.auto-imports.md)。
  • 服务器侧:packages/nitro-server/src/index.ts 中会把每个 layer 的shared/utilsshared/types加入 Nitro 的importDirs,让 Nitro 同样自动导入这些导出。

也就是说,你不需要写任何额外配置,capitalizeapp/server/中都是"开箱即用"的。

文件是如何被扫描的:只有两个子目录参与自动导入

只有shared/utils/shared/types/目录中的文件会被自动导入。这两个目录的子目录里嵌套的文件默认不会被自动导入,除非你把对应子目录加入imports.dirs(应用侧)和nitro.imports.dirs(服务器侧)配置。

-| shared/ ---| capitalize.ts # 不会被自动导入 ---| formatters -----| lower.ts # 不会被自动导入 ---| utils/ -----| lower.ts # 会被自动导入 -----| formatters -------| upper.ts # 不会被自动导入 ---| types/ -----| bar.ts # 会被自动导入

这张图传达了一个容易踩坑的细节:只有"一层"有效——utils/直接子文件自动导入,但utils/formatters/里的文件不会;shared/根目录下的文件(如capitalize.ts)也不会。需要深层文件时,有两条路:

  1. 扩大扫描范围:将子目录加入imports.dirsnitro.imports.dirs,让两侧都扫描到;
  2. 手动导入(更常用):使用 Nuxt 自动配置的#shared别名,见下一节。

使用 #shared 别名手动导入

shared/下创建的其他文件(不在自动导入范围内的文件)必须通过#shared别名手动导入,这个别名由 Nuxt 自动配置。别名解析逻辑在 packages/schema/src/config/common.ts 的alias.$resolve中:'#shared'被解析为resolve(rootDir, dir.shared)并带尾部斜杠——也就是说它指向项目根目录下的 shared 目录(可通过dir.shared配置项改名),无论你的文件位于app/还是server/,导入路径都保持一致:

// 直接位于 shared 根目录的文件 import capitalize from '#shared/capitalize' // 嵌套目录中的文件 import lower from '#shared/formatters/lower' // utils 内嵌套文件夹中的文件 import upper from '#shared/utils/formatters/upper'

多层(Layers)场景下的 shared/

如果你使用 Nuxt 的 layers /extends机制,每一层都有自己的shared/目录,它们会被逐层合并扫描。从源码结构看:

  • packages/kit/src/layers.ts 在归一化每层目录时,会为每个 layer 解析shared目录(相对该层 root 解析,支持别名);
  • 前述应用侧与 Nitro 侧的扫描循环都是for (const layer of nuxt.options._layers),即按 layer 顺序收集每层的shared/utilsshared/types

这一点有测试直接验证:packages/nuxt/test/shared-dir-config.test.ts 断言最终生效的导入目录包含根目录与各 extends/layers 层(如extends/bar/shared/utilslayers/bar/shared/types)的shared/utilsshared/types路径,并特别注明 "shared/types在两个上下文中都可用,server/types则仅限服务器"。类型测试 fixture test/fixtures/basic-types/shared/shared-types.ts 也展示了在双上下文共享类型的实际用法。

关键配置与规则速查

配置/规则说明默认值/取值
dir.shared共享目录名(相对 rootDir)shared(见 packages/schema/src/config/common.ts 中$resolve
#shared别名手动导入共享代码的统一入口自动配置,指向resolve(rootDir, dir.shared)
自动导入目录应用侧与 Nitro 侧共同扫描shared/utils/shared/types/(一层)
深层文件不自动导入加入imports.dirs+nitro.imports.dirs,或用#shared手动导入
导入边界shared代码禁止导入#app/#build/#server/ 服务器目录由 import protection 在构建期强制(packages/nuxt/src/core/plugins/import-protection.ts)
版本要求shared/目录自 Nuxt v3.14+ 可用当前仓库为 Nuxt 4.x 文档体系

小结

shared/目录的本质是 Nuxt 官方为"双 bundle 架构"划出的中立共享区:把跨端复用的纯函数与类型集中管理,靠shared/utilsshared/types的双侧自动导入省去样板代码,靠#shared别名覆盖自动导入之外的所有文件,靠 layers 机制让 monorepo 式的多层项目各自扩展共享代码,再由构建期的 import protection 确保这条边界不会被误用。对开发者而言,实践建议很简单:共享的"逻辑"放shared/utils,共享的"契约"放shared/types,任何依赖 Vue 或 Nitro 运行时的代码都留在各自一侧,通过 HTTP($fetch/useFetch)而非 import 通信。

【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 23:16:27

PHU插件实操指南:Photoshop图片批量处理与工作流优化

简介&#xff1a;这是一份面向4G/5G无线网络优化工程师的前景培训教材&#xff0c;聚焦华为PHU-Smart手机路测APP及GC平台实操&#xff0c;解决测试任务下发、工参导入、数据回传与报告生成等日常痛点。资源为单个PDF压缩包&#xff0c;大小4.29MB&#xff0c;内容共13页&#…

作者头像 李华
网站建设 2026/9/6 23:12:42

STM32声光驱鸟系统设计:从硬件选型到状态机实现

简介&#xff1a;一份基于STM32的声光驱鸟系统毕业设计资料&#xff0c;面向计算机、电子信息及自动化专业学生&#xff0c;也可供嵌入式开发者借鉴。系统以声光协同、自动感应为核心思路&#xff0c;以STM32单片机为控制核心&#xff0c;通过微波感应雷达检测鸟类活动&#xf…

作者头像 李华
网站建设 2026/9/6 23:12:15

WVP-PRO国标视频平台通道录像配置实战指南

这次我们直接看一个目前实战中很常见的国标视频平台&#xff1a;WVP-PRO。 它是完全免费开源的国标 GB28181 视频接入平台&#xff0c;核心功能就是把海康、大华、宇视这类支持国标协议的摄像头或 NVR 接入到统一的 Web 管理界面里&#xff0c;然后通过浏览器直接看直播、回放…

作者头像 李华
网站建设 2026/9/6 23:05:21

VM是什么?VMware、JVM与Node.js沙箱全解读

不管你是刚开始折腾 VMware Workstation 的新手&#xff0c;还是已经在用 VirtualBox 做实验的老手&#xff0c;只要在搜索引擎里敲下“VM”这两个字母&#xff0c;大概率都会遇到同一个困惑&#xff1a;为什么搜出来的东西千奇百怪&#xff0c;有讲虚拟机的&#xff0c;有报 J…

作者头像 李华