你好?
? 您是否曾经遇到过希望可以向少数用户推出一项功能,然后根据反馈/分析将其推广给 100% 的用户的情况?或者您的团队刚刚开发了一个巨大的功能,但营销/产品团队说我们还不会推出它?
? 您最终创建了一个单独的功能分支并尝试使其与您的主分支保持同步。但这并不止于此。几周后,您想启动该功能。现在,您必须再次触发部署。移动应用程序的情况更糟,完全推出需要 2-4 天。
?哦!等待?你发现了一个问题。您想阻止用户使用该功能。祝你好运!
? 为了拯救我们,开发人员,我们有功能标志!不仅是开发人员,它甚至可以帮助营销、产品和销售团队。
什么是功能标志?
我喜欢 LaunchDarkly 的定义
功能标志是一种软件开发过程/模式,用于在不部署代码的情况下远程启用或禁用功能。可以在不让用户看到的情况下部署新功能。功能标志有助于将部署与发布分离,让您管理功能的整个生命周期。
功能标志可用于:
- 运行 A/B 测试。
- 管理 Beta 程序。
- 减少多次部署或回滚。
- 提供基于角色的访问。
- 通过首先向较小的组推出功能来最大程度地减少发布失败。
一旦开始使用功能标志,就没有回头路了。
在 React 中添加功能标志
此实现使用 React ContextAPI 。在继续之前,请确保您了解基础知识。
让我们从一个例子开始:
想象一下,你在一个庞大的网站/应用程序的 ? 支付网关上工作。这最近增加了两种新的支付模式:Apple Pay 和 Google Pay。
作为 10 倍的开发人员,您很快就完成了这两个集成,但营销团队希望将 Google Pay 的发布推迟几周。 Apple Pay 将于明天上线。
您不想维护一个单独的分支并在几周后重新部署。因此,您选择添加功能标志。 ?
首先,让我们从为功能标志创建上下文开始。
// /contexts/FeatureFlags.js export const FeatureFlags = React . createContext ({});
现在,让我们创建一个 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 > ); };
我们的 Context 都设置好了,还有一些事情要做。现在,我们可以用这个提供者包装树。
// 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 > );
现在,我们所要做的就是获取我们的功能。我使用 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 }); });
回到我们的上下文文件,让我们获取特征。
// /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 > ); };
刚刚为我们的应用程序添加了一个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 ;
? 现在,我们可以完全控制新创建的功能来启动这个应用程序。
? 我们可以随时启用 Google Pay,用户会立即看到它。如果出现问题,我们可以禁用这两种付款方式。
reply . send ({ isGooglePayEnabled : false , isApplePayEnabled : false });
在你离开之前的最后一件事,这个实现是最低限度的。您可以扩展它以满足您团队的需求。我最关心的几个改进是:
- 添加一个
FeatureFlag
组件,该组件接受一个 propfeature
并基于该特性隐藏或呈现子项。
< FeatureFlag feature = "isGooglePayEnabled" > < button onClick = { handlePayment } > Google Pay </ button > </ FeatureFlag >
- 添加缓存和回退机制。如果您的 API 调用失败怎么办?在这种情况下,我们可以回退到我们的缓存版本。这个真的很有趣。 ?
? 哦,这里是代码框链接,如果你想玩的话。
这就是所有的人! ?
我希望这篇文章对你有所帮助。考虑与他人分享。
? 如果你想聊点什么,请在Twitter或LinkedIn上私信我。
原文: https://dev.to/thesanjeevsharma/adding-feature-flags-to-your-react-codebase-22a8