星阵围棋手机版
64.59MB · 2025-11-21
在大型前端项目的生产环境部署中,静态资源(JS、CSS、字体文件等)通常占据较大的体积。将这些资源部署到对象存储服务(OSS)并通过 CDN 加速,可以带来以下优势:
我们的项目需要实现以下功能:
static/css/、static/fonts/、static/js/ 目录favicon.ico 和 styles/ 目录不上传 OSS,使用相对路径┌─────────────────┐
│ 本地开发环境 │ → 相对路径 (/)
└─────────────────┘
┌─────────────────┐
│ 测试环境构建 │ → 相对路径 (/) → 直接部署
└─────────────────┘
┌─────────────────┐
│ 生产环境构建 │ → CDN 路径 (https://cdn.example.com/)
└─────────────────┘
│
├─→ static/css/ → 上传 OSS
├─→ static/fonts/ → 上传 OSS
├─→ static/js/ → 上传 OSS
├─→ favicon.ico → 不上传(相对路径)
└─→ styles/ → 不上传(相对路径)
publicPath 决定了构建后资源的引用路径。我们需要根据环境变量动态设置:
module.exports = {
publicPath: process.env.CDN_URL ||
(process.env.NODE_ENV === 'development' ? '/' : // 本地开发环境
process.env.VUE_APP_PATH_TYPE === 'gray' ? '/' : // 测试环境使用相对路径
'https://js-cdn.m.cc/'), // 生产环境使用 CDN
outputDir: 'dist',
assetsDir: 'static',
filenameHashing: true,
}
关键点说明:
assetsDir: 'static':所有资源输出到 static/ 目录filenameHashing: true:启用文件名 hash,实现缓存控制VUE_APP_PATH_TYPE === 'gray'):使用相对路径为了确保文件路径的一致性,需要显式设置输出文件名:
configureWebpack: config => {
// 统一使用 static/js/ 路径,与 assetsDir 保持一致
config.output.filename = 'static/js/[name].[hash:8].js'
config.output.chunkFilename = 'static/js/[name].[hash:8].js'
}
为什么需要显式设置?
虽然 assetsDir: 'static' 会自动处理路径,但显式设置可以:
生产环境需要特殊处理:静态资源使用 CDN,但 favicon.ico 和 styles/ 使用相对路径。
实现原理:
在 Webpack 的 emit 阶段(生成文件后,写入磁盘前),通过自定义插件修改 HTML 内容:
chainWebpack: config => {
// 只在生产环境(非测试环境)运行
if (process.env.NODE_ENV === 'production' && process.env.VUE_APP_PATH_TYPE !== 'gray') {
class ModifyHtmlPlugin {
apply(compiler) {
compiler.hooks.emit.tapAsync('ModifyHtmlPlugin', (compilation, callback) => {
Object.keys(compilation.assets).forEach(filename => {
if (filename.endsWith('.html')) {
let html = compilation.assets[filename].source()
// 1. 先处理 BASE_URL 变量(如果还没被 Vue CLI 替换)
html = html.replace(/href="<%= BASE_URL %>favicon.ico"/g, 'href="/favicon.ico"')
html = html.replace(/href="<%= BASE_URL %>styles/([^"]+)"/g, 'href="/styles/$1"')
// 2. 替换其他 BASE_URL 变量为 CDN 路径
const baseUrl = process.env.CDN_URL || 'https://js-cdn.m.cc/'
html = html.replace(/<%= BASE_URL %>/g, baseUrl)
// 3. 最后处理已经被替换的 CDN 路径(将 favicon 和 styles 改回相对路径)
html = html.replace(/href="https://js-cdn.modb.cc/favicon.ico"/g, 'href="/favicon.ico"')
html = html.replace(/href="https://js-cdn.modb.cc/styles/([^"]+)"/g, 'href="/styles/$1"')
compilation.assets[filename] = {
source: () => html,
size: () => html.length
}
}
})
callback()
})
}
}
config.plugin('modify-html').use(ModifyHtmlPlugin)
}
}
处理顺序的重要性:
BASE_URL 变量(模板阶段)这样可以确保所有路径都被正确处理。
为了提升加载性能,我们配置了代码分割:
config.optimization.splitChunks = {
chunks: 'all',
minSize: 20000,
maxAsyncRequests: 20,
maxInitialRequests: 15,
cacheGroups: {
// Vue 核心库单独拆分
vueCore: {
name: 'chunk-vue-core',
test: /[\/]node_modules[\/](vue|vue-router|vuex|vue-i18n)[\/]/,
priority: 40,
chunks: 'initial',
enforce: true
},
// 大型 UI 库单独拆分
elementPlus: {
name: 'chunk-element-plus',
test: /[\/]node_modules[\/]element-plus[\/]/,
priority: 35,
chunks: 'initial',
enforce: true
},
// 其他配置...
}
}
优势:
async,减少初始加载体积首先需要确定服务器的 CPU 架构,以选择正确的 ossutil 版本:
uname -m
# 输出示例:
# x86_64 → 使用 ossutil64
# aarch64 → 使用 ossutilarm64
根据架构下载对应版本(以阿里云 OSS 为例):
# 创建目录
mkdir -p /root/oss
cd /root/oss
# 下载对应版本(示例:ARM64 架构)
wget https://gosspublic.alicdn.com/ossutil/1.7.13/ossutilarm64
# 或者使用 curl
curl -o ossutilarm64 https://gosspublic.alicdn.com/ossutil/1.7.13/ossutilarm64
# 添加执行权限
chmod +x ossutilarm64
# 验证安装
/root/oss/ossutilarm64 --version
ossutil 支持两种配置方式:
/root/oss/ossutilarm64 config -e <endpoint>
-i <accessKeyId>
-k <accessKeySecret>
创建配置文件 ~/.ossutilconfig:
cat > /root/.ossutilconfig << 'EOF'
[Credentials]
language=EN
endpoint=oss-cn-hangzhou.aliyuncs.com
accessKeyID=YOUR_ACCESS_KEY_ID
accessKeySecret=YOUR_ACCESS_KEY_SECRET
EOF
# 设置文件权限(安全考虑)
chmod 600 /root/.ossutilconfig
配置说明:
language=EN:使用英文输出,避免编码问题endpoint:OSS 区域节点地址accessKeyID 和 accessKeySecret:OSS 访问凭证# 测试连接
/root/oss/ossutilarm64 ls oss://your-bucket-name/
# 如果成功,会显示 OSS 上的文件列表
上传脚本需要包含以下功能:
#!/bin/bash
set -e # 遇到错误立即退出
echo "Starting OSS upload for production environment..."
# ========== 1. OSS 配置 ==========
# 如果使用配置文件,可以跳过此步骤
# 如果需要动态配置,使用以下命令:
# /root/oss/ossutilarm64 config -e <endpoint>
# -i <accessKeyId>
# -k <accessKeySecret>
# ========== 2. 测试 OSS 连接 ==========
echo "Testing OSS connection..."
if /root/oss/ossutilarm64 ls oss://your-bucket-name/ 2>/dev/null > /dev/null; then
echo "OSS connection test passed"
else
echo "Warning: OSS connection test failed, but continuing..."
fi
# ========== 3. 设置构建路径 ==========
DIST_PATH="/u01/dist"
# ========== 4. 检查构建输出 ==========
if [ ! -d "$DIST_PATH" ]; then
echo "Error: $DIST_PATH not found"
exit 1
fi
echo "Build path: $DIST_PATH"
# 检查 static 目录
if [ ! -d "$DIST_PATH/static" ]; then
echo "Error: $DIST_PATH/static not found"
exit 1
fi
# 检查关键文件
if [ ! -f "$DIST_PATH/index.html" ]; then
echo "Error: $DIST_PATH/index.html not found"
exit 1
fi
if [ ! -f "$DIST_PATH/favicon.ico" ]; then
echo "Warning: $DIST_PATH/favicon.ico not found"
fi
if [ ! -d "$DIST_PATH/styles" ]; then
echo "Warning: $DIST_PATH/styles directory not found"
fi
# ========== 5. 清理 OSS 上的旧文件 ==========
echo ""
echo "Cleaning old files from OSS..."
# 删除 OSS 上的 css 目录(使用 -f 参数避免交互式提示)
echo " Deleting old CSS files..."
/root/oss/ossutilarm64 rm -r oss://your-bucket-name/static/css/ -f 2>/dev/null || echo " (CSS directory may not exist, skipping)"
# 删除 OSS 上的 fonts 目录
echo " Deleting old fonts files..."
/root/oss/ossutilarm64 rm -r oss://your-bucket-name/static/fonts/ -f 2>/dev/null || echo " (Fonts directory may not exist, skipping)"
# 删除 OSS 上的 js 目录
echo " Deleting old JS files..."
/root/oss/ossutilarm64 rm -r oss://your-bucket-name/static/js/ -f 2>/dev/null || echo " (JS directory may not exist, skipping)"
echo "Old files cleaned (if any existed)"
echo ""
# ========== 6. 上传新文件 ==========
# 上传 static/css/ 目录
if [ -d "$DIST_PATH/static/css" ]; then
echo "Uploading static/css/ directory..."
/root/oss/ossutilarm64 cp -r $DIST_PATH/static/css/ oss://your-bucket-name/static/css/ -f
if [ $? -eq 0 ]; then
echo "CSS directory uploaded successfully"
else
echo "Failed to upload CSS directory"
exit 1
fi
else
echo "Warning: $DIST_PATH/static/css not found, skipping..."
fi
# 上传 static/fonts/ 目录
if [ -d "$DIST_PATH/static/fonts" ]; then
echo "Uploading static/fonts/ directory..."
/root/oss/ossutilarm64 cp -r $DIST_PATH/static/fonts/ oss://your-bucket-name/static/fonts/ -f
if [ $? -eq 0 ]; then
echo "Fonts directory uploaded successfully"
else
echo "Failed to upload fonts directory"
exit 1
fi
else
echo "Warning: $DIST_PATH/static/fonts not found, skipping..."
fi
# 上传 static/js/ 目录
if [ -d "$DIST_PATH/static/js" ]; then
echo "Uploading static/js/ directory..."
/root/oss/ossutilarm64 cp -r $DIST_PATH/static/js/ oss://your-bucket-name/static/js/ -f
if [ $? -eq 0 ]; then
echo "JS directory uploaded successfully"
else
echo "Failed to upload JS directory"
exit 1
fi
else
echo "Error: $DIST_PATH/static/js not found (required)"
exit 1
fi
# ========== 7. 验证上传 ==========
echo ""
echo "Verifying upload..."
# 统计上传的文件数量
JS_COUNT=$(/root/oss/ossutilarm64 ls oss://your-bucket-name/static/js/ 2>/dev/null | wc -l)
CSS_COUNT=$(/root/oss/ossutilarm64 ls oss://your-bucket-name/static/css/ 2>/dev/null | wc -l)
FONTS_COUNT=$(/root/oss/ossutilarm64 ls oss://your-bucket-name/static/fonts/ 2>/dev/null | wc -l)
echo ""
echo "Upload completed!"
echo "Upload statistics:"
echo " JS files: $JS_COUNT"
echo " CSS files: $CSS_COUNT"
echo " Fonts files: $FONTS_COUNT"
# 检查关键文件是否存在
echo ""
echo "Checking key files..."
JS_CHECK=$(/root/oss/ossutilarm64 ls oss://your-bucket-name/static/js/app.*.js 2>/dev/null | head -1 | wc -l)
CSS_CHECK=$(/root/oss/ossutilarm64 ls oss://your-bucket-name/static/css/app.*.css 2>/dev/null | head -1 | wc -l)
if [ "$JS_CHECK" -gt 0 ] && [ "$CSS_CHECK" -gt 0 ]; then
echo "Key files app.js and app.css found in OSS"
echo "Upload verification passed!"
else
echo "Warning: Some key files may be missing"
fi
echo ""
echo "OSS upload process completed!"
set -e 的作用set -e
遇到任何错误立即退出,避免继续执行可能导致的问题。
-f 参数ossutilarm64 rm -r oss://bucket/path/ -f
ossutilarm64 cp -r local/path/ oss://bucket/path/ -f
-f 参数的作用:
rm -r -f:强制删除,不询问确认cp -r -f:强制覆盖,不询问确认为什么重要:在自动化脚本中,交互式提示会导致脚本挂起。
command 2>/dev/null || echo "fallback message"
2>/dev/null:隐藏错误输出||:如果命令失败,执行后续命令避免使用可能被系统识别的变量名:
# 不推荐(可能被某些系统识别为系统变量)
BUILD_PATH="/u01/dist"
# 推荐
DIST_PATH="/u01/dist"
JS_COUNT=$(ossutilarm64 ls oss://bucket/static/js/ 2>/dev/null | wc -l)
使用 wc -l 统计文件数量,用于验证上传是否成功。
测试环境使用相对路径,不需要上传 OSS:
# 1. 构建项目
npm run gray
# 2. 打包
tar -czf dist.gz dist/
# 3. 上传到服务器并解压
scp dist.gz user@server:/path/to/deploy/
ssh user@server "cd /path/to/deploy && tar -xzf dist.gz"
生产环境需要上传资源到 OSS:
# 1. 构建项目
npm run build
# 2. 打包
tar -czf dist.gz dist/
# 3. 上传到服务器
scp dist.gz user@server:/u01/
scp upload-to-oss-production.sh user@server:/u01/
# 4. 在服务器上执行部署脚本
ssh user@server << 'EOF'
cd /u01
rm -rf dist && tar -zxvf ./dist.gz
chmod +x upload-to-oss-production.sh
./upload-to-oss-production.sh
EOF
可以将解压和上传合并到一个脚本中:
#!/bin/bash
set -e
# 解压构建文件
cd /u01
rm -rf dist && tar -zxvf ./dist.gz
# 执行 OSS 上传脚本
./upload-to-oss-production.sh
# 部署到 Web 服务器(Nginx 示例)
# cp -r dist/* /usr/share/nginx/html/
# systemctl reload nginx
600chmod 600 ~/.ossutilconfig
为 OSS 访问密钥设置最小必要权限:
对于大量文件,可以考虑并行上传:
# 使用后台任务并行上传
/root/oss/ossutilarm64 cp -r $DIST_PATH/static/css/ oss://bucket/static/css/ -f &
/root/oss/ossutilarm64 cp -r $DIST_PATH/static/fonts/ oss://bucket/static/fonts/ -f &
/root/oss/ossutilarm64 cp -r $DIST_PATH/static/js/ oss://bucket/static/js/ -f &
# 等待所有任务完成
wait
如果文件很多,可以使用 --update 参数进行增量上传:
ossutilarm64 cp -r local/path/ oss://bucket/path/ --update
但需要注意:如果文件名包含 hash,每次构建都是新文件,增量上传意义不大。
在 OSS 控制台或通过 API 设置缓存头:
Cache-Control: public, max-age=31536000
对于带 hash 的文件名,可以设置长期缓存。
如果使用 CDN,需要配置:
记录上传过程的关键信息:
LOG_FILE="/var/log/oss-upload.log"
echo "$(date): Starting OSS upload" >> $LOG_FILE
# ... 上传过程 ...
echo "$(date): OSS upload completed" >> $LOG_FILE
在删除旧文件前,可以先备份:
# 备份当前版本
BACKUP_DIR="backup/$(date +%Y%m%d_%H%M%S)"
/root/oss/ossutilarm64 cp -r oss://bucket/static/ oss://bucket/$BACKUP_DIR/ -f
如果需要回滚,可以从备份恢复:
/root/oss/ossutilarm64 cp -r oss://bucket/$BACKUP_DIR/static/ oss://bucket/static/ -f
症状:浏览器控制台显示 404 错误
排查步骤:
dist/index.html 中的路径publicPath 配置是否正确ModifyHtmlPlugin 是否正常运行解决方案:
# 检查构建后的 HTML
cat dist/index.html | grep -E '(href|src)='
# 检查环境变量
echo $NODE_ENV
echo $VUE_APP_PATH_TYPE
症状:ossutil: No such file or directory 或连接超时
排查步骤:
解决方案:
# 检查 ossutil 是否存在
ls -la /root/oss/ossutilarm64
# 测试网络连接
ping oss-cn-hangzhou.aliyuncs.com
# 测试 OSS 连接
/root/oss/ossutilarm64 ls oss://bucket-name/
症状:脚本执行失败,部分文件未上传
排查步骤:
解决方案:
# 检查磁盘空间
df -h
# 检查网络
curl -I https://oss-cn-hangzhou.aliyuncs.com
# 重试上传
./upload-to-oss-production.sh
症状:Permission denied
解决方案:
chmod +x /root/oss/ossutilarm64
症状:syntax error: unexpected EOF 等
常见原因:
解决方案:
# 检查脚本语法
bash -n upload-to-oss-production.sh
# 使用 shellcheck 检查(如果已安装)
shellcheck upload-to-oss-production.sh
症状:测试环境使用了 CDN 路径,或生产环境使用了相对路径
排查步骤:
# 检查 package.json 中的脚本
cat package.json | grep -A 5 '"scripts"'
# 确认环境变量设置
# 测试环境应该设置 VUE_APP_PATH_TYPE=gray
# 生产环境不应该设置此变量
本文详细介绍了 Vue.js 项目将静态资源部署到 OSS 的完整实现过程,包括:
希望本文能帮助您成功实现静态资源的 OSS 部署!