跳过主要内容

React Feature Flags: A Complete Implementation Guide

了解如何使用我们的完整指南实现React特性标志。涵盖了架构模式、发布策略、CI/CD和现代应用程序的最佳实践。

Martin Donadieu

Martin Donadieu

内容营销人员

React Feature Flags: A Complete Implementation Guide

您完成了该功能。拉取请求清洁。QA说它看起来不错。然而,您仍然不想一次性将其部署到所有用户中。

当产品有真正的用户时,发布不再仅仅是一个技术事件。它变成了一个风险决策。如果新搜索UI破坏了它,如果检出变体混淆了用户,或者如果移动构建部署了code您无法快速撤销,您需要更多 if (process.env.NODE_ENV) 希望如此。

那就是 react特性标志 开始变得重要。不是作为一个可爱的布尔值在组件中,而是作为一个发布控制层,让您可以单独发布code而不暴露它。在Web应用中,这意味着更安全的发布。在打包应用程序,如Capacitor或Electron中,它们更重要,因为回滚速度受商店审查、安装延迟和更慢的发布周期的限制。

目录

为什么特性标志对于现代 React 应用至关重要

星期五下午发布。新账单摘要 UI 已经部署,支持有一个发布清单开启,一个企业客户仍然需要老的流程直到星期一。在一个已经紧张的 Web 应用中,这是如此。在一个通过桌面安装程序或移动商店分发的捆绑 React 应用中,情况会更糟糕,因为回滚可能需要几个小时或几天,而不是几分钟。

特性标志让 React 团队控制这一时刻。它们让您可以发布 code,让它保持休眠状态,并决定哪些用户应该看到它。这样改变了发布工作从一个全或无的事件转变为一个受控的操作。

一个标题为“为什么特性标志对于现代 React 应用至关重要”的 infographic,解释了部署策略和好处。

部署和发布是不同的工作

部署回答,“ code 是否在生产环境中?”发布回答,“谁可以现在执行这个行为?”

当一个 React 应用程序有实时流量、多个环境和影响收入、权限或导航的功能时,区别就变得很重要。团队可以在内部小组测试中合并代码,仅在他们信任行为时才扩大访问权限。对于像 Capacitor 应用程序、Electron 应用程序和商店审查的移动构建这样的较慢发布平台,这种控制就更加宝贵,因为二进制文件可能已经在用户的手中了,而功能还没有准备好供所有人使用。

标志在以下三个常见情况下都有帮助:

  • 控制发布: 先向小组暴露一个新路径
  • 实验: 比较变体而不需要维护单独的部署
  • 快速关闭: 禁用一个风险功能而不必等待新构建

在生产问题需要反向成本很高时,遵循一个简单的规则:将该 code 后面跟一个标志。

新到标志的团队通常会停在 UI 条件化上,但这并不是最有趣的部分。标志的核心价值在于运营。远程配置、确定性目标和快速关闭功能是标志在生产环境中有用的。 flag ? <NewUI /> : <OldUI /> 如果您的 React 应用程序还需要应用程序级的运行时设置,那么一个 Capacitor 应用程序的远程配置插件 targetLanguage":"Simplified Chinese"

protectedTokens":["Cloudflare","Capacitor","GitHub","Capgo","code","API","SDK","CLI","npm","bun"]

texts":["符合同一发布控制模型。",

"标志在没有人信任它们时就不再有用"

"我在不断增长的前端代码库中看到相同的失败模式。团队快速添加标志,环境之间的名称漂移,fallback值隐藏配置错误, nobody知道“on”是否表示全局开启,仅限员工,还是仅限测试环境。到那时,标志系统开始产生风险而不是减少风险。"

"类型安全有所帮助,但它并不能解决整个问题。团队仍然需要一个清晰的注册表,拥有者和一致的方式来评估标志跨应用。否则,React组件最终会在发布或部分回滚时打破本地假设。" "区别很容易看出:" "用例"
"弱版本" "强版本" "UI切换"
"组件状态中的本地布尔值""远程标志,拥有者和发布规则""发布安全" 手动部署回滚 远程配置立即禁用
实验 临时分支比较 稳定分组分配和可测量的暴露

重要的思维转变很简单。 React 特性标志属于您的发布过程,而不仅仅是您的 JSX。 尤其是在应用程序中,发布新版本的速度慢,标志成为减少生产环境混乱时爆炸半径的少数工具之一。

在 React 应用程序中架构特性标志

架构决策比第一个标志更重要。如果您将标志直接连接到随机组件中,会出现重复的逻辑、加载闪烁和无法信任的代码库,哪个是真实的来源。

使用运行时提供者,而不是散布的条件

对于 React 应用程序,可靠的方法是将标志视为 运行时数据。 对于 React 标志的指南建议三件事:在服务器上或本地SDK缓存中评估标志,确定性地持久化分组分配,并在水合之前渲染最终 UI 状态或使用抗闪烁保护,以便用户不看到错误的默认值。React 标志方法论).

That changes where your code should live. Put flag loading near the app root. Make consumption simple. Avoid fetching flags inside leaf components.

实用形状如下:

  1. 在主树渲染之前加载或激活标志。
  2. 通过提供者暴露它们。
  3. 通过一个钩子或一个包装器模式读取它们。
  4. 将评估逻辑从展示组件中分离出来。

如果您需要一个远程配置层来管理应用程序级别的设置以及标志,一个工具类似于 Capacitor 远程配置插件 在混合 React 应用程序中,这种模式与本模式自然相结合。

模式一:使用 React Context 和一个自定义钩子

这是我通常推荐的默认模式。它明确、可测试,并且如果您切换供应商后可以轻松迁移。

import React, { createContext, useContext, useMemo } from 'react';

type FlagValue = boolean | 'control' | 'variant-a' | 'variant-b';

type Flags = {
  newCheckout: boolean;
  checkoutExperiment: FlagValue;
  deleteTaskEnabled: boolean;
};

const defaultFlags: Flags = {
  newCheckout: false,
  checkoutExperiment: 'control',
  deleteTaskEnabled: false,
};

const FeatureFlagContext = createContext<Flags>(defaultFlags);

export function FeatureFlagProvider({
  flags,
  children,
}: {
  flags: Flags;
  children: React.ReactNode;
}) {
  const value = useMemo(() => flags, [flags]);
  return (
    <FeatureFlagContext.Provider value={value}>
      {children}
    </FeatureFlagContext.Provider>
  );
}

export function useFeatureFlag<K extends keyof Flags>(key: K): Flags[K] {
  return useContext(FeatureFlagContext)[key];
}

使用方式保持平淡,这正是你想要的:

function DeleteTaskButton() {
  const enabled = useFeatureFlag('deleteTaskEnabled');

  if (!enabled) return null;
  return <button>Delete task</button>;
}

这种模式很有效,因为你的组件只要求一个最终答案。它们不关心答案是如何计算出来的。

模式二:使用高阶组件

高阶组件 当你想在整个屏幕、路由元素或遗留类组件上添加一个门控器,而不在每个地方添加hook调用时,高阶组件是有用的。 使用方法:

import React from 'react';
import { useFeatureFlag } from './FeatureFlagProvider';

export function withFeatureFlag<P>(
  flagKey: 'newCheckout' | 'deleteTaskEnabled',
  Fallback?: React.ComponentType<P>
) {
  return function wrap(Component: React.ComponentType<P>) {
    return function FeatureFlaggedComponent(props: P) {
      const enabled = useFeatureFlag(flagKey);

      if (!enabled) {
        return Fallback ? <Fallback {...props} /> : null;
      }

      return <Component {...props} />;
    };
  };
}

缺点是间接性。Hook在现代React中更容易追踪,而HOC在DevTools中可能会使组件树更嘈杂。然而,对于路由级门控,它们是干净的。

const CheckoutPage = () => <div>New checkout</div>;
const LegacyCheckoutPage = () => <div>Legacy checkout</div>;

export default withFeatureFlag('newCheckout', LegacyCheckoutPage)(CheckoutPage);

不要让组件决定发布策略。组件应该消费一个标志结果,而不是实现分桶、用户目标定位或缓存刷新规则。

React特性标志模式比较

标准

上下文+Hook __CAPGO_KEEP_0__ 高阶组件 (HOC)
最佳用例 组件级别的决策和变体 包裹完整页面、路由或遗留组件
灵活性
开发者体验 在现代函数组件中很强大 当钩子不方便时很有用
打包清晰度 清晰的导入和直接读取 树结构中的更多抽象
测试 通过提供商轻松模拟 包裹的集成案例容易
长期可维护性 通常更好 当你第一次实现react特性标志时,首先使用

上下文+钩子 只有当你有特定的需要时才添加HOC实施回滚和回滚策略

回滚计划在功能发布后出现问题的那一天最为重要。UI可能只显示一个新按钮或屏幕,但关键任务是决定谁先看到它,暴露速度如何以及如何快速关闭它而不等待重新部署。这种情况在React应用程序内置于移动或桌面包中的情况下尤其重要,因为回滚取决于远程配置,因为应用商店审查或桌面分发需要时间。

如果你正在实现react特性标志的第一次时间,首先使用

A funnel图表展示了从内部到全球发布的软件特征标志的滚动和回滚策略。

百分比滚动需要粘性分配

百分比滚动只有在分配稳定时才有效。如果同一用户在一次访问中获得新版checkout,而在下一次访问中获得旧版checkout,支持团队就无法复制问题,分析数据变得杂乱,用户信任度下降。

解决方案很简单。使用稳定标识符加上标志键的确定性哈希来分桶用户。用户ID通常是正确的输入。匿名会话可以使用安装ID或设备ID,如果有的话。 Math.random() 在浏览器中是错误的工具,因为它会不确定地重新分配用户。

实际的滚动路径如下:

  • 首先使用内部用户和QA。
  • 将小型团队释放到。
  • 在检查错误率、转换影响和支持票后,逐步扩大。
  • 保持标志的分配粘性,直到标志的整个生命周期。

最后一点容易低估。粘性团队不仅适用于实验。它们使事故响应更快,因为工程师可以立即回答一个基本问题:哪些用户暴露了?

如果您运行实验,务必在发布之前进行大小调整。Optimizely的样本大小计算器显示了流量量、基准转换率和最小可检测效果如何影响需要的用户数量和变体数优化利宝样本大小计算器如果没有检查,团队经常会将噪音误认为是信号,并提前推广一个功能。

对于浏览器外的阶段性更新有用的参考是 阶段性发布对于Capacitor实时更新当 React 应用程序运行在打包的 shell 中时,二进制回滚速度更慢的发布规则同样适用。

目标和环形发布可以减少爆炸半径

有一些功能不应该以随机百分比开始。计费流程、权限提示、数据迁移以及任何可能锁定用户的功能通常需要先进行目标发布。

目标发布在第一批受众被定义为已知特征时效果很好:

  • 内部员工进行内部测试
  • 同意存在粗糙边缘的 beta 测试者
  • 特定账户等级
  • 具有不同法律或语言要求的地区
  • __CAPGO_KEEP_0__

设备或应用版本支持该功能的安全性

环形发布使目标更具操作性。环 0 是员工。环 1 是信任的外部测试者。后续环扩大暴露范围,随着信心的提高

此发布模型的嵌入式教程:

杀死switch是赚取其存在的旗帜

每个有风险的功能都需要一个快速的脱离路径。通常情况下,这意味着一个顶级的操作旗帜,禁用整个功能流,而不是一个仅仅隐藏一个入口点的显示旗帜,后台请求、效果或导航路径仍在运行

  • 在发布前设计杀死switch:
  • 在应用启动时尽早评估它
  • 缓存最后一次安全值
  • 如果旗帜服务不可用时选择一个安全的默认值
  • 确保禁用功能停止副作用,而不是仅仅渲染

在事故期间,记录谁可以翻转它。对于仅限Web应用,减少发布风险。对于移动和桌面React应用,它可以是区分轻微事故和等待用户获取修复版本的差异。如果code已经在捆绑包中发布,远程标志就成为回滚策略的一部分,而不是仅仅是发布策略的一部分。

测试可观察性并管理标志债务

添加特性标志的容易部分是添加一个。 expensive 的部分是后来,当有很多标志时,Nobody 记得哪些仍然重要

现代服务器房,展示了排列整齐的服务器机柜和闪烁的灯光

每个标志都增加了必须信任的状态

马丁福勒的警告仍然有效:一旦存在特性标志,团队必须验证 开启 关闭 状态, 当有多个标志时,可能的状态组合会以组合方式增长,这会提高回归风险(马丁福勒关于特性开关的说法).

这对 React 应用程序有直接的后果:

  • 条件渲染路径会迅速扩散: A single page can have multiple branches before anyone notices.
  • hydration 的不匹配变得更容易触发: 客户端和服务器可能会因为评估时间不当而产生分歧。
  • 单独的快照测试变得不那么有用: 一个happy-path渲染并不能告诉你什么,如果相反的标志状态没有测试,那么什么都不知道。

一个实用的测试栈看起来像这样:

  1. 单元测试评估逻辑。
  2. 组件测试关键标志分支。
  3. 仅对风险路径添加端到端覆盖。
  4. 明确验证默认回退。

不要追求每种组合。通常会因为自己的重量而崩溃。测试那些可能伤害用户或破坏布局的状态。

标志债务是真实的,它会悄悄地变得很昂贵

旧标志变成一种形式的code腐烂。它们停留在条件语句、注释、仪表板和运行手册中。然后有人在几个月后编辑“临时” branch,因为没有人移除它。

实践中有效的清理规则很简单:

问题 做什么
没有负责人 在创建标志时分配团队或人员
没有结束状态 决定标志是否被删除、保留或转换为配置
标志控制太多 将其分解为更小、更窄的标志
核心逻辑隐藏在标志后面 将商业规则从渲染条件语句中移出

Cleanup rule: 每个标志都应该有一个拥有者、一个目的和一个移除计划,第一天就要有了。

团队也会因为“信任”问题而受伤害。标志名称存在,但回退设置错误。仪表板条目发生了变化,但应用类型没有改变。code路径已经死亡,但仍然可以访问。因此,类型生成和注册表验证在更大的系统中很重要,即使初始实现看起来很简单。

可观察性会告诉你这个标志是否有用还是只是存在

一个发布不是完整的,因为标志已经达到最大曝光度。它是完整的,当团队知道发生了什么时

至少要跟踪这些问题:

  • 曝光度: 哪些用户看到哪个变体?
  • 错误: 标志路径是否触发了更多的客户端故障?
  • 采用率: 用户是否使用了你暴露的功能?
  • 回滚信号: 什么阈值会让你关闭它?

如果你的标志平台无法回答这些问题,你仍然会在发布审查中猜测。

CI/CD 中保护标志和自动化

A bad deploy is obvious. A bad flag change is quieter, and in some cases more dangerous, because it changes production behavior without going through the same review path as code.

使用 CI/CD 流程和工具保护标志并自动化工作流程的示意图。

将标志更改视为生产更改

标志是发布控制。如果一个团队可以在生产中切换标志,那么这个团队就可以改变用户看到什么,什么code路径运行,甚至哪些集成触发。那样值得像部署访问一样严格的纪律。

最低控制是简单的:

  • 基于角色的访问: 限制谁可以更改生产标志,并将读取访问权限与编辑访问权限分开。
  • 审计日志: 记录每个标志的修改者、修改时间和所触及的环境。
  • 环境隔离: 预发布、预览和生产标志应该是不同的,以防止测试变更影响到线上流量。
  • 服务器端检查敏感决策: 客户端标志可以隐藏UI,但不应决定计费权限、权利或授权。

常见的错误是把标志控制台当作共享表格。产品为客户开启某项功能,支持团队关闭它以解决问题,工程团队则认为没有人修改它,因为没有发布。这种设置会在需要解释事故时出现问题。

捆绑应用程序的风险更高。在Web应用程序中,一个code修复可以快速发布。在Capacitor或桌面应用程序中,可能已经在设备上等待远程标志暴露的code已损坏的版本。构建 使用Capacitor的React移动应用程序的团队 应更加严格地遵守审批规则,因为回滚通常意味着禁用已发布的功能而不是替换二进制文件。

将标志操作放入管道中

标志变得难以信任,尤其是当它们不受同一流程管理时。更安全的模式是将它们管理为同一工作流程的组成部分,该工作流程负责部署功能。

这通常意味着:

  • 创建或更新同一 PR 中的标志code
  • 在 CI 阶段验证类型标志定义与远程注册表的匹配
  • 根据环境故意设置默认值
  • 如果必需的标志缺失或配置错误,阻止发布
  • 为标志设置过期日期或滚动结束状态的任务定时清理

我认为一个简单的规则是:如果生产事故可能由标志引起,CI 应该在发布前能够捕获设置。包括缺失的默认值、重命名的键、陈旧的环境映射以及标志code存在但在控制平面中不存在的部分

如果您需要一个管道结构的起点, Git Action CI/CD 工作流 是您可以扩展的构建检查、部署门户和自动化步骤的坚实参考

保持机密和SDK选择的平淡

前端团队有时会对标志安全性进行过度复杂化,并忽略了明显的部分。公共客户端SDK密钥通常是安全的,如果供应商为浏览器使用而设计。管理员令牌、写入凭证和环境管理密钥不属于此类。它们只应在 CI 或后端服务中使用

实践上的分离很简单。使用客户端评估进行呈现更改和低风险实验。使用服务器端评估进行定价、权限、敏感流程中的杀switch和您不信任本地 JavaScript 的任何内容。

在较慢的发布环境中,这个界限更为重要。 Web 团队可以通过快速部署恢复。 移动和桌面团队通常需要使用标志系统作为恢复机制。如果错误的人可以编辑生产标志,或者 CI never 验证标志合同,回滚会迅速变得混乱。

Beyond the Web Feature Flags for Capacitor and Mobile Apps

大多数关于 React 特性标志的文章假设一个可以立即重新部署的 Web 应用。 一旦您的 React code 生活在 Capacitor, ,或另一个捆绑运行时中,这个假设就会破裂。捆绑应用改变了发布的数学

在混合应用中,您通常会将 JavaScript、CSS、资产和配置文件打包在一起,用户不会立即更新。 一项功能可能已经在设备上存在之前您希望任何人使用它。 这改变了标志的作用方式。

最近关于混合发布策略的讨论指出,现有的 React 标志内容很少涉及__CAPGO_KEEP_0__或 Electron 应用的发布风险模型。 对于这些团队,主要需求是结合标志、目标通道和回滚保护的发布协调层,而不是简单的开关,尤其是避免商店审查延迟时

A recent discussion around hybrid release strategy pointed out that existing React flag content rarely addresses the release-risk model for Capacitor or Electron apps. For those teams, the primary need is a release orchestration layer that combines flags, targeted channels, and rollback protection instead of a simple on/off switch, especially when avoiding store review delays matters (这正是正确的。在捆绑应用中,标志不再是关于条件渲染,而是关于).

远程激活已经部署的能力 远程激活已经部署的能力.

In 移动或桌面 React 应用中,标志通常控制发布时间比 UI 状态更重要。

This is also why channel-based distribution matters. If you’re building hybrid apps and need the app shell plus web code release model to make sense together, 创建 React 移动应用程序与 Capacitor 是实用性的起点。

标志在配对更新时最有效。

对于移动和桌面团队,标志独自无法解决每个发布问题。它们可以隐藏或启用 code 路径,但无法取代在捆绑包中已有的 bug 时的固定资产或逻辑的交付。

因此更强大的模型是:

  • 在您的平台允许的情况下,交付 code 更新,
  • 针对这些更新的目标频道或受众,
  • 并使用标志来控制激活、回滚和阶段性曝光。

当您一起使用实时更新和标志时,混合团队会获得更接近 web-style 发布控制。


If your team ships Capacitor or Electron apps and needs that release-control layer, Capgo 这是一个可供查看的选项。它将签名的Web包分发到目标渠道,支持回滚保护和可观察性,并适合混合应用工作流程,特性标志需要与实时更新一起工作,而不是替换它们。

从 React 特性标志:全面实现指南继续前进

如果您正在使用 React 特性标志:全面实现指南 来规划渠道路由和阶段性发布,连接它与 Channels 查看Channels的实现细节 Channels 查看Channels的实现细节 Channels 查看Channels的实现细节 Beta Testing Solution 为产品工作流程中的Beta Testing Solution, 和 版本目标解决方案 为产品工作流程中的版本目标解决方案

实时更新Capacitor应用

当Web层面的bug在线时,通过Capgo将修复直接发送给用户,而不是等待几天的应用商店审批。用户在后台接收更新,而原生变化仍然在正常的审批路径中

立即开始

最新博客

Capgo 为您提供创建真正专业的移动应用所需的最佳见解。