跳过主要内容

React Feature Flags: A Complete Implementation Guide

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

马丁·多纳迪厄

马丁·多纳迪厄

内容营销人员

React 功能标志: 完整的实现指南

您已经完成了功能。拉取请求很干净。QA 说它看起来很好。然而,您仍然不想一次性将其部署给所有人。

这种感觉通常是 React 应用程序超出了简单部署的第一个迹象。产品有真正的用户后,发布不再仅仅是一个技术事件。它变成了一个风险决策。如果新搜索 UI 破坏了,如果检出变体混淆了用户,或者如果移动构建部署了 code 您无法快速撤销,您需要更多的 if (process.env.NODE_ENV) 仅仅依靠希望是不够的。

这就是 react 功能标志 开始发挥作用的地方。不是作为组件中的一个可爱的布尔值,而是一个发布控制层,让您可以单独部署 code 而不暴露它。在 Web 应用程序中,这意味着更安全的发布。在打包应用程序,如 Capacitor 或 Electron,情况甚至更为严重,因为回滚速度受到商店审查、安装延迟和更慢的发布周期的限制。

目录

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

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

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

一个标题为《为什么特性旗帜对于现代React应用至关重要》的图表,解释了部署策略和好处。

__CAPGO_KEEP_0__

Deployment answers, “Is the code in production?” Release answers, “Who can execute this behavior right now?”

部署回答,“Capacitor 是否在生产环境中?”发布回答,“谁可以立即执行此行为?”

一旦 React 应用程序有了真正的流量、多个环境和影响收入、权限或导航的功能,区分这些概念就变得很重要。团队可以合并早期版本、在生产环境中测试内部小组,并在他们信任行为之前只扩大访问权限。对于像 __CAPGO_KEEP_0__ 这样的发布速度较慢的平台(如 Electron 应用程序和商店审查的移动构建),这种控制甚至更为宝贵,因为二进制文件可能已经在用户的手中了,而功能尚未准备好供所有人使用。

  • 标志在以下三个常见情况下有所帮助: 控制发布:
  • 首先暴露一个新路径给一个小组 实验:
  • 比较变体而不需要维护单独的部署 快速关闭:

A simple rule works well here. If a production issue would be expensive to reverse, ship that code behind a flag.

在生产问题需要反向很昂贵时,遵循一个简单的规则:将 __CAPGO_KEEP_0__ 后面跟一个标志。 flag ? <NewUI /> : <OldUI /> 是可见的部分,但这并不是最有趣的部分。其核心价值在于可操作性。远程配置、确定性目标和快速关闭特性的能力使旗帜在生产环境中有用。如果您的 React 应用程序还需要应用程序级别的运行时设置,则 适用于Capacitor应用程序的远程配置插件 遵循相同的发布控制模型。

当没有人信任它们时,旗帜就不再有用了。

我在成长的前端代码库中看到相同的失败模式。团队快速添加标志,环境之间的名称漂移,fallback 值隐藏配置错误, nobody 确定“on”是否表示全局开启、仅限员工开启还是仅限 staging 开启。在这种情况下,标志系统开始创造风险而不是减少风险。

类型安全有所帮助,但它并不能解决整个问题。团队仍然需要清晰的注册表、拥有权和一致的方法来评估标志跨应用程序。否则,React 组件最终会在启动或部分回滚时出现局部假设,这些假设会破坏。

区别很容易看出:

用例弱版本强版本
UI 切换组件状态中的本地布尔值远程标志与拥有权和滚动规则
发布安全手动部署回滚通过远程配置立即禁用
实验临时分支比较稳定分组分配和可测量的暴露

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

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

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

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

对于React应用程序,可靠的方法是将标志视为 runtime data. React flagging 的指南建议三件事:在服务器或本地 SDK 缓存中评估标志,确定分层分配,渲染最终 UI 状态之前的水合或使用抗闪烁保护,以便用户不会看到错误的默认值(React 标志方法).

改变了您的 code 应该存放的地方。将标志加载放在应用程序根处。使消费简单。避免在叶子组件中获取标志。

实用形状如下:

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

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

模式一:使用 React 上下文和自定义钩子

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

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>;
}

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

模式二:使用高阶组件

高阶组件(HOC)有用时,您想在不在各个组件中添加钩子调用时,gate整个屏幕、路由元素或遗留类组件。 使用方法: 缺点是间接性。钩子在现代 React 中更容易跟踪,而 HOC 可能会在 DevTools 中使组件树杂乱。然而,对于路由级 gate,仍然是干净的。

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} />;
    };
  };
}

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

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

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

React 特性标志模式比较

__CAPGO_KEEP_0__

__CAPGO_KEEP_1__

标准上下文 + 钩子高阶组件 (HOC)
最佳使用场景组件级别的决策和变体包裹完整页面、路由或遗留组件
灵活性
开发者体验在现代函数组件中非常强大当钩子不方便使用时有用
Bundle clarityClear imports and direct readsMore abstraction in the tree
TestingEasy to mock via providerEasy for wrapped integration cases
Long-term maintainabilityUsually betterFine when used sparingly

If you’re implementing react feature flags for the first time, start with Context + Hook. Add an HOC only when you have a specific need for wrapper-style gating.

实施回滚和回退策略

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

软件功能标志的回滚和回退策略的漏斗图

百分比回滚需要粘性分配

百分比回滚只有在分配稳定时才有效。如果同一用户在一次访问时看到新结账界面,在下一次访问时看到旧结账界面,支持人员无法复制问题,分析数据变得噪音,用户信任会丧失。

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

实际的回滚路径如下:

  • 首先使用内部用户和 QA
  • 将其发布到小型试验组
  • 在检查错误率、转换影响和支持票后,逐步扩大范围。
  • 在旗帜的整个生命周期中保持分配稳定。

That last point is easy to underestimate. Sticky cohorts are not just for experiments. They make incident response faster because engineers can answer a basic question immediately: which users were exposed?

If you do run experiments, size them before you ship. A sample size calculator from Optimizely shows how traffic volume, baseline conversion, and minimum detectable effect change the number of users you need per variant (Optimizely sample size calculatorA useful reference for staged updates outside the browser is

phased rollouts for __CAPGO_KEEP_0__ live updates phased rollouts for Capacitor live updatesTargeted and ring-based releases reduce blast radius

Some features should not start with a random percentage. Billing flows, permission prompts, data migrations, and anything that can lock users out usually need targeted release first.

Targeting works well when the first audience is defined by known traits:

Internal staff for dogfooding

  • Beta testers who agreed to rough edges
  • Optimizely
  • 特定账户等级
  • 具有不同法律或语言要求的地区
  • 支持该功能的设备或应用版本

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

这里是嵌入式的导览,配套的发布模型:

杀死switch是旗帜的价值所在

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

在发布之前设计杀死switch:

  • 在应用启动早期评估它。
  • 缓存最后一次安全值。
  • 如果旗帜服务不可用时选择一个安全的默认值。
  • 确保禁用功能停止副作用,而不是仅仅停止渲染。
  • Document who can flip it during an incident.

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

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

添加特性标志的容易部分是添加一个。昂贵的部分是稍后发生的,当有很多标志时,Nobody还记得哪些标志仍然重要。

一间现代的服务器房,展示了排列整齐的服务器机柜和闪烁的灯光和有组织的网络线路

每个标志都会增加您必须信任的状态数量

马丁福勒的警告仍然有效:一旦存在特性标志,团队必须验证 OnOff 状态,并且随着多个标志的出现,可能的状态组合会以组合方式增长,这会增加回归风险(马丁福勒关于特性开关的说法).

That has direct consequences for React apps:

  • 条件渲染路径会迅速传播: 一个页面可以有多个 branch 在任何人注意到之前就已经存在了.
  • hydration 的不匹配变得更容易触发: 客户端和服务器可能会因为评估时间不当而产生分歧.
  • 单元测试评估逻辑. 组件测试关键 flag 分支.

仅对风险路径添加端到端覆盖.

  1. 显式验证默认回退。
  2. 测试栈看起来像这样:
  3. 测试评估逻辑的单元测试.
  4. 测试关键 flag 分支的组件测试.

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

旗子债务是真实的,它会悄悄地变得很昂贵。

旧的旗子会变成一种code的腐烂。它们会留在条件语句、注释、仪表板和操作手册中。然后有人会在几个月后编辑“临时” branch,因为没有人删除它。

在实际中有效的清理规则是简单的:

问题做什么
没有负责人在创建旗子时分配一个团队或人员
没有结束状态决定是否删除、保留或将旗子转换为配置
旗子控制太多将其分解为更小、更窄的旗子
Core logic hidden behind flags将商业规则从渲染条件语句中移出

Cleanup 规则: 每个标志都应有一个拥有者、一个目的和一个在第一天就有的移除计划。

这也是团队在“信任”问题中受伤的地方。标志名称存在,但回退是错误的。仪表板条目发生了变化,但应用类型没有。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移动应用程序的团队应该更加严格地遵守审批规则,因为回滚通常意味着禁用已发布的功能而不是替换二进制文件。 将标志操作放入管道中。

__CAPGO_KEEP_0__:__CAPGO_KEEP_1__:__CAPGO_KEEP_2__

旗帜变得难以信任,尤其是它们生活在你的交付过程之外。更安全的模式是将它们管理为同一工作流程的一部分,这个工作流程负责交付功能。

这通常意味着:

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

我更喜欢一个简单的规则:如果生产事故可能由旗帜引起,CI应该在发布之前捕获设置。包括缺失的默认值、重命名的键、陈旧的环境映射和存在于code但不在控制平面的旗帜。

如果你需要一个pipeline结构的起点, Git Action CI/CD工作流 是构建检查、部署门槛和自动化步骤的坚实参考,用于扩展旗帜验证。

保留机密和SDK选择的枯燥

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

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

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

超越Web特性标志forCapacitor和移动应用

大多数关于react特性标志的文章假设一个可以立即重新部署的Web应用。 这个假设一旦您的Reactcode生活在 Capacitor, Electron,或另一个捆绑运行时

捆绑应用改变了发布数学

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

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

的确如此。在打包应用中,标志不再是关于条件渲染,而是关于 远程激活已经发布的能力.

在移动或桌面React应用中,标志通常控制发布时间而不是UI存在

这也是为什么基于通道的分发很重要。如果您正在构建混合应用并需要应用壳和webcode发布模型一起工作, 使用Capacitor创建React移动应用 是一个实际的起点

标志在配对时最有效。

对于移动和桌面团队,标志单独无法解决每个发布问题。它们可以隐藏或启用code路径,但无法替代在捆绑包中修复的资产或逻辑,尤其是bug已经在捆绑包中

这就是为什么更强大的模型是:

  • 在您的平台允许的情况下,通过code更新外部发布应用,避免每个完整的商店周期
  • 针对目标用户群或频道进行更新,
  • 并使用标志来控制激活、回滚和阶段性发布。

使用这些功能,实时更新和标志可以为混合团队提供更接近Web发布控制的体验。然而,这并不意味着需要放松纪律。它只是给了你更多的控制手段,当出现问题时。


如果您的团队开发或部署Capacitor或Electron应用,并需要发布控制层, Capgo 是一个值得考虑的选项。它可以将签名的Web包分发到目标频道,支持回滚保护和可观察性,并适合混合应用的工作流程,特性标志需要与实时更新一起工作,而不是取代它们。

继续阅读:React Feature Flags: A Complete Implementation Guide

如果您正在使用 React Feature Flags: A Complete Implementation Guide 来规划频道路由和阶段性发布, Channels 可以帮助您了解Channels的实现细节。 频道 了解频道的实现细节 频道 了解频道的实现细节 Beta 测试解决方案 了解 Beta 测试解决方案中的产品工作流程 版本目标解决方案 了解版本目标解决方案中的产品工作流程

为 Capacitor 应用提供实时更新

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

立即开始

最新博客

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