如何对不同平台生成不同的 _redirects 规则
解读
_redirects 文件是 Netlify、Vercel 等国内常用静态托管平台的“路由重写/跳转”配置。在前端交付流水线里,同一份源码往往要部署到 测试环境、预发布环境、正式环境 甚至 私有化集群,而每个平台的域名、HTTPS 强制策略、CDN 前缀、API 网关地址都不一样,因此 _redirects 内容必须“同构不同份”。Grunt 作为老牌任务运行器,优势在于“配置即代码”,可以把“平台差异”抽象成 多份模板 + 变量注入,在构建阶段一次性生成目标平台的最终规则,避免人肉维护多份文件导致的“配置漂移”。
知识点
- Grunt 任务阶段:init-config → registerTask → run;在 init-config 里把“平台”作为维度,先拼装数据,再交给模板插件渲染。
- 模板引擎:grunt-template、grunt-mustache、grunt-ejs 均可,国内团队更倾向 grunt-template,因为语法就是 Lodash 模板,无额外学习成本。
- 变量注入:通过 grunt.option('env') 或 grunt.option('platform') 读取命令行参数,例如
grunt build --platform=netlify-prod,在 Gruntfile 里映射到对应 JSON 数据片段。 - 文件生成:grunt-contrib-copy + grunt-contrib-rename 组合,或者直接用 grunt-file-creator 把渲染后的字符串写入 dist/_redirects;注意 Windows 与 Linux 换行差异,用
line endings: 'lf'保证托管平台识别。 - 多平台差异:
- Netlify:支持 200/301/302/404/!,可带 Country、Role 条件;
- Vercel:重定向写在 vercel.json,格式不同,需要同构转换;
- 腾讯云静态托管:只识别纯 301/302,不支持边缘规则;
- 私有化 Nginx:需要生成 rewrite.conf 而非 _redirects。
因此“一份模板,多种出口”是核心思路。
- 校验与回滚:用 grunt-contrib-clean 先清旧文件,再用 grunt-validate-redirects(社区插件)做 语法校验,防止把错误规则推上线导致全站 404。
- CI 集成:在 GitHub Actions / Gitee Go / Jenkins 里把 platform 作为矩阵变量,并行产出多份产物,最后由发布脚本 scp/ossutil/coscli 上传到对应 bucket。
答案
- 在 Gruntfile.js 顶部声明平台映射表:
const platformRules = {
'netlify-prod': [
'/api/* https://api.xxx.com/:splat 200',
'/* /index.html 200'
],
'vercel-prod': [
{ source: '/api/(.*)', destination: 'https://api.xxx.com/$1', statusCode: 200 },
{ source: '/(.*)', destination: '/index.html', statusCode: 200 }
],
'tc-cloud': [
'/api/* https://api.xxx.com/:splat 301',
'/* /index.html 301'
]
};
- 安装并加载插件:
npm i -D grunt-template grunt-contrib-clean grunt-file-creator
- 配置任务:
module.exports = function(grunt) {
const platform = grunt.option('platform') || 'netlify-prod';
const rules = platformRules[platform];
grunt.initConfig({
clean: { redirects: ['dist/_redirects'] },
template: {
options: { data: { rules: rules.join('\n') } },
'netlify-prod': {
files: [{ src: 'redirects.tpl', dest: 'dist/_redirects' }]
}
},
// 若目标平台是 Vercel,则把数组转成 JSON
'file-creator': {
'vercel-prod': {
'dist/vercel.json': function(fs, fd, done) {
fs.writeSync(fd, JSON.stringify({ redirects: rules }, null, 2));
done();
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-template');
grunt.loadNpmTasks('grunt-file-creator');
grunt.registerTask('redirects', function() {
if (platform.startsWith('vercel')) {
return grunt.task.run(['clean:redirects', 'file-creator:vercel-prod']);
}
return grunt.task.run(['clean:redirects', 'template:netlify-prod']);
});
grunt.registerTask('build', ['clean', 'webpack:prod', 'redirects']);
};
- 模板文件 redirects.tpl(放在项目根目录):
# 平台:<%= platform %>
# 生成时间:<%= grunt.template.today() %>
<%= rules %>
- 运行:
grunt build --platform=netlify-prod # 生成 dist/_redirects
grunt build --platform=vercel-prod # 生成 dist/vercel.json
- 在 package.json 中封装脚本,方便 CI 调用:
"scripts": {
"build:prod": "grunt build --platform=netlify-prod",
"build:pre": "grunt build --platform=tc-cloud"
}
通过以上步骤,即可实现“一份源码,多条流水线,各自产出平台专属的重定向规则”,且全程可追踪、可回滚、可校验。
拓展思考
- 灰度场景:如果要在 _redirects 里加入 按 Cookie 或 Header 分流(Netlify 支持 Role 条件),可以把灰度名单维护在远端 Redis,构建前通过 grunt-http 拉取,再注入模板,实现“配置热更新”而无需重新打包静态资源。
- 多语言站点:国内业务常区分 简体、繁体、英文,可在平台维度之外再叠加 locale 维度,使用
grunt build --platform=netlify-prod --locale=zh-cn,模板里用双重循环生成/zh-cn/* /zh-cn/index.html 200这类规则,避免手写 30 条重复配置。 - 安全合规:私有化部署时,客户要求 不能出现真实后台地址,可在 Gruntfile 里通过 node-env-encrypt 对目标地址做对称加密,生成 _redirects 后由运维在服务器端用 Lua 解密,实现“地址脱敏”。
- 性能极限:当规则数量 >1000 条 时,Netlify 会降级为边缘函数,冷启动增加 50~80 ms。此时可把规则拆成 哈希分区,用 Grunt 生成多个 _redirects 片段,再由 Nginx
include合并,既兼容托管平台,又保证性能。