JS金丝雀发布指的是将新构建的前端脚本先投放给一小部分真实用户,在确认稳定后再逐步扩大比例,最终完全替换旧版本。和传统全量发布相比,它把故障影响范围锁死在可控流量内,特别适合单页应用、组件库升级等改动风险较高的场景。实现这套机制并不依赖特定框架,核心在于资源路由分流与用户标识命中两块逻辑。

基于HTTP层的JS资源分流配置
最直观的做法是在网关或CDN边缘按请求特征把不同用户引到不同JS文件。例如用Nginx判断Cookie里的canary字段,命中后重写到带canary标记的脚本路径。这种方案对前端代码侵入小,运营人员也能在后台直接调比例。
下面是一段简化的Nginx配置,它读取名为uid的Cookie,对其做取模运算,百分之五的流量会被导向新包。注意真实环境要结合缓存策略,避免旧用户拿到混合资源导致报错。
map $cookie_uid $js_version {
default "stable";
"~*(?<num>d+)$" $num;
}
server {
listen 80;
server_name app.ipipp.com;
location /static/main.js {
if ($cookie_canary = "1") {
rewrite ^ /static/main.canary.js break;
}
proxy_pass https://oss.ipipp.com;
}
}
该方式的优势是规则集中、易回滚,只要去掉rewrite就能瞬间恢复全量稳定版。缺点是纯HTTP层难以做复杂用户画像,且CDN节点同步配置存在短暂不一致窗口,需要配合版本号查询参数如main.js?v=2来强刷。
利用特性开关与本地标记控制命中
当无法改造网关时,可以在入口HTML里内联一段引导脚本,根据用户ID哈希决定加载哪份JS,并把结果写进localStorage长期保持。这种前端自治方案不依赖运维,适合小团队快速验证。
示例代码中,我们先算一个0到99的随机数模拟分流,也可以替换成后台下发的白名单。命中后动态创建script标签载入金丝雀包,否则走稳定包。用localStorage.setItem记录避免每次刷新跳变,提升体验一致性。
(function () {
var hit = false;
var flag = localStorage.getItem('js_canary');
if (flag === null) {
var rand = Math.floor(Math.random() * 100);
hit = rand < 5;
localStorage.setItem('js_canary', hit ? '1' : '0');
} else {
hit = flag === '1';
}
var url = hit ? 'https://app.ipipp.com/static/main.canary.js' : 'https://app.ipipp.com/static/main.js';
var s = document.createElement('script');
s.src = url;
document.head.appendChild(s);
})();
特性开关的灵活性高,还能配合业务开关做功能级灰度,比如只放新版购物车而保留旧首页。但要防范localStorage被手动篡改导致 Support 收到非预期反馈,因此关键路径建议再加一层服务端校验。
发布流程与监控回滚要点
配置好分流只是第一步,完整金丝雀还要有观察期和决策点。通常放5%流量观察十分钟,重点看JS报错率、白屏率和接口耗时。若指标平稳,按10%、25%、50%、100%阶梯推进,每阶间隔不少于五分钟。
监控端可以用window.onerror上报异常,并打上版本标签。一旦发现canary版错误突增,立即通过配置中心关掉开关或把Nginx规则指回stable。由于浏览器可能缓存了旧script,回滚时需变更文件URL或设置max-age=0。
window.onerror = function (msg, src, line, col, err) {
var version = src.indexOf('canary') > -1 ? 'canary' : 'stable';
navigator.sendBeacon('/log/js_err', JSON.stringify({
msg: msg,
version: version,
t: Date.now()
}));
return false;
};
此外,建议把构建产物同时保留两个版本目录,不要覆盖发布。这样回滚是秒级文件指向切换,而不是重新打包。团队还应把金丝雀配置写进文档,避免人员流动后规则失传。做好上述环节,JS金丝雀发布就能成为前端日常上线的安全网。