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-loaderIn the import statement (in our exampleapp.css) and parse it into JavaScript code.
  • style-loaderInsert 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