前端后端分离(BFF)模式允许您使用单独的后端来管理所有 API 查询和数据处理,并开发 React 应用程序。这有助于保持前端代码的简洁性和清晰性,并可以提高应用程序的性能。
如果使用像 Next.js 这样的框架,实现这一点甚至会更容易,因为它具有集成的 API 路由系统,可以让您快速构建前端应用程序的 API 服务。但这只是一种可选项,在应用程序中实现 BFF 模式并不一定需要Next.js,有超多别的选择。
那么,何时使用 BFF 模式对您的 React 应用程序可能是有利的呢?一个例子是具有多个 API 调用、数据处理和聚合责任的大型、复杂的前端应用程序,例如仪表板。通过将繁重的处理逻辑与前端分离,您可以创建一个更可扩展和可维护的架构。
如果您的应用程序有许多可共享的相似数据或 API 查询,使用 BFF 模式也可以帮助您避免重复代码,并促进应用程序之间的代码重用。
import { useState } from 'react'; // 堆代码 duidaima.com function MyComponent() { const [data, setData] = useState([]); const processData = (rawData) => { // super long and complex data processing here }; const loadData = () => { const rawData = [ { id: 1, title: 'Smartphone', category: 'electronics' }, { id: 2, title: 'Laptop', category: 'electronics' }, { id: 3, title: 'Chair', category: 'furniture' }, { id: 4, title: 'Table', category: 'furniture' }, ]; const processedData = processData(rawData); setData(processedData); }; loadData(); return ( <div> {data.map((item) => ( <div key={item.id}>{item.title}</div> ))} </div> ); }
import { useEffect, useState } from 'react'; import axios from 'axios'; const API_URL = 'https://my-bff-service.com'; function MyComponent({ data }) { return ( <div> {data.map(item => ( <div key={item.id}>{item.title}</div> ))} </div> ); } function MyBFFComponent() { const [data, setData] = useState([]); useEffect(() => { axios.get(`${API_URL}/my-data`).then(response => { setData(response.data); }); }, []); return <MyComponent data={data} />; }
在这个例子中,我们向 BFF 服务发送请求,该服务预处理我们的数据,这样我们就不需要在前端进行处理,而且可以减轻客户端的负担。然而,使用这种模式并不是只有优点,我们必须承担 API 请求延迟的代价。只有当客户端处理和数据聚合显著减慢我们的前端时,例如太多的网络请求、大型数据集上的复杂转换或其他类似情况时,将我们的数据处理转移到 API 上才是有利的。
React 中的 hooks 模式是一个很棒的特性,它允许我们在多个组件之间重用有状态行为。Hooks 是函数,它们使我们能够在函数组件中利用 React 的状态管理和其他能力。
import { useState, useEffect } from 'react'; import axios from 'axios'; function useFetch(url) { const [data, setData] = useState([]); useEffect(() => { axios.get(url).then((response) => { setData(response.data); }); }, [url]); return data; } function MyComponent() { const data = useFetch('https://my-api.com/my-data'); return ( <div> {data.map((item) => ( <div key={item.id}>{item.title}</div> ))} </div> ); }
在这个例子中,我们创建了一个名为 useFetch 的自定义 hook,它以 URL 作为参数并返回 API 的数据。然后我们在 MyComponent 组件中使用这个 hook 来获取和显示我们的数据。通过创建自定义 hook,我们可以在多个组件之间重用有状态逻辑,并创建更模块化和可维护的架构。
注意:虽然 React hooks 可以像普通函数一样声明和编写,但它们是独特的,因为它们是唯一能够hooking到 React 状态和生命周期中的特殊函数类型,它们很容易被识别,因为它们的名称前缀总是以 use 开头(自定义hook在实现或使用时会有一些限制,详情可查阅相关资料)。
高阶组件的一个流行的应用场景是执行身份验证。比如,我们可以编写一个函数组件,检查身份验证,然后判断是渲染指定的组件还是重定向到登录页面。然后,可以将此组件包装在 HOC 中,以添加身份验证功能到任何其他要保护的组件中。
这种模式有助于抽象出可能需要在多个组件中使用的常见功能,使我们的代码更模块化且更易于维护。此外,HOC 可以在多个组件中重复使用,避免代码重复。
import React from 'react'; function withAuth(WrappedComponent) { return function WithAuth(props) { const [isLoggedIn, setIsLoggedIn] = useState(false); useEffect(() => { // Check if the user is logged in const isLoggedIn = true; // Replace with actual authentication logic setIsLoggedIn(isLoggedIn); }, []); if (!isLoggedIn) { return <p>You must be logged in to access this content.</p>; } return <WrappedComponent {...props} />; }; } function MyComponent() { return <p>This is protected content that requires authentication.</p>; } export default withAuth(MyComponent);
在这个例子中,我们编写了一个名为 withAuth 的 HOC,它接受一个组件作为输入,并生成一个包含身份验证逻辑的新组件。然后,在我们的 MyComponent 组件中使用此 HOC 来保护我们的内容并要求身份验证。
观察者模式允许我们通过自动将一个对象中的更改传播到所有订阅者来构建对象之间的一对多关系。在 React 应用程序中,这种方法对于控制状态和数据流非常有用。
import { createContext, useContext, useState, useEffect } from 'react'; const MyContext = createContext([]); function MyProvider(props) { const [data, setData] = useState([]); useEffect(() => { // Fetch data from the API const newData = [...]; // Replace with actual data setData(newData); }, []); return <MyContext.Provider value={data}>{props.children}</MyContext.Provider>; } function MyObserver() { const data = useContext(MyContext); return ( <div> {data.map((item) => ( <div key={item.id}>{item.title}</div> ))} </div> ); } function App() { return ( <MyProvider> <MyObserver /> </MyProvider> ); }
在这个例子中,我们构建了一个名为 'MyProvider' 的提供者组件,它从 API 接收数据并使用 useContext hook 将数据传递给其子组件。然后使用 'MyObserver' 组件显示提供者的数据。通过使用观察者模式,我们可以构建一个更灵活、可维护的架构,用于控制 React 应用程序中的状态和数据流。
一个大型应用程序通常会结合多种模式形成更复杂的架构。为了构建一个可扩展和健壮的前端架构,我们可以将 BFF 模式、hooks 模式、高阶组件模式和观察者模式等模式集成到一个解决方案中。
import { createContext, useContext, useState, useEffect, useMemo } from 'react'; import axios from 'axios'; const API_URL = 'https://example.com/my-bff-service'; const MyContext = createContext([]); function withAuth(WrappedComponent) { return function WithAuth(props) { const [isLoggedIn, setIsLoggedIn] = useState(false); useEffect(() => { // Check if the user is logged in const isLoggedIn = true; // Replace with actual authentication logic setIsLoggedIn(isLoggedIn); }, []); if (!isLoggedIn) { return <div>You must be logged in to access this content.</div>; } return <WrappedComponent {...props} />; }; } function useFetch(url) { const [data, setData] = useState([]); useEffect(() => { axios.get(url).then((response) => { setData(response.data); }); }, [url]); return data; } const AuthenticatedMyComponent = withAuth(function MyComponent() { return ( <div> {data.map((item) => ( <div key={item.id}>{item.title}</div> ))} </div> ); }); function MyObserver() { const data = useContext(MyContext); return ( <div> {data.map((item) => ( <div key={item.id}>{item.title}</div> ))} </div> ); } function MyProvider({ children }) { const data = useFetch(`${API_URL}/my-data`); const value = useMemo(() => data), [data]) return ( <MyContext.Provider value={value}> {children} </MyContext.Provider> ); } function App() { return ( <MyProvider> <AuthenticatedMyComponent /> <MyObserver /> </MyProvider> ); } export default App;
在这个例子中,我们将所有上述的模式结合到一个应用程序中。我们使用 MyProvider 组件来提供数据给我们的 MyComponent 和 MyObserver 组件。我们还使用 withAuth HOC 来保护我们的 MyComponent 组件中的内容,并使用 useFetch hook 来从我们的 BFF 服务中获取数据。所有这些模式的组合简化了代码库中所有复杂的接口,现在我们可以轻松创建新的功能,而不需要重复代码。