gpt4 book ai didi

javascript - 如何自定义延迟加载页面之间共享的 React 组件的样式?

转载 作者:行者123 更新时间:2023-12-01 15:14:49 24 4
gpt4 key购买 nike

我正在构建一个 React 应用程序并开始使用 CRA .我使用 React Router 配置了应用程序的路由.页面组件是延迟加载的。
有 2 页:主页和关于。

...
const Home = lazy(() => import('./Home'));
const About = lazy(() => import('./About'));

...
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route path="/about" component={About} />
<Route path="/" component={Home} />
</Switch>
</Suspense>
...
每页使用 Button下面的组件。
import React from 'react';
import styles from './Button.module.scss';

const Button = ({ children, className = '' }) => (
<button className={`${styles.btn} ${className}`}>{children}</button>
);

export default Button;
Button.module.scss文件只是将按钮的背景颜色设置为红色。
.btn {
background: red;
}
Button组件接受 className然后将其添加到呈现的按钮中。这是因为我想给组件的消费者自由。例如,在某些页面中可能需要边距,或者背景应该是黄色而不是红色。
为简单起见,我只想为 Button 设置不同的背景颜色。基于当前页面,这样:
  • 首页 => 蓝色按钮
  • 关于页面 => 黄色按钮

  • 每个页面定义如下:
    import React from 'react';
    import Button from './Button';
    import styles from './[PageName].module.scss';

    const [PageName] = () => (
    <div>
    <h1>[PageName]</h1>
    <Button className={styles.pageBtn}>[ExpectedColor]</Button>
    </div>
    );

    export default [PageName];
    在哪里 [PageName]是页面的名称和 [ExpectedColor]是基于上述项目符号列表的相应预期颜色(蓝色或黄色)。
    导入的SCSS模块,导出一个类 .pageBtn设置 background属性为所需的颜色。
    注:我可以在 Button 上使用 Prop 定义要显示的变体(蓝色/黄色)并基于该 Prop 添加在 SCSS 文件中定义的类的组件。我不想这样做,因为更改可能不属于变体(例如 margin-top )。
    问题
    如果我使用 yarn start 运行应用程序,应用程序运行良好。但是,如果我构建应用程序( yarn build )然后开始为应用程序提供服务(例如,使用 serve -s build ),行为会有所不同,并且应用程序无法按预期工作。
    加载主页时,按钮正确显示为蓝色背景。检查加载的 CSS block ,它包含:
    .Button_btn__2cUFR {
    background: red
    }

    .Home_pageBtn__nnyWK {
    background: blue
    }
    没关系。然后我单击导航链接以打开“关于”页面。即使在这种情况下,按钮也会以黄色背景正确显示。检查加载的 CSS block ,它包含:
    .Button_btn__2cUFR {
    background: red
    }

    .About_pageBtn__3jjV7 {
    background: yellow
    }
    当我返回主页时,按钮现在显示为红色背景而不是黄色。这是因为 About 页面已经加载了上面的 CSS,它再次定义了 Button_btn__2cUFR类(class)。由于类(class)现在是 之后 Home_pageBtn__nnyWK类定义,按钮显示为红色。
    注: Button组件不会在公共(public) block 上导出,因为它的大小太小。把它放在一个共同的 block 中可以解决这个问题。但是,我的问题是关于小型共享组件。
    解决方案
    我想到了 2 个解决方案,但是我不太喜欢:
    增加选择器的特异性 [PageName].module.scss 中指定的类可以定义为:
    .pageBtn.pageBtn {
    background: [color];
    }
    这将增加选择器的特异性并覆盖默认的 Button_btn__2cUFR类(class)。但是,如果组件非常小(小于 30kb),每个页面 block 将包含共享组件。此外,组件的使用者必须知道这个技巧。
    弹出并配置 webpack
    弹出应用程序(或使用类似 react-app-rewired 的东西)将允许使用 webpack 指定公共(public) block 的最小大小。但是,这不是我想要的所有组件。

    总而言之,问题是:在使用延迟加载路由时,覆盖共享组件样式的正确工作方式是什么?

    最佳答案

    您可以将以下逻辑与配置文件一起用于任何页面。此外,您可以从远程服务器(req/res API)发送配置数据并使用 redux 进行处理。
    见演示:CodeSandBox
    创建 components目录并创建如下文件:

    src
    |---components
    |---Button
    | |---Button.jsx
    | |---Button.module.css
    按钮组件:
    // Button.jsx

    import React from "react";
    import styles from "./Button.module.css";

    const Button = props => {
    const { children, className, ...otherProps } = props;
    return (
    <button className={styles[`${className}`]} {...otherProps}>
    {children}
    </button>
    );
    };

    export default Button;
    ...
    // Button.module.css

    .Home_btn {
    background: red;
    }
    .About_btn {
    background: blue;
    }
    创建 utils目录并创建 AppUtils.js文件:

    This file handle config files of pages and return new object

    class AppUtils {
    static setRoutes(config) {
    let routes = [...config.routes];

    if (config.settings) {
    routes = routes.map(route => {
    return {
    ...route,
    settings: { ...config.settings, ...route.settings }
    };
    });
    }

    return [...routes];
    }

    static generateRoutesFromConfigs(configs) {
    let allRoutes = [];
    configs.forEach(config => {
    allRoutes = [...allRoutes, ...this.setRoutes(config)];
    });
    return allRoutes;
    }
    }

    export default AppUtils;
    创建 app-configs目录并创建 routesConfig.jsx文件:

    This file lists and organizes routes.

    import React from "react";

    import AppUtils from "../utils/AppUtils";
    import { pagesConfig } from "../pages/pagesConfig";

    const routeConfigs = [...pagesConfig];

    const routes = [
    ...AppUtils.generateRoutesFromConfigs(routeConfigs),
    {
    component: () => <h1>404 page not found</h1>
    }
    ];

    export default routes;
    修改 index.jsApp.js文件到:
    // index.js

    import React from "react";
    import ReactDOM from "react-dom";
    import { BrowserRouter as Router } from "react-router-dom";

    import App from "./App";

    const rootElement = document.getElementById("root");
    ReactDOM.render(
    <React.StrictMode>
    <Router>
    <App />
    </Router>
    </React.StrictMode>,
    rootElement
    );
    ...

    react-router-config: Static route configuration helpers for ReactRouter.

    // App.js

    import React, { Suspense } from "react";
    import { Switch, Link } from "react-router-dom";
    import { renderRoutes } from "react-router-config";

    import routes from "./app-configs/routesConfig";

    import "./styles.css";

    export default function App() {
    return (
    <div className="App">
    <ul>
    <li>
    <Link to="/">Home</Link>
    </li>
    <li>
    <Link to="/about">About</Link>
    </li>
    </ul>
    <Suspense fallback={<h1>loading....</h1>}>
    <Switch>{renderRoutes(routes)}</Switch>
    </Suspense>
    </div>
    );
    }
    创建 pages目录并创建文件和子目录,如下所示:
    src
    |---pages
    |---about
    | |---AboutPage.jsx
    | |---AboutPageConfig.jsx
    |
    |---home
    |---HomePage.jsx
    |---HomePageConfig.jsx
    |
    |---pagesConfig.js
    关于页面文件:
    // AboutPage.jsx

    import React from "react";
    import Button from "../../components/Button/Button";

    const AboutPage = props => {
    const btnClass = props.route.settings.layout.config.buttonClass;
    return (
    <>
    <h1>about page</h1>
    <Button className={btnClass}>about button</Button>
    </>
    );
    };

    export default AboutPage;
    ...
    // AboutPageConfig.jsx

    import React from "react";

    export const AboutPageConfig = {
    settings: {
    layout: {
    config: {
    buttonClass: "About_btn"
    }
    }
    },
    routes: [
    {
    path: "/about",
    exact: true,
    component: React.lazy(() => import("./AboutPage"))
    }
    ]
    };
    主页文件:
    // HomePage.jsx

    import React from "react";
    import Button from "../../components/Button/Button";

    const HomePage = props => {
    const btnClass = props.route.settings.layout.config.buttonClass;
    return (
    <>
    <h1>home page</h1>
    <Button className={btnClass}>home button</Button>
    </>
    );
    };

    export default HomePage;
    ...
    // HomePageConfig.jsx

    import React from "react";

    export const HomePageConfig = {
    settings: {
    layout: {
    config: {
    buttonClass: "Home_btn"
    }
    }
    },
    routes: [
    {
    path: "/",
    exact: true,
    component: React.lazy(() => import("./HomePage"))
    }
    ]
    };
    ...
    // pagesConfig.js

    import { HomePageConfig } from "./home/HomePageConfig";
    import { AboutPageConfig } from "./about/AboutPageConfig";

    export const pagesConfig = [HomePageConfig, AboutPageConfig];

    编辑部分:
    特设 也许这样: CodeSandBox
    创建 hoc目录和 withPage.jsx文件:
    src
    |---hoc
    |---withPage.jsx
    ...
    // withPage.jsx

    import React, { useEffect, useState } from "react";

    export function withPage(Component, path) {
    function loadComponentFromPath(path, setStyles) {
    import(path).then(component => setStyles(component.default));
    }
    return function(props) {
    const [styles, setStyles] = useState();

    useEffect(() => {
    loadComponentFromPath(`../pages/${path}`, setStyles);
    }, []);
    return <Component {...props} styles={styles} />;
    };
    }
    然后页面如下:
    src
    |---pages
    |---about
    | |---About.jsx
    | |---About.module.css
    |
    |---home
    |---Home.jsx
    |---Home.module.css
    About.jsx 文件:
    // About.jsx

    import React from "react";
    import { withPage } from "../../hoc/withPage";

    const About = props => {
    const {styles} = props;
    return (
    <button className={styles && styles.AboutBtn}>About</button>
    );
    };

    export default withPage(About, "about/About.module.css");
    About.module.css 文件:
    // About.module.css

    .AboutBtn {
    background: yellow;
    }
    Home.jsx 文件:
    // Home.jsx

    import React from "react";
    import { withPage } from "../../hoc/withPage";

    const Home = props => {
    const { styles } = props;
    return <button className={styles && styles.HomeBtn}>Home</button>;
    };

    export default withPage(Home, "home/Home.module.css");
    Home.module.css 文件:
    // Home.module.css

    .HomeBtn {
    background: red;
    }

    关于javascript - 如何自定义延迟加载页面之间共享的 React 组件的样式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62757143/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com