AwsLinker/README-STATIC-BUILD.md
2025-09-16 17:19:58 +08:00

8.2 KiB
Raw Blame History

静态构建解决方案文档

🚀 问题解决方案

原始问题

  1. 静态资源访问问题 - 图片、CSS、JS 文件访问失败
  2. 新闻页面检索问题 - 新闻文章无法显示

解决方案概述

采用 Next.js 静态导出 (Static Export) 模式,通过以下技术实现:

  • 静态文件生成,无需服务器端 API
  • 文章数据在构建时预生成
  • 图片和资源使用相对路径
  • 完整的 SEO 支持

📁 项目结构

dongyun/
├── app/                          # Next.js App Router
│   ├── [locale]/                 # 多语言路由
│   │   ├── news/                 # 新闻页面
│   │   │   ├── page.tsx          # 新闻列表页
│   │   │   └── [id]/            # 新闻详情页
│   │   │       └── page.tsx
│   │   └── ...                   # 其他页面
│   └── components/               # 组件目录
├── docs/                         # 文章数据源
│   ├── zh-CN/                    # 中文文章
│   ├── zh-TW/                    # 繁体中文文章
│   └── en/                       # 英文文章
├── lib/
│   └── static-data.ts            # 静态数据处理函数
├── public/                       # 静态资源
│   └── images/                   # 图片资源
├── scripts/
│   └── build-static.bat          # 自动化构建脚本
├── out/                          # 构建输出目录 (生成后)
└── next.config.mjs               # Next.js 配置

🛠️ 核心实现

1. Next.js 配置 (next.config.mjs)

const isStaticExport = process.env.BUILD_MODE === 'static';

const nextConfig = {
    ...(isStaticExport && {
        output: 'export',           // 启用静态导出
        trailingSlash: true,        // 添加尾部斜杠
        images: {
            unoptimized: true,      // 禁用图片优化
        },
    }),
    env: {
        BUILD_MODE: process.env.BUILD_MODE || 'server',
    },
};

2. 静态数据处理 (lib/static-data.ts)

export function getStaticArticles(locale: 'zh-CN' | 'zh-TW' | 'en' = 'zh-CN'): Article[] {
    const localeDir = path.join(docsDirectory, locale);
    
    // 读取 markdown 文件
    const fileNames = fs.readdirSync(localeDir);
    const articles = fileNames
        .filter((fileName: string) => fileName.endsWith('.md'))
        .map((fileName: string) => {
            const fullPath = path.join(localeDir, fileName);
            const fileContents = fs.readFileSync(fullPath, 'utf8');
            const { data, content } = matter(fileContents);
            // ... 处理逻辑
        })
        .sort((a, b) => new Date(b.metadata.date).getTime() - new Date(a.metadata.date).getTime());

    return articles;
}

3. 组件智能切换 (app/components/news/NewsPageClient.tsx)

const fetchArticles = useCallback(async () => {
    // 检查是否为静态导出模式
    const isStatic = process.env.NODE_ENV === 'production' && 
                     typeof window !== 'undefined' && 
                     !window.location.origin.includes('localhost');
    
    if (isStatic) {
        // 静态模式:直接从预生成数据获取
        const { getStaticArticles, getStaticCategories } = await import('../../../lib/static-data');
        // ...
    } else {
        // 开发模式:使用 API
        const response = await fetch(`/api/articles?locale=${locale}`);
        // ...
    }
}, [locale, selectedCategory]);

🚀 使用方法

快速构建(推荐)

# 使用自动化脚本
npm run deploy

# 或手动构建
npm run build:static

预览构建结果

npm run preview

然后访问 http://localhost:8080

部署到服务器

# 方法1: rsync 同步
rsync -avz --delete out/ user@your-server:/var/www/html/

# 方法2: scp 复制
scp -r out/* user@your-server:/var/www/html/

# 方法3: 打包上传
tar -czf dongyun-static.tar.gz out
scp dongyun-static.tar.gz user@your-server:~/
ssh user@your-server "cd /var/www/html && tar -xzf ~/dongyun-static.tar.gz --strip-components=1"

📋 完整构建流程

自动化脚本 (scripts/build-static.bat)

@echo off
echo 开始静态构建...

echo 1. 临时移动 API 目录...
if exist "app\api" (
    if not exist "..\temp" mkdir "..\temp"
    move "app\api" "..\temp\api-disabled" >nul
)

echo 2. 构建静态文件...
call npm run build:static

echo 3. 恢复 API 目录...
if exist "..\temp\api-disabled" (
    move "..\temp\api-disabled" "app\api" >nul
)

echo ✅ 静态构建完成!
echo 📁 静态文件位置: out/ 目录

package.json 脚本

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "build:static": "cross-env BUILD_MODE=static next build",
    "build:server": "cross-env BUILD_MODE=server next build",
    "export": "npm run build:static",
    "start": "next start",
    "deploy": "scripts\\build-static.bat",
    "preview": "cd out && python -m http.server 8080"
  }
}

验证结果

1. 静态资源访问

  • 图片文件:/images/hero-bg.jpg → 正确加载
  • CSS 样式:/_next/static/css/... → 正确加载
  • JavaScript/_next/static/chunks/... → 正确加载

2. 新闻页面功能

  • 新闻列表页:显示所有文章
  • 新闻详情页:显示完整文章内容
  • 搜索功能:客户端搜索工作正常
  • 分类筛选:分类切换工作正常
  • 相关文章:推荐功能正常

3. SEO 和性能

  • 静态 HTML 生成SEO 友好
  • 预加载关键资源
  • 图片优化和懒加载
  • 多语言支持

🔧 技术要点

为什么移除 API 目录?

Next.js 静态导出模式不支持 API 路由,构建时会报错。我们采用以下策略:

  1. 构建时:临时移除 app/api 目录
  2. 运行时:组件智能判断环境,选择数据获取方式
  3. 构建后:恢复 app/api 目录,保持开发功能

数据获取策略

  • 开发环境 (npm run dev):使用 API 路由,支持热更新
  • 生产环境 (静态导出):直接从文件系统读取,性能最佳

路径处理

  • 静态资源使用相对路径,确保在任何服务器环境下都能正确访问
  • 图片路径自动适配,支持 CDN 部署

🌐 部署选项

1. 静态网站托管

  • Vercel: vercel --prod
  • Netlify: 直接上传 out/ 目录
  • GitHub Pages: 推送到 gh-pages 分支

2. 传统服务器

  • Nginx: 配置静态文件服务
  • Apache: 设置 DocumentRoot 到 out/ 目录
  • CDN: 上传到阿里云 OSS、腾讯云 COS 等

3. 容器化部署

FROM nginx:alpine
COPY out/ /usr/share/nginx/html/
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

📈 性能优化

已实现优化

  • 静态预渲染,首屏加载快
  • 代码分割和懒加载
  • 图片优化和 WebP 支持
  • CSS 和 JS 压缩
  • Gzip 压缩支持

可选优化

  • CDN 加速静态资源
  • Service Worker 离线缓存
  • 图片格式优化 (WebP, AVIF)
  • HTTP/2 Server Push

🆘 常见问题

Q: 构建失败,提示 API 路由错误?

A: 确保使用 npm run build:static 而不是 npm run build,或使用自动化脚本 npm run deploy

Q: 图片显示 404 错误?

A: 检查图片路径是否正确,确保图片文件在 public/images/ 目录中。

Q: 新闻文章不显示?

A: 确认 docs/ 目录中的 markdown 文件格式正确,包含必要的 frontmatter。

Q: 开发环境和生产环境表现不一致?

A: 这是正常的,开发环境使用 API生产环境使用静态数据。使用 npm run preview 测试生产版本。

🔄 更新文章

  1. docs/zh-CN/ 目录添加新的 .md 文件
  2. 运行 npm run build:static 重新构建
  3. 上传更新的 out/ 目录到服务器

📞 技术支持

如果遇到问题,请检查:

  1. Node.js 版本 >= 18
  2. 依赖包是否正确安装 (npm install)
  3. markdown 文件格式是否正确
  4. 图片资源是否存在

🎉 恭喜! 你的 Next.js 应用现在支持完整的静态部署,解决了静态资源访问和新闻页面检索的所有问题!