Oma*_*llo 6 javascript sass reactjs create-react-app
我正在构建一个 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>
...
Run Code Online (Sandbox Code Playgroud)
每个页面都使用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;
Run Code Online (Sandbox Code Playgroud)
该Button.module.scss文件只是将按钮的背景颜色设置为red。
.btn {
background: red;
}
Run Code Online (Sandbox Code Playgroud)
该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];
Run Code Online (Sandbox Code Playgroud)
其中[PageName]是页面名称,[ExpectedColor]是基于上述项目符号列表(蓝色或黄色)的相应预期颜色。
导入的 SCSS 模块导出一个类.pageBtn,该类将background属性设置为所需的颜色。
注意:我可以在Button组件上使用定义要显示的变体(蓝色/黄色)的道具,并基于该道具添加在 SCSS 文件中定义的类。我不想这样做,因为更改可能不属于变体(例如margin-top)。
如果我使用 运行该应用程序yarn start,该应用程序工作正常。但是,如果我构建应用程序 ( yarn build) 然后我开始为应用程序提供服务(例如使用serve -s build),则行为会有所不同并且应用程序不会按预期工作。
当主页加载页面时,该按钮被正确地以蓝色背景显示。检查加载的 CSS 块,它包含:
.Button_btn__2cUFR {
background: red
}
.Home_pageBtn__nnyWK {
background: blue
}
Run Code Online (Sandbox Code Playgroud)
没关系。然后我点击导航链接打开关于页面。即使在这种情况下,该按钮也会以黄色背景正确显示。检查加载的 CSS 块,它包含:
.Button_btn__2cUFR {
background: red
}
.About_pageBtn__3jjV7 {
background: yellow
}
Run Code Online (Sandbox Code Playgroud)
当我返回主页时,该按钮现在显示为红色背景而不是黄色。那是因为关于页面已经加载了上面的 CSS,它再次定义了Button_btn__2cUFR类。由于类现在是以后的Home_pageBtn__nnyWK类定义,该按钮显示为红色。
注:该Button组件没有出口的共同块,因为它的尺寸太小。将它放在一个公共块中可以解决问题。但是,我的问题是关于小型共享组件。
我想过 2 个解决方案,但是,我不太喜欢:
中指定的类[PageName].module.scss可以定义为:
.pageBtn.pageBtn {
background: [color];
}
Run Code Online (Sandbox Code Playgroud)
这将增加选择器的特异性并将覆盖默认Button_btn__2cUFR类。但是,如果组件非常小(小于30kb),则每个页面块都将包含共享组件。此外,组件的使用者必须知道这个技巧。
弹出应用程序(或使用类似react-app- rewired 的东西)将允许使用 webpack 指定公共块的最小大小。但是,这并不是我想要的所有组件。
总而言之,问题是:在使用延迟加载路由时覆盖共享组件样式的正确工作方式是什么?
您可以在任何页面的配置文件中使用以下逻辑。此外,您可以从远程服务器(req/res API)发送配置数据并使用 redux 进行处理。
见演示:CodeSandBox
创建components目录并创建如下文件:
src
|---components
|---Button
| |---Button.jsx
| |---Button.module.css
Run Code Online (Sandbox Code Playgroud)
按钮组件:
// 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;
Run Code Online (Sandbox Code Playgroud)
...
// Button.module.css
.Home_btn {
background: red;
}
.About_btn {
background: blue;
}
Run Code Online (Sandbox Code Playgroud)
创建utils目录并创建AppUtils.js文件:
该文件处理页面的配置文件并返回新对象
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;
Run Code Online (Sandbox Code Playgroud)
创建app-configs目录并创建routesConfig.jsx文件:
该文件列出并组织路线。
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;
Run Code Online (Sandbox Code Playgroud)
修改index.js和App.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
);
Run Code Online (Sandbox Code Playgroud)
...
react-router-config:React Router 的静态路由配置助手。
// 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>
);
}
Run Code Online (Sandbox Code Playgroud)
创建pages目录并创建文件和子目录,如下所示:
src
|---pages
|---about
| |---AboutPage.jsx
| |---AboutPageConfig.jsx
|
|---home
|---HomePage.jsx
|---HomePageConfig.jsx
|
|---pagesConfig.js
Run Code Online (Sandbox Code Playgroud)
关于页面文件:
// 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;
Run Code Online (Sandbox Code Playgroud)
...
// 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"))
}
]
};
Run Code Online (Sandbox Code Playgroud)
主页文件:
// 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;
Run Code Online (Sandbox Code Playgroud)
...
// HomePageConfig.jsx
import React from "react";
export const HomePageConfig = {
settings: {
layout: {
config: {
buttonClass: "Home_btn"
}
}
},
routes: [
{
path: "/",
exact: true,
component: React.lazy(() => import("./HomePage"))
}
]
};
Run Code Online (Sandbox Code Playgroud)
...
// pagesConfig.js
import { HomePageConfig } from "./home/HomePageConfig";
import { AboutPageConfig } from "./about/AboutPageConfig";
export const pagesConfig = [HomePageConfig, AboutPageConfig];
Run Code Online (Sandbox Code Playgroud)
编辑部分: 使用HOC也许这样:CodeSandBox
创建hoc目录和withPage.jsx文件:
src
|---hoc
|---withPage.jsx
Run Code Online (Sandbox Code Playgroud)
...
// 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} />;
};
}
Run Code Online (Sandbox Code Playgroud)
然后页面如下:
src
|---pages
|---about
| |---About.jsx
| |---About.module.css
|
|---home
|---Home.jsx
|---Home.module.css
Run Code Online (Sandbox Code Playgroud)
关于.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");
Run Code Online (Sandbox Code Playgroud)
关于.module.css 文件:
// About.module.css
.AboutBtn {
background: yellow;
}
Run Code Online (Sandbox Code Playgroud)
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");
Run Code Online (Sandbox Code Playgroud)
Home.module.css 文件:
// Home.module.css
.HomeBtn {
background: red;
}
Run Code Online (Sandbox Code Playgroud)
我建议不要同时添加默认样式和消费者样式,而是使用消费者的样式而不是您的样式,并在未提供时使用您的回调。消费者仍然可以使用composes关键字编写默认值。
按钮.js
import React from 'react';
import styles from './Button.module.scss';
const Button = ({ children, className}) => (
<button className={className ?? styles.btn}>{children}</button>
);
export default Button;
Run Code Online (Sandbox Code Playgroud)
SomePage.module.scss
.pageBtn {
// First some defaults
composes: btn from './Button.module.scss';
// And override some of the defautls here
background: yellow;
}
Run Code Online (Sandbox Code Playgroud)
如果您愿意,可以使用 sass @extends 或 @mixin 代替
编辑:还没有测试过,但是是否仅通过使用composeswebpack 就可以确保仅捆绑默认值一次?因此,您不再需要使用以下命令更改 Button.js 代码??
| 归档时间: |
|
| 查看次数: |
877 次 |
| 最近记录: |