Feature Flag 在 HTML 中本质是前端配置问题,需通过服务端渲染、构建时注入或 JS 运行时加载配置来实现;纯 HTML 无逻辑能力,仅作占位;JS 方案需处理请求失败、时机与缓存;构建注入更稳定但需重新部署;SSR 才能真正实现灰度控制与安全限制。

Feature Flag 在 HTML 中本质是前端配置问题
纯 HTML 无法动态控制 Feature Flag,它没有运行时逻辑能力。所谓“HTML 实现”,实际是指在服务端渲染(SSR)或构建时注入开关状态,或由 JS 加载后读取外部配置来决定 DOM 渲染分支。直接写 <div data-feature="new-search"> 这类标记只是占位,不构成开关逻辑——真正起作用的是后续 JS 判断或服务端条件渲染。
用 JavaScript 读取并响应 Feature Flag 配置
最常见做法是让 JS 初始化时拉取一个轻量配置接口(如 /api/feature-flags),再根据返回值控制组件显隐或行为分支。注意以下几点:
- 必须处理请求失败场景:默认关闭新功能,避免白屏或报错,例如 fallback 到
{ "new_checkout": false } - 不要在
DOMContentLoaded之前做依赖 flag 的 DOM 操作,否则可能找不到元素 - 避免重复请求:可缓存结果(localStorage + 过期时间),但需配合服务端支持 ETag 或版本号强制刷新
- 示例逻辑:
fetch('/api/feature-flags').then(r => r.json()).then(flags => { if (flags['ai_suggestion']) { document.getElementById('suggestion-box').style.display = 'block'; } });
构建时静态注入比运行时更可控
如果你用 Webpack/Vite 等工具构建,推荐在打包阶段把 flag 状态写入全局变量(如 window.__FEATURE_FLAGS__),而不是每次加载都发请求。这样能规避网络延迟、CORS、竞态等问题。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 构建脚本中通过环境变量传入,例如
VITE_FEATURE_AI=true vite build - 在入口 HTML 模板里插入:
<script>window.__FEATURE_FLAGS__ = { ai_suggestion: <%= JSON.stringify(process.env.VITE_FEATURE_AI === 'true') %> };</script> - JS 中直接使用:
if (window.__FEATURE_FLAGS__.ai_suggestion) { ... } - 缺点:修改开关需重新部署,不适合需要秒级生效的灰度场景
服务端渲染(SSR)才是真·灰度控制点
如果页面由 Node.js / PHP / Java 等后端渲染,Feature Flag 的判断应在服务端完成。HTML 输出前就决定是否包含某段 DOM,既避免前端 JS 闪动,也防止用户手动篡改 window.__FEATURE_FLAGS__ 绕过限制。
立即学习“前端免费学习笔记(深入)”;
- 典型错误:只在前端隐藏按钮,但没关掉对应 API 接口权限 → 功能仍可被调用
- 正确做法:服务端同时控制模板渲染 + 接口路由拦截 + 权限校验
- 灰度依据可以是 cookie、user_id 哈希、AB 测试分组 ID,而非简单 UA 或 IP
- 注意 CDN 缓存:带 flag 的页面不能被无差别缓存,需设置
Vary: Cookie或按分组生成不同 URL
Feature Flag 的复杂性不在 HTML 标签怎么写,而在于状态来源、更新时机、多端一致性以及降级策略。很多人卡在“JS 读了 flag 却没清空旧 DOM”或“构建时注入却忘了同步接口权限”,这些才是上线后出问题的高发区。

















