Skip to main content

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 是一个纯客户端库,依赖浏览器的 windowself 等 API。在 Docusaurus 的静态站点生成(SSG)过程中,组件会在服务器端预渲染,而 Plotly.js 无法在 Node.js 环境中运行,导致构建失败。

解决方案

使用 Docusaurus 的 <BrowserOnly> 组件将 Plotly 组件包装起来,使其仅在客户端加载。

实施的修复

1. 创建客户端包装器组件

为每个 Plotly 组件创建了对应的包装器:

  • ClientSideInteractiveLinearRegression.js
  • ClientSideMultipleRegression3D.js
  • ClientSideCoefficientInterpreter.js
  • ClientSideFeatureImportanceViz.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.js
  • src/components/ClientSideMultipleRegression3D.js
  • src/components/ClientSideCoefficientInterpreter.js
  • src/components/ClientSideFeatureImportanceViz.js

更新的文件:

  • docs/Statistics/线性回归.md
  • docs/Statistics/回归系数.md
  • docs/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

为什么需要这个

  1. SSG 过程: Docusaurus 在构建时预渲染页面为静态 HTML
  2. 服务器端环境: Node.js 没有 windowdocument 等浏览器 API
  3. Plotly 依赖: Plotly.js 及其依赖(d3.js)需要浏览器环境
  4. 解决方案: 延迟加载 Plotly 组件到客户端

其他注意事项

SEO 影响

使用 <BrowserOnly> 不会显著影响 SEO,因为:

  • 包装器的内容是交互式可视化工具
  • 页面的主要内容(文本、公式、标题)仍然会被索引
  • fallback 内容提供基本的占位符

性能考虑

  • 首次加载时显示 "Loading..." 占位符
  • Plotly 库(约 3MB)会在客户端加载
  • 考虑使用代码分割和懒加载优化
  • 可以添加预加载提示改善用户体验

替代方案

如果 <BrowserOnly> 不够用,还可以考虑:

  1. 动态导入:

    import dynamic from '@docusaurus/lib/dynamic';
    const Component = dynamic(() => import('./Component'), { ssr: false });
  2. ExecutionEnvironment:

    import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';

    if (ExecutionEnvironment.canUseDOM) {
    // 客户端代码
    }
  3. 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 了!