问题背景
在开发 Next.js 项目时,我们经常遇到以下问题:
- 静态资源(图片、视频等)体积较大,每次部署都需要上传这些文件
- 开发环境和生产环境的静态资源路径不同
- 需要将静态资源部署到专门的静态资源服务器(如 Nginx)
- 需要保持其他静态资源(如 CSS、JS)的原始路径
本文将介绍如何解决这些问题,实现静态资源的优化部署。
解决方案
1. 环境配置
首先,我们需要为不同环境配置不同的静态资源路径:
.env.development文件内容:
env
# 开发环境配置
# 当运行 npm run dev 时自动加载此文件
# 环境标识
NODE_ENV=development
# 静态资源基础URL(开发环境本地地址)
NEXT_PUBLIC_STATIC_BASE_URL=
.env.test文件内容:
env
# 测试环境配置
# 当运行 npm run build:test 时自动加载此文件
# 环境标识
NODE_ENV=test
# 静态资源基础URL(测试环境地址)
NEXT_PUBLIC_STATIC_BASE_URL=/nginx-assets/website
# 基础路径
NEXT_PUBLIC_BASE_PATH=/official-website
.env.production文件内容:
env
# 生产环境配置
# 当运行 npm run build 时自动加载此文件
# 环境标识
NODE_ENV=production
# 静态资源基础URL(CDN地址)
NEXT_PUBLIC_STATIC_BASE_URL=/nginx-assets/website
2. Next.js 配置
在 next.config.ts
中配置静态资源路径:
typescript
import type { NextConfig } from "next";
const isDev = process.env.NODE_ENV === 'development';
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || '';
const assetsPath = process.env.NEXT_PUBLIC_ASSETS_PATH || '/nginx-assets/website';
const nextConfig: NextConfig = {
/* config options here */
output: 'export', // 添加这行
basePath, // 添加基础路径配置
images: {
unoptimized: true, // 如果使用了 next/image,需要添加这行
domains: ['your-domain.com'], // 添加您的域名
path: isDev ? '' : assetsPath, // 使用环境变量中的路径
},
// 添加配置以排除特定目录
webpack: (config) => {
if (!isDev) {
// 在生产环境下,从 public 目录中排除 svg、images 和 videos 目录
config.module.rules.push({
test: /\.(png|jpg|jpeg|gif|svg|webp|avif|mp4|webm)$/,
exclude: [
/public\/svg/,
/public\/videos/,
/public\/images/
],
use: {
loader: 'file-loader',
options: {
publicPath: basePath || assetsPath,
outputPath: 'static/media',
name: '[name].[hash].[ext]',
},
},
});
}
return config;
},
};
export default nextConfig;
3. 静态资源路径处理工具
创建工具函数统一处理静态资源路径:
typescript
// src/utils/static.ts
export const getStaticUrl = (path: string): string => {
const baseUrl = process.env.NEXT_PUBLIC_STATIC_BASE_URL;
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || '';
// 开发环境下直接返回原始路径
if (process.env.NODE_ENV === 'development') {
return path;
}
// 检查是否是特殊资源路径(svg/、images/、videos/)
const isSpecialPath = /^(\/?(svg|images|videos)\/)/i.test(path);
if (isSpecialPath) {
// 特殊资源使用完整的静态资源URL
if (!baseUrl) {
return path;
}
// 移除开头的斜杠
let cleanPath = path.startsWith('/') ? path.slice(1) : path;
// 处理视频路径
if (cleanPath.startsWith('videos/')) {
// 已经是正确的格式,不需要修改
} else if (cleanPath.includes('/videos/')) {
cleanPath = 'videos/' + cleanPath.split('/videos/')[1];
}
// 处理图片路径
if (cleanPath.startsWith('svg/')) {
// 已经是正确的格式,不需要修改
} else if (cleanPath.includes('/svg/')) {
cleanPath = 'svg/' + cleanPath.split('/svg/')[1];
}
// 处理图片路径
if (cleanPath.startsWith('images/')) {
// 已经是正确的格式,不需要修改
} else if (cleanPath.includes('/images/')) {
cleanPath = 'images/' + cleanPath.split('/images/')[1];
}
return `${baseUrl}/${cleanPath}`;
} else {
// 非特殊资源使用basePath
return `${basePath}${path}`;
}
};
4. 构建后清理脚本
创建构建后清理脚本,移除不需要的静态资源目录:
javascript
// scripts/post-build.js
import fs from 'fs';
import path from 'path';
// 要删除的目录
const directoriesToRemove = [
path.join(process.cwd(), 'out', 'images'),
path.join(process.cwd(), 'out', 'videos'),
path.join(process.cwd(), 'out', 'svg')
];
// 删除目录
directoriesToRemove.forEach(dir => {
if (fs.existsSync(dir)) {
console.log(`Removing directory: ${dir}`);
fs.rmSync(dir, { recursive: true, force: true });
}
});
5. 更新 package.json
在 package.json
中添加相关配置:
注意:
cross-env
这部分是window上需要的配置,根据电脑系统不同需要有所调整。
json
{
"type": "module",
"scripts": {
"dev": "next dev --turbopack",
"build": "next build && node scripts/post-build.js",
"build:test": "cross-env NODE_ENV=test next build && node scripts/post-build.js",
"start": "next start",
"lint": "next lint"
}
}
实现效果
-
开发环境:
- 直接访问
public
目录下的静态资源 - 开发体验不受影响
- 所有路径保持原样
- 直接访问
-
生产环境:
- 图片和视频资源从 Nginx 配置的目录加载
- 其他静态资源(CSS、JS等)保持原始路径
- 构建时不包含图片和视频目录
-
部署优化:
- 构建包体积减小
- 部署时间缩短
- 图片和视频由 Nginx 直接提供,性能更好
- 其他静态资源保持原有部署方式
注意事项
-
确保 Nginx 配置正确:
nginxlocation /nginx-assets/ { alias /usr/local/nginx/nginx-assets/; autoindex on; autoindex_exact_size on; autoindex_localtime on; }
-
静态资源目录结构:
- 将
public/images
、public/videos
和public/svg
目录复制到服务器的/usr/local/nginx/nginx-assets/website/
目录 - 保持原有的目录结构
- 将
-
版本控制:
-
在
.gitignore
中添加:arduinopublic/images/ public/videos/ public/svg/
-
总结
通过以上配置,我们实现了:
- 开发环境和生产环境的无缝切换
- 图片和视频资源的优化部署
- 其他静态资源保持原有路径
- 构建包的体积优化
- 部署效率的提升
这种方案特别适合:
- 静态资源较多的项目
- 需要频繁部署的项目
- 对部署速度有要求的项目
- 使用 Nginx 作为静态资源服务器的项目