Vercel 部署 SSR 错误修复指南
问题描述
在 Vercel 部署时遇到以下错误:
Error: Can't render static file for pathname "/docs/Statistics/回归系数"
...
ReferenceError: self is not defined
at node_modules/@plotly/d3/d3.js
根本原因
Plotly.js 是一个纯客户端库,依赖浏览器的 window、self 等 API。在 Docusaurus 的静态站点生成(SSG)过程中,组件会在服务器端预渲染,而 Plotly.js 无法在 Node.js 环境中运行,导致构建失败。
解决方案
使用 Docusaurus 的 <BrowserOnly> 组件将 Plotly 组件包装起来,使其仅在客户端加载。
实施的修复
1. 创建客户端包装器组件
为每个 Plotly 组件创建了对应的包装器:
ClientSideInteractiveLinearRegression.jsClientSideMultipleRegression3D.jsClientSideCoefficientInterpreter.jsClientSideFeatureImportanceViz.js
每个包装器的结构:
import React from 'react';
import BrowserOnly from '@docusaurus/BrowserOnly';
const ComponentWrapper = () => {
return (
<BrowserOnly fallback={<div>Loading...</div>}>
{() => {
const Component = require('./OriginalComponent').default;
return <Component />;
}}
</BrowserOnly>
);
};
export default ComponentWrapper;
2. 更新 Markdown 文件中的导入
将所有直接导入的 Plotly 组件改为使用包装器:
之前:
import InteractiveLinearRegression from '@site/src/components/InteractiveLinearRegression';
之后:
import InteractiveLinearRegressionWrapper from '@site/src/components/ClientSideInteractiveLinearRegression';
3. 更新组件使用
之前:
<InteractiveLinearRegression />
之后:
<InteractiveLinearRegressionWrapper />
修改的文件
新增文件:
src/components/ClientSideInteractiveLinearRegression.jssrc/components/ClientSideMultipleRegression3D.jssrc/components/ClientSideCoefficientInterpreter.jssrc/components/ClientSideFeatureImportanceViz.js
更新的文件:
docs/Statistics/线性回归.mddocs/Statistics/回归系数.mddocs/XML/intro.md
验证
运行本地构建测试:
npm run build
构建成功输出:
✔ Server: Compiled successfully in 7.63s
✔ Client: Compiled successfully in 41.81s
[SUCCESS] Generated static files in "build".
部署
现在可以安全地推送到 Git 并触发 Vercel 部署:
git add .
git commit -m "Fix SSR issues with Plotly components"
git push
Vercel 将成功构建并部署网站。
工作原理
BrowserOnly 组件
<BrowserOnly> 是 Docusaurus 提供的组件,用于处理客户端专用代码:
<BrowserOnly fallback={<LoadingPlaceholder />}>
{() => {
// 这里的代码只在浏览器中执行
const ClientComponent = require('./ClientComponent').default;
return <ClientComponent />;
}}
</BrowserOnly>
- fallback: 在服务器端渲染时显示的内容
- children 函数: 只在客户端执行,可以安全地使用浏览器 API
为什么需要这个
- SSG 过程: Docusaurus 在构建时预渲染页面为静态 HTML
- 服务器端环境: Node.js 没有
window、document等浏览器 API - Plotly 依赖: Plotly.js 及其依赖(d3.js)需要浏览器环境
- 解决方案: 延迟加载 Plotly 组件到客户端
其他注意事项
SEO 影响
使用 <BrowserOnly> 不会显著影响 SEO,因为:
- 包装器的内容是交互式可视化工具
- 页面的主要内容(文本、公式、标题)仍然会被索引
- fallback 内容提供基本的占位符
性能考虑
- 首次加载时显示 "Loading..." 占位符
- Plotly 库(约 3MB)会在客户端加载
- 考虑使用代码分割和懒加载优化
- 可以添加预加载提示改善用户体验
替代方案
如果 <BrowserOnly> 不够用,还可以考虑:
-
动态导入:
import dynamic from '@docusaurus/lib/dynamic';
const Component = dynamic(() => import('./Component'), { ssr: false }); -
ExecutionEnvironment:
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
if (ExecutionEnvironment.canUseDOM) {
// 客户端代码
} -
useEffect 钩子:
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [Component, setComponent] = useState(null);
useEffect(() => {
import('./Component').then(mod => setComponent(mod.default));
}, []);
if (!Component) return <div>Loading...</div>;
return <Component />;
}
总结
通过使用 Docusaurus 的 <BrowserOnly> 组件包装所有 Plotly 可视化组件,成功解决了 Vercel 部署时的 SSR 错误。这是处理客户端专用库的标准模式,确保了:
- ✅ 成功的静态站点生成
- ✅ 正确的客户端渲染
- ✅ 良好的用户体验
- ✅ 不影响 SEO
网站现在可以成功部署到 Vercel 了!