In the previous article, we use the TypeScript ESLint/Webpack built a React applications. In this article, we continue to add CSS/Less/Sass/Antd based on the previous article
Introduce CSS into your code
Create a SRC/pages/CssDemo/index. The TSX composite components and add a CSS class name
import React from "react";
const Index: React.FC = () = > {
return (
<div className="container">
<div>CSSDemo</div>
</div>
);
};
export default Index;
Copy the code
Then we create an index. CSS file in the component’s sibling directory and add the following
.container > div {
font-size: 20px;
color: blue;
width: 200px;
height: 200px;
border: 1px solid gray;
}
Copy the code
Run the NPM start command to start the application
Of course! The style is definitely not going to work. The component has no idea where the container is defined. so! We need to import the style file module into the component
import "./index.css";
Copy the code
Webpack displays an error :”Module parse failed: Unexpected token”. The reason for the ❎ error is that we are using a file module that Webpack cannot parse
Configure the CSS for the development environment
We need Webpack to know how to parse CSS files, so we need two parsers in webpack.dev.js
const config= {
...
module: {
rules: [..., {test: /\.css$/i,
use: ["style-loader"."css-loader"],},],}... };Copy the code
After this configuration, when Webpack encounters a.css file again, it will use a CSS-Loader and style-loader for processing (loaders in the use array execute from back to front).
css-loader
In the import statement (in our exampleapp.css
) and parse it into JavaScript code.style-loader
Insert CSS from your JavaScript code into your HTML file in the form of a style tag.
For the new Webpack configuration to work, we need to install csS-Loader and style-Loader:
yarn add css-loader style-loader --dev
Copy the code
Next, restart the app and observe the interface. Sure! Style works!
Configure the CSS for the production environment
In a production environment, the CSS styles need to be separated into separate files (to avoid browser caching). We can do this by using the mini-css-extract-plugin instead of style-loader.
The official WebPack documentation says:
Starting with WebPack V4, extract-text-webpack-plugin should be replaced with mini-css-extract-plugin
Next, we install the Mini-CSS-extract-plugin and its TypeScript type library
yarn add mini-css-extract-plugin @types/mini-css-extract-plugin --dev
Copy the code
Then we need to add this Loader to the production environment configuration file and modify the following in webpack.prod.js
.const MiniCssExtractPlugin = require('mini-css-extract-plugin')
constconfig= { ... .module: {
rules: [..., {test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"],},],}... .plugins: [...new MiniCssExtractPlugin({
filename: "[name].[contenthash].css",}),],... };Copy the code
The mini-css-extract-plugin pulls the CSS styles out of the JS code and creates a.css file
If our application uses Code Split
- You can use the [name] flag to get Webpack to name the new CSS file.
- Also avoid browser caching by using the [Contenthash] flag to change the name of the new file that has been packaged when the content changes.
Here, let’s pack a bag and have a look
yarn run build
Copy the code
You can see that the CSS file has been generated in the Build directory
Then we looked at the HTML file and saw that the CSS file was also automatically introduced
Add styles to multiple components
Next we add two sub-components to the cssDemo/index.tsx component and import two style files
import "./heading.css";
import "./content.css";
const App = () = > (
<>
<Heading />
<Content />
</>
);
const Heading = () = > <h1 className="heading">My React and TypeScript App</h1>;
const Content = () = > <div className="content">With CSS!</div>;
Copy the code
The contents of the two CSS files are shown below
/* heading.css */
.heading {
color: red;
}
/* content.css */
.content {
color: green;
}
Copy the code
Then we start the application with Yanr Start and look at the interface, and we see that the style file has been transformed into two style tags introduced into the HTML file
Next, run yarn Run build to package the project and look at the packaged files, we see that the CSS generates separate files
Isn’t that interesting! 🙂
Using CSS modules
At this point, experienced students should see a problem: developers must carefully name CSS classes to avoid style name conflicts. For example, if we call both CSS classes “text,” what are the colors for the title and content?
The second CSS class takes precedence over the first, resulting in both paragraphs of text being green.
How to solve it?
CSS Modules solves the problem of style class name conflicts by qualifying CSS names to specific components
CSS Modules:
import heading from "./heading.module.css";
import content from "./content.module.css"; .const Heading = () = > (
<h1 className={heading.heading}>My React and TypeScript App</h1>
);
const Content = () = > <div className={content.content}>With CSS!</div>;
Copy the code
The import statement is slightly different from the original. We import a variable from a file with the suffix.module.css
This variable is an object that contains all the CSS class names of the corresponding style file, and then references the corresponding class name variable in the component
We also need to rename the content.css to content.module. CSS and the heading. CSS to calling.module.css.
But! Error in TypeScript compilation ❎ “cannot find module ‘.module. CSS ‘or corresponding type declaration” error because TS cannot parse CSS modules
To resolve this error, we need to create a SRC /typings.d.ts type declaration file and add the following
declare module "*.module.css";
Copy the code
Then restart the application and check the interface again. You can see that the style is displayed as expected
We see that CSS class names are given seemingly random names. This ensures that style names in different components do not conflict.
If we remember how we refer to class names in components, it looks a little strange:
className={fileName.cssClassName}
Copy the code
Why does the imported style class name in the code differ from the generated class name? Because the above code is actually telling Webpack to give the class name a globally unique name.
Next, when we look at the style files after the NPM Run build, we see that the production style file class names are also globally unique
Optimized CSS Modules reference mode
Every time you need to implement CSS modules through *.module. CSS is not easy. In fact, we can simplify the writing of CSS Modules by modifying the Webpack configuration
Modify webpack.dev.js configuration, CSS Modules official document
. {test: /\.css$/i,
use: ["style-loader", {
loader: "css-loader".options: {
modules: true},},],}...Copy the code
Modify the configuration of webpack.pro.js
. {test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, {
loader: "css-loader".options: {
modules: true},},],}...Copy the code
Example Modify the configuration of typings.d.ts
declare module "*.css";
Copy the code
We also need to rename content.module. CSS to content.css and calling.module. CSS to calling.css.
Change the name of the style imported into the App component
import contentStyle from "./content.css";
import headingStyle from "./heading.css";
Copy the code
Finally, restart the application to check the interface effect, you can find that the effect is the same as before
It’s a little more concise, right? 😯
Configure Less in the project
In real life, we usually need some CSS precompiler to improve our efficiency. First, how to configure Less WebPack reference documentation
Modify the webpack.dev.js configuration
{
test: /\.less$/i,
use: [
{
loader: "style-loader"}, {loader: "css-loader".options: {
modules: true,}}, {loader: "less-loader",}]},Copy the code
Modify the webpack.pro.js configuration
{
test: /\.less$/i,
use: [MiniCssExtractPlugin.loader, {
loader: "css-loader".options: {
modules: true.importLoaders: 1,}}, {loader: "less-loader",}]},Copy the code
For the new Webpack configuration to work properly, we need to install less-Loader and less:
yarn add less less-loader --dev
Copy the code
Note: You also need to include the following in typings.d.ts, otherwise Typescript will not recognize Less types
declare module "*.less";
Copy the code
Add Less for a component
Next we import the less file into the SRC /lessDemo/index. TSX component
import React from "react";
import style from "./index.less";
const Index: React.FC = () = > {
return (
<div className={style.container}>
<div>LessDemo</div>
</div>
);
};
export default Index;
Copy the code
The content of the Less file is as follows
@color: purple;
.container {
div {
font-size: 20px;
color: @color;
width: 200px;
height: 200px;
border: 1pxsolid gray; }}Copy the code
Then we start the application with Yanr Start and look at the interface, and we see that the style file has been transformed into two style tags introduced into the HTML file
Next, we run yarn Run build to package the project and look at the packaged files. We see that Less and the rest of the CSS generate separate files
Isn’t that interesting! 🙂
Configure Sass in your project
Let’s take a look at how to configure Sass, which is similar to Less. [WebPack Reference][14]
Modify the webpack.dev.js configuration
{
test: /\.s[ac]ss$/i,
use: [
// Create a 'style' node from the JS code containing the CSS
{
loader: "style-loader",},// Convert the CSS to CommonJS code
{
loader: "css-loader".options: {
modules: true,}},// Convert Sass to CSS
{
loader: "sass-loader",}]},Copy the code
Modify webpack.pro.js as follows
{
test: /\.s[ac]ss$/i,
use: [MiniCssExtractPlugin.loader, {
loader: "css-loader".options: {
modules: true.importLoaders: 1,}}, {loader: "sass-loader",}]},Copy the code
For the new Webpack configuration to work properly, we need to install Sass-Loader and Sass:
yarn add sass sass-loader --dev
Copy the code
Note: You also need to include the following in typings.d.ts, otherwise Typescript will not recognize the sass/ SCSS types
declare module "*.sass";
declare module "*.scss";
Copy the code
Add Sass to the component
Next we put in the Sass style file for the SRC /sassDemo/index.tsx component
import React from "react";
import style from "./index.scss";
const Index: React.FC = () = > {
return (
<div className={style.container}>
<div>SASSDemo</div>
</div>
);
};
export default Index;
Copy the code
The Ssss file content is as follows
$color: red;
.container {
div {
font-size: 20px;
color: $color;
width: 200px;
height: 200px;
border: 1pxsolid gray; }}Copy the code
Then start the application through yanr Start to see the interface effect, as expected…
Add the UI library -Antd to the project
In real world development, we usually use UI libraries to improve the efficiency of our interface development. Antd is the most popular React UI library in China. Now let’s start the configuration
First install the Antd dependency
yarn add antd
Copy the code
Modify the sr/pages/antdDemo/index. The TSX components – the introduction of a Button
import React from "react";
import { Button } from "antd";
const Index: React.FC = () = > {
return (
<div>
<Button type="primary">Antd button</Button>
</div>
);
};
export default Index;
Copy the code
After starting the project by using Yarn Start and observing the interface effect, we find that the button is referenced successfully, but the correct style is not displayed
Next, let’s style the Antd
import React from "react";
import { Button } from "antd";
import "./index.less";
const Index: React.FC = () = > {
return (
<div>
<Button type="primary">Antd button</Button>
</div>
);
};
export default Index;
Copy the code
The contents of index.less are as follows
@import "~antd/dist/antd.less";
Copy the code
As you can see here, the style of Antd is based on Less
“Module build failed”
We look for a solution according to the issue address in the error report. There is a solution that says Less plus javascriptEnabled is enough
So let’s add it up
{
test: /\.less$/i,
use: ["style-loader", {
loader: "css-loader".options: {
modules: true,}}, {loader: "less-loader".options: {
lessOptions: {
javascriptEnabled: true},},},],},Copy the code
After setting, restart the project and observe the interface effect, but we found that Antd still had no style, so we continued to look for the issue
The real reason:
Because our WebPack project is configured with CSS-modules, it modularizes and hashes Ant’s styles, resulting in a style mismatch.
Therefore, it is recommended to exclude the node_modules directory file from the CSS Modules configuration
In other words, let antD less not through csS-module-loader, only the project’s own less files through csS-module-loader
The correct Webpack configuration is as follows
const config = {
........
module: {
rules{: [...test: /\.less$/i,
use: ["style-loader", {
loader: "css-loader".options: {
modules: true,}}, {loader: "less-loader"],},exclude: path.resolve(__dirname, ".. /src/app.less"),}, {test: /\.less$/i,
use: ["style-loader", {
loader: "css-loader"}, {loader: "less-loader".options: {
lessOptions: {
javascriptEnabled: true}},},].include: path.resolve(__dirname, ".. /src/app.less"),},]},........Copy the code
Modify the content of SRC /app.less
@import "~antd/dist/antd.less";
/* Global styles, no CSS modules: 1. Override Antd styles 2. Define global common style */
.myText {
color: red;
}
Copy the code
Example Modify the contents of SRC /index. TSX
import React from "react";
import ReactDOM from "react-dom";
import './app.less'
import AntdDemo from "./pages/antdDemo"; .const App = () = > (
<>.<AntdDemo />.</>
);
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>.document.getElementById("root"));Copy the code
Restart the project and see that the styles are displayed properly, 😆
Is this the end? 🙅♂️ No, it’s a little far from building a full React app. So in the next article, I’ll show you how to integrate image/font resources into your project