发布于 2026-01-06 0 阅读
0

在你的 React 代码库中添加功能标志 DEV 的全球展示挑战赛,由 Mux 呈现:展示你的项目!

在 React 代码库中添加功能标志

由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!

嗨👋

🤔 你是否曾经遇到过这种情况:你希望先向少数用户推出某个功能,然后根据反馈/分析结果将其推广到所有用户?或者你的团队开发了一个大型功能,但市场/产品团队却说我们暂时不会发布?

😖 你最终会创建一个单独的功能分支,并试图让它与主分支保持同步。但这还没完。几周后,你想发布这个功能。现在,你又得重新触发部署。移动应用的情况更糟,因为完整的发布流程需要 2-4 天。

😭 哦!等等?你发现了一个问题。你想阻止用户使用该功能。祝你好运!

图片描述

👌 为了避免开发者们遇到类似的情况,我们推出了功能开关!它不仅对开发者有用,对市场营销、产品和销售团队也很有帮助。


什么是功能标志?

我喜欢 LaunchDarkly 的定义。

功能开关是一种软件开发流程/模式,用于在不部署代码的情况下远程启用或禁用功能。新功能可以在不向用户可见的情况下部署。功能开关有助于将部署与发布解耦,从而让您可以管理功能的完整生命周期。

特征标志可用于:

  1. 进行A/B测试。
  2. 管理 Beta 测试项目。
  3. 减少多次部署或回滚。
  4. 提供基于角色的访问权限。
  5. 通过先向较小群体推出功能,最大限度地减少发布失败。

一旦开始使用功能标志,就再也回不去了。


在 React 中添加功能标志

本实现使用了 React ContextAPI。在继续之前,请确保您已了解其基础知识。

我们先举个例子:
假设你负责一个大型网站/应用程序的💰支付网关。该网站/应用程序最近新增了两种支付方式:Apple Pay 和 Google Pay。

作为一名效率极高的开发者,你很快就完成了这两项集成,但市场团队希望将 Google Pay 的上线时间推迟几周。Apple Pay 将于明天正式上线。

你肯定不想维护一个单独的分支,然后在几周后重新部署。所以,你选择添加功能开关。😎

首先,让我们从创建功能标志的上下文开始。

// /contexts/FeatureFlags.js

export const FeatureFlags = React.createContext({});
Enter fullscreen mode Exit fullscreen mode

现在,让我们创建一个 Provider 来包装我们的 React DOM 树。

// /contexts/FeatureFlags.js

export const FeatureFlags = React.createContext({});

export const FeatureFlagsProvider = ({ children }) => {
  const [features, setFeatures] = React.useState({});

  return (
    <FeatureFlags.Provider value={{ features }}>
      {children}
    </FeatureFlags.Provider>
  );
};
Enter fullscreen mode Exit fullscreen mode

我们的上下文已经全部设置完毕,只剩下最后几项工作了。现在,我们可以用这个提供程序来封装这棵树。

// index.js

// ... imports here

import App from "./App";
import { FeatureFlagsProvider } from "./contexts/FeatureFlags";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <FeatureFlagsProvider>
      <App />
    </FeatureFlagsProvider>
  </StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

现在,我们只需要实现我们的功能。我已经用 Fastify 创建了一个示例 API,您可以忽略这部分。

// enabling cors for codesandbox
fastify.register(require("fastify-cors"), {
  origin: /\.csb\.app$/
});

// feature flags route
fastify.get("/feature-flags", function(request, reply) { 

  const features = {
    isGooglePayEnabled: true, 
    isApplePayEnabled: false
  }

  reply.send({ features });
});
Enter fullscreen mode Exit fullscreen mode

回到我们的上下文文件,让我们获取特征。

// /contexts/FeatureFlags.js

import { fetchFeatures } from 'api'

export const FeatureFlags = React.createContext({});

export const FeatureFlagsProvider = ({ children }) => {
   const [isLoading, setIsLoading] = React.useState(true);
   const [features, setFeatures] = React.useState({});

   React.useEffect(() => {
     (async () => {
       try {
         const data = await fetchFeatures();
         if (data.features) {
           setFeatures(data.features);
         }
       } catch (err) {
         console.log(err);
       } finally {
         setIsLoading(false);
       }
     })();
   }, []);

   return (
     <FeatureFlags.Provider value={{ features }}>
       {isLoading ? "Loading..." : children}
     </FeatureFlags.Provider>
   );
};
Enter fullscreen mode Exit fullscreen mode

我们刚刚useEffect为应用程序添加了加载状态。
大功告成!🎉

最后一步是将它应用到我们的组件中。

// components/PaymentOptions.js

import { FeatureFlags } from "contexts/FeatureFlags";

const PaymentOptions = () => {
  const { features } = React.useContext(FeatureFlags);

  const handleClick = () => alert("Payment successful!");

  return (
    <>
      <button className="btn" onClick={handleClick}>
        Credit Card
      </button>
      {features.isApplePayEnabled && (
        <button className="btn" onClick={handleClick}>
          Apple Pay
        </button>
      )}
      {features.isGooglePayEnabled && (
        <button className="btn" onClick={handleClick}>
          Google Pay
        </button>
      )}
    </>
  );
};

export default PaymentOptions;
Enter fullscreen mode Exit fullscreen mode

🚀 现在,我们可以启动这个应用程序,并完全控制新创建的功能。

👏 我们可以随时启用 Google Pay,用户会立即看到。如果出现问题,我们可以禁用这两种支付方式。

reply.send({ isGooglePayEnabled: false, isApplePayEnabled: false});
Enter fullscreen mode Exit fullscreen mode

临走前还有最后一点,这个实现方案只是最基本的功能。你可以根据团队的需求进行扩展。我目前想到的一些改进方向包括:

  • 添加一个FeatureFlag组件,该组件接受一个属性feature,并根据该属性隐藏或渲染子组件。
<FeatureFlag feature="isGooglePayEnabled">
  <button onClick={handlePayment}>Google Pay</button>
</FeatureFlag>
Enter fullscreen mode Exit fullscreen mode
  • 添加缓存和回退机制。如果 API 调用失败怎么办?在这种情况下,我们可以回退到缓存版本。这个功能真的很有意思。😉

🔗 哦,这里是codesandbox 链接,如果你想自己动手试试的话。


就这些啦!👋

希望这篇文章对你有所帮助。也欢迎分享给其他人。

🤙 如果你想和我聊任何事,请在TwitterLinkedIn上私信我。

文章来源:https://dev.to/thesanjeevsharma/adding-feature-flags-to-your-react-codebase-22a8