antd-mobile 服务端渲染(SSR)接入指南:Next.js 12/13 与 Remix 完整配置
2026/9/23 15:41:12 网站建设 项目流程

antd-mobile 服务端渲染(SSR)接入指南:Next.js 12/13 与 Remix 完整配置

【免费下载链接】ant-design-mobileEssential UI blocks for building mobile web apps.项目地址: https://gitcode.com/gh_mirrors/an/ant-design-mobile

服务端渲染(SSR)是移动端 H5 应用提升首屏体验与 SEO 的常见手段,而 antd-mobile 作为面向移动 Web 的 React 组件库,其 SSR 支持目前仍处于实验(Experimental)阶段,需要在 Next.js 与 Remix 中做额外配置才能正常工作。本文以 docs/guide/ssr.en.md 为核心,结合 antd-mobile 源码中的 DOM 访问逻辑与构建产物结构,完整讲解在 Next.js 12、Next.js 13 以及 Remix 中接入 antd-mobile 的每一步配置,并说明 SSR 环境下必须注意的坑点。读完本文,你将能够独立完成上述三大框架的 SSR 集成,并理解这些配置背后的原理。

SSR 支持现状:实验性能力与已知边界

antd-mobile 官方文档明确标注,其对 SSR(服务端渲染)的支持"还处在比较初始的阶段"(still in the initial stage),如果你在使用过程中发现了 bug,欢迎向官方提交 issue。这意味着 SSR 场景并非组件的默认一等公民,以下三类问题需要开发者自行留意:

  1. 编译产物问题:antd-mobile 的源码与发布产物中包含 ESM/TSX 形式的内容(如 src 目录 下的组件源码以及es构建目录),Node 环境无法直接解析,必须通过转译(transpile)才能运行。
  2. 浏览器 API 依赖问题:组件库内部大量使用windowdocumentnavigator等浏览器全局对象,服务端渲染阶段这些对象并不存在。
  3. 样式与主题初始化问题:antd-mobile 通过 src/global/global.less 注入全局样式与 CSS 变量,SSR 时需要确保样式文件被正确加载。

因此,官方文档给出了针对各框架的"最小可用配置",这也是本文接下来要逐一展开的内容。

理解配置背后的原理:antd-mobile 的 DOM 访问与产物结构

在动手配置之前,先理解两条关键事实,它们直接决定了为什么 Next.js 需要next-transpile-modules/transpilePackages,而 Remix 需要 path alias。

浏览器全局对象的守卫:canUseDom

antd-mobile 内部对 SSR 并非毫无准备,其几乎所有涉及 DOM 的工具都通过 src/utils/can-use-dom.ts 进行环境守卫:

export const canUseDom = !!( typeof window !== 'undefined' && typeof document !== 'undefined' && window.document && window.document.createElement )

从源码结构看,这一守卫被大量工具复用:例如 src/utils/convert-px.ts 在设置根字体大小时会先判断!canUseDom || !document.body再执行;src/utils/get-scroll-parent.ts 会以canUseDom ? window : undefined作为默认滚动根;src/utils/validate.ts 中通过canUseDom判断后才读取navigator.userAgent做平台检测;src/utils/supports-passive.ts 同样在canUseDom为真时才探测 passive event listener 支持。

此外,src/global/index.ts 中为移动端 Safari 修复:active伪类而注册的空touchstart监听,也包裹在if (canUseDom)之内:

import './global.less' import { canUseDom } from '../utils/can-use-dom' if (canUseDom) { // Make sure the `:active` CSS selector of `button` and `a` take effect document.addEventListener('touchstart', () => {}, true) }

这说明:只要保证组件代码在 Node 侧只被"解析、转译"而不在渲染阶段真正触发 DOM 副作用(例如弹窗类组件使用 src/utils/render-to-body.ts 动态创建容器,仅会在客户端执行),SSR 的初始渲染即可安全进行。这也是为何各框架配置的核心目标都是"让服务端能正确解析并打包 antd-mobile"。

发布产物结构:为什么 Remix 需要 path alias

antd-mobile 的构建脚本定义在 gulpfile.js 中,其默认构建流水线会产出lib/es(ESM 模块)、lib/cjs(CommonJS 模块)以及lib/bundle(由 Vite 打包的 bundle,见 gulpfile.js 的getViteConfigForPackage)。其中lib/bundle下包含antd-mobile.es.js(ES bundle)与style.css(合并后的样式文件),这正是 Remix 配置中引用的路径来源。

同时,package.json 中的sideEffects字段声明了样式文件、./es/index.js./src/index.ts./src/global/index.ts等具有副作用,这意味着打包器在 tree-shaking 时不会错误地移除全局样式与初始化逻辑。

在 Next.js 12 中使用 antd-mobile

Next.js 12 时代,Next.js 默认不会转译node_modules中的依赖,而 antd-mobile 的发布产物中包含需要转译的代码,因此必须借助next-transpile-modules插件显式声明。

第一步:安装 next-transpile-modules

按官方文档,支持 npm / yarn / pnpm / bun 四种包管理器:

$ npm install --save-dev next-transpile-modules # or $ yarn add -D next-transpile-modules # or $ pnpm add -D next-transpile-modules # or $ bun add -D next-transpile-modules

第二步:配置 next.config.js

在项目根目录的next.config.js中包裹配置:

const withTM = require('next-transpile-modules')([ 'antd-mobile', ]); module.exports = withTM({ // other Next.js configuration in your project });

withTM接收一个包名数组,含义是让 Next.js 在编译时把antd-mobile当作项目源码一样进行转译,从而解决 Node 侧无法直接解析其 ESM 产物的问题。若项目中还依赖了其他需要转译的包,可以一并加入该数组。

在 Next.js 13 中使用 antd-mobile

Next.js 13 引入了transpilePackages配置项,可以自动转译并打包node_modules中的外部依赖,从而取代了next-transpile-modules插件。

配置 transpilePackages

// next.config.js const nextConfig = { transpilePackages: ['antd-mobile'], }; module.exports = nextConfig;

这是官方推荐的 Next.js 13 方案,配置更简洁,不再需要额外安装任何插件。

在 app 目录下使用:'use client' 指令

如果你的项目使用了 Next.js 13 的app目录(App Router),由于 antd-mobile 是客户端组件库(依赖windowdocument与 React 状态/事件系统),必须在使用 antd-mobile 组件的文件顶部添加'use client'指令,将其显式标记为客户端组件:

// app/page.jsx 'use client' import { Button } from 'antd-mobile'

否则 Next.js 会在服务端渲染阶段将该模块视为 Server Component 并尝试在 Node 环境中执行,从而触发浏览器 API 相关报错。注意:'use client'标记的是模块边界,app 目录下未直接使用 antd-mobile 的布局(layout)文件仍可保持服务端组件身份。

在 Remix 中使用 antd-mobile

Remix 默认在服务端使用 Node 运行时,对第三方 ESM 包的处理方式与 Next.js 不同,因此官方文档给出的方案是:通过tsconfig.json的路径别名直接指向 antd-mobile 的预打包 bundle,并手动引入样式。

第一步:配置 tsconfig.json 路径别名

tsconfig.jsoncompilerOptions.paths中新增 antd-mobile 配置,并在include字段中添加global.d.ts

{ "include": ["remix.env.d.ts", "global.d.ts", "**/*.ts", "**/*.tsx"], "compilerOptions": { ... "paths": { "antd-mobile": ["node_modules/antd-mobile/bundle/antd-mobile.es.js"] } } }

这里把antd-mobile解析到node_modules/antd-mobile/bundle/antd-mobile.es.js,即 gulpfile.js 中由 Vite 生成的 ES bundle,让 Remix 直接消费单文件打包产物,绕开对散装 ESM 目录的解析问题。

第二步:添加 global.d.ts 类型声明

由于路径别名指向的是 bundle 文件,TypeScript 无法直接从其推导出 antd-mobile 的类型,因此需要在项目根目录新增global.d.ts

declare module 'antd-mobile' { export * from 'antd-mobile/es'; }

该声明告诉 TypeScript:antd-mobile模块的类型与antd-mobile/es(即 package.json 中moduletypes字段指向的 ES 目录)一致,从而获得完整的组件类型提示与检查。

第三步:在 app/root.tsx 引入样式

antd-mobile 的全局样式在 bundle 中被合并为style.css,需要在 Remix 的根路由中通过links()函数将其作为样式表加载:

import styles from "antd-mobile/bundle/style.css"; export function links() { return [{ rel: "stylesheet", href: styles }]; }

links()是 Remix 根路由的专用导出,返回的样式表会在所有页面中生效。这一步与 src/global/global.less 中定义的全局 CSS 变量(如--adm-color-primary等主题令牌)相对应,缺少它会出现组件样式丢失、主题变量失效的问题。

常见 SSR 问题排查建议

结合 antd-mobile 源码实现,以下几点可以帮你快速定位 SSR 场景下的典型问题:

  1. 样式未生效:优先检查是否按上文在 Next.js 中正确引入(Next.js 会自动处理被转译包的 CSS)或在 Remix 根路由links()中加载了bundle/style.css。样式由 src/global/global.less 及各个组件的.less文件编译而来,gulpfile.js 的buildStyle任务会通过 Less 与 autoprefixer 处理它们。

  2. 水合(hydration)不一致报错:antd-mobile 的弹窗类组件(Dialog、Toast、Popup 等)通过 src/utils/render-imperatively.tsx 与 src/utils/render-to-body.ts 在运行时动态创建 DOM 容器,这类命令式 API 只应在客户端事件回调中调用,切勿在组件 render 阶段或服务端生命周期中执行,否则会导致服务端与客户端渲染结果不一致。

  3. 报 "document is not defined" 等浏览器 API 错误:说明组件代码被在 Node 环境中执行了。检查 Next.js 中是否遗漏'use client'指令或transpilePackages配置;Remix 中是否遗漏路径别名配置。可对照 src/utils/can-use-dom.ts 确认组件库自身的环境守卫是否生效——理论上正常配置下,服务端首屏渲染不会直接触发 DOM 副作用。

  4. SSR 期间触摸/滚动相关异常:antd-mobile 的弹层与滚动锁定逻辑(如 src/utils/use-lock-scroll.ts)依赖document.addEventListenerdocument.body.classList等浏览器 API,这些副作用均在useEffect中触发(仅在客户端执行),SSR 首屏不会执行,无需额外处理。

官方参考资源

  • 官方文档还提供了 Remix 模板仓库 作为参考(本文按规范不输出外部链接,可自行搜索"3lang3 antd-mobile-template"查看)。
  • Next.js 官方文档中关于transpilePackages与 Server/Client Components 的说明是理解上述配置的重要背景。
  • 若需要了解 antd-mobile 的快速上手、按需引入与预构建 bundle 等其他主题,可继续阅读仓库中的 docs/guide/quick-start.zh.md、docs/guide/import-on-demand.zh.md 与 docs/guide/pre-built-bundles.zh.md。

小结

antd-mobile 的 SSR 支持尚处实验阶段,但通过以下三组最小配置即可在主流框架中完成接入:

  • Next.js 12:安装next-transpile-modules并用withTM(['antd-mobile'])包裹配置;
  • Next.js 13:在next.config.js中配置transpilePackages: ['antd-mobile'],app 目录下使用组件时添加'use client'
  • Remix:在tsconfig.jsonpaths中把antd-mobile指向node_modules/antd-mobile/bundle/antd-mobile.es.js,新增global.d.ts声明类型,并在根路由links()中加载bundle/style.css

理解这些配置背后的原理(组件库的canUseDom环境守卫、Vite 生成的 bundle 产物结构、样式全局注入机制)后,即使未来升级框架版本,也能举一反三地完成迁移与排错。

【免费下载链接】ant-design-mobileEssential UI blocks for building mobile web apps.项目地址: https://gitcode.com/gh_mirrors/an/ant-design-mobile

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询