在上一篇文章中,我们使用 TypeScript/ESLint/Webpack 搭建了一个 React 应用。这篇文章中,我们继续在上一篇文章的基础上加入 CSS/Less/Sass/Antd
在代码中引入 CSS
创建一个src/pages/CssDemo/index.tsx
组件并添加一个 CSS 类名
import React from "react";
const Index: React.FC = () => {
return (
<div className="container">
<div>CSSDemo</div>
</div>
);
};
export default Index;
然后我们在组件的同级目录创建一个 index.css
文件并加入以下内容
.container > div {
font-size: 20px;
color: blue;
width: 200px;
height: 200px;
border: 1px solid gray;
}
通过npm start
启动应用
当然!样式肯定是不会生效的。因为组件根本不知道container
是在哪里被定义的。 so!我们需要在组件中导入样式文件模块
import "./index.css";
Webpack 会提示错误:"Module parse failed: Unexpected token".错误 ❎ 的原因是我们使用了一个 Webpack 无法解析的文件模块
为开发环境配置 CSS
我们需要让 Webpack 知道如何解析 CSS 文件,因此我们需要在webpack.dev.js
中加入两个解析器
const config= {
...
module: {
rules: [
...,
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
],
},
...
};
这样配置后,当 Webpack 再遇到.css
文件时,它将使用css-loader
和style-loader
进行处理(use 数组中的加载器从后向前执行)。
css-loader
在 import 语句(在我们的示例中为app.css
)中读取引用的 CSS 文件并解析成 JavaScript 代码。style-loader
将 JavaScript 代码中的 CSS 以 style 标签的形式插入到 html 文件中。
为了使新的 Webpack 配置正常工作,我们需要安装css-loader
和style-loader
:
yarn add css-loader style-loader --dev
接下来重新启动应用并观察界面,当然!样式生效了!
为生产环境配置 CSS
在生产环境中,需要把 CSS 样式抽离成单独的文件(避免浏览器缓存). 我们可以使用mini-css-extract-plugin
代替style-loader
来做到这一点。
webpack 官方文档有说:
接下来,我们安装mini-css-extract-plugin
及其 TypeScript 类型库
yarn add mini-css-extract-plugin @types/mini-css-extract-plugin --dev
然后,我们需要把此 loader 加入到生产环境配置文件中,在webpack.prod.js
中修改如下内容
...
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const config= {
...,
module: {
rules: [
...,
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
...,
plugins: [
...,
new MiniCssExtractPlugin({
filename: "[name].[contenthash].css",
}),
],
...
};
mini-css-extract-plugin
会将 CSS 样式从 JS 代码中抽离出来并创建.css 文件
如果我们的应用使用了 code split
- 那么可以使用[name]标志来让 Webpack 命名新的 CSS 文件。
- 当内容更改时,使用[contenthash]标志更改已打包的新的文件名称,这也可以避免浏览器缓存。
来,我们打一个包看看
yarn run build
我们可以看到在 build
目录下已经生成了 CSS 文件
然后我们查看 HTML 文件,发现 CSS 文件也被自动引入了
为多个组件添加样式
接下来我们在cssDemo/index.tsx
组件中添加两个子组件,并且导入两个样式文件
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>;
两个 CSS 文件内容如下所示
/* heading.css */
.heading {
color: red;
}
/* content.css */
.content {
color: green;
}
然后通过yanr start
启动应用查看界面效果,我们发现样式文件被转换了,变成了两个 style 标签引入到了 HTML 文件中
接下来执行yarn run build
打包项目,并查看打包后的文件,我们看到 CSS 生成了单独的文件
是不是很有趣!?
使用 CSS modules
到这里,有经验的同学应该会发现一个问题: 开发人员必须仔细命名 CSS 类,才能避免样式名称冲突。 例如,如果我们将两个 CSS 类都称为“text”,那么标题和内容的颜色是什么呢?
第二个 CSS 类的优先级高于第一个 CSS 类,导致两段文本均为绿色。
如何解决呢?
CSS modules
可以通过将 CSS 名称限定到特定组件中来解决样式类名冲突问题
接下来我们CV
如下代码实现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>;
import 语句跟原来的略有不同。 我们通过引用.module.css
后缀的文件,并从中导入为一个变量
这个变量是一个对象,包含了对应样式文件的所有 CSS 类名称, 然后在组件中引用对应的类名变量
我们还需要将content.css
重命名为content.module.css
,将heading.css
重命名为heading.module.css
。
但是!TypeScript 编译出现错误 ❎“无法找到模块'.module.css'或对应的类型声明”错误,因为 TS 无法解析CSS modules
为了解决这个错误,我们需要创建一个src/typings.d.ts
类型声明文件并加入以下内容
declare module "*.module.css";
之后重启应用,再次查看界面效果,可以发现样式按预期的正常显示了
我们看到 CSS 类名称被赋予了一个看起来很随机的名称。 因为这样可以确保不同组件中的样式名称不会冲突。
如果我们还记得在组件中引用类名称的方式,那看起就有些奇怪了:
className={fileName.cssClassName}
为什么代码中导入的样式类名与生成后的类名不一样?因为上面的代码其实在通知 Webpack 为类名称赋予全局唯一的名称。
接下来,我们再看看npm run build
后的样式文件,我们发现生产环境的样式文件类名也是全局唯一的
CSS modules 的引用方式优化
每次都需要通过 *.module.css
的方式实现 CSS modules 不免有些麻烦。其实,我们可以通过修改 Webpack 配置简化 CSS modules 的写法
修改webpack.dev.js
的配置, CSS modules 官方文档
...
{
test: /\.css$/i,
use: ["style-loader", {
loader: "css-loader",
options: {
modules: true,
},
},
],
},
...
修改webpack.pro.js
的配置
...
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, {
loader: "css-loader",
options: {
modules: true,
},
},],
},
...
修改typings.d.ts
的配置
declare module "*.css";
我们还需要将content.module.css
重命名为content.css
,将heading.module.css
重命名为heading.css
。
修改 App 组件中导入的样式名称
import contentStyle from "./content.css";
import headingStyle from "./heading.css";
最后重新启动应用查看界面效果,可以发现与之前的效果一致
写法更简洁了是吧??
在项目中配置 Less
真实工作中,我们一般需要一些 CSS 预编译器以提高我们的工作效率,先说如何配置 Less webpack 参考文档
修改webpack.dev.js
配置
{
test: /\.less$/i,
use: [
{
loader: "style-loader",
},
{
loader: "css-loader",
options: {
modules: true,
},
},
{
loader: "less-loader",
},
],
},
修改webpack.pro.js
配置
{
test: /\.less$/i,
use: [MiniCssExtractPlugin.loader, {
loader: "css-loader",
options: {
modules: true,
importLoaders: 1,
},
},
{
loader: "less-loader",
},
],
},
为了使新的 Webpack 配置正常工作,我们需要安装less-loader
和less
:
yarn add less less-loader --dev
注意:还需要在typings.d.ts
中加入以下内容,否则 Typescript 无法识别 Less 类型
declare module "*.less";
为组件添加 Less
接下来我们在 src/lessDemo/index.tsx
组件中导入 less 文件
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;
Less 文件内容如下所示
@color: purple;
.container {
div {
font-size: 20px;
color: @color;
width: 200px;
height: 200px;
border: 1px solid gray;
}
}
然后通过yanr start
启动应用查看界面效果,我们发现样式文件被转换了,变成了两个 style 标签引入到了 HTML 文件中
接下来执行yarn run build
打包项目,并查看打包后的文件,我们看到 Less 和其他的 CSS 一起 生成了单独的文件
是不是很有趣!?
在项目中配置 Sass
接下来我们看看如何配置 Sass,其实与 Less 的设置方式是类似的 . [webpack 参考文档][14]
修改webpack.dev.js
配置
{
test: /\.s[ac]ss$/i,
use: [
//从包含CSS的JS代码中 创建 `style` 节点
{
loader: "style-loader",
},
// 将 CSS 转换为 CommonJS 格式的JS代码
{
loader: "css-loader",
options: {
modules: true,
},
},
// 将 Sass 转换为 CSS
{
loader: "sass-loader",
},
],
},
修改webpack.pro.js
为如下配置
{
test: /\.s[ac]ss$/i,
use: [MiniCssExtractPlugin.loader, {
loader: "css-loader",
options: {
modules: true,
importLoaders: 1,
},
},
{
loader: "sass-loader",
},
],
},
为了使新的 Webpack 配置正常工作,我们需要安装sass-loader
和sass
:
yarn add sass sass-loader --dev
注意:还需要在typings.d.ts
中加入以下内容,否则 Typescript 无法识别 sass/scss 类型
declare module "*.sass";
declare module "*.scss";
为组件添加 Sass
接下来我们为 src/sassDemo/index.tsx
组件入 Sass 样式文件
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;
Ssss 文件内容如下所示
$color: red;
.container {
div {
font-size: 20px;
color: $color;
width: 200px;
height: 200px;
border: 1px solid gray;
}
}
然后通过yanr start
启动应用查看界面效果,跟预期一样...
为项目添加 UI 库-Antd
在真实开发中,我们一般使用 UI 库来提高我们的界面开发效率。国内最火的 React UI 库非 Antd
莫属。接下来我们开始配置吧
首先安装 Antd
依赖
yarn add antd
修改 sr/pages/antdDemo/index.tsx
组件-引入一个 Button 组件
import React from "react";
import { Button } from "antd";
const Index: React.FC = () => {
return (
<div>
<Button type="primary">Antd 按钮</Button>
</div>
);
};
export default Index;
通过yarn start
启动项目并观察界面效果,我们发现按钮被成功引用了,但是却没有显示正确的样式
接下我们给 Antd 加上样式
import React from "react";
import { Button } from "antd";
import "./index.less";
const Index: React.FC = () => {
return (
<div>
<Button type="primary">Antd 按钮</Button>
</div>
);
};
export default Index;
index.less
的内容如下
@import "~antd/dist/antd.less";
这里可以发现,Antd 的样式是基于 Less 实现的
保存代码后却发现 Webpack 编译报错"Module build failed"
我们根据报错中的 issue 地址去找解决方案,有方案说Less加上javascriptEnabled
即可
来我们加一下看看
{
test: /\.less$/i,
use: ["style-loader", {
loader: "css-loader",
options: {
modules: true,
},
},
{
loader: "less-loader",
options: {
lessOptions: {
javascriptEnabled: true
},
},
},
],
},
设置完成后重启项目并观察界面效果,但是我们发现 Antd 还是没有样式,那么我们继续查找 issue
真实原因:
正确的 Webpack 配置如下
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"),
},
]
},
........
修改src/app.less
的内容
@import "~antd/dist/antd.less";
/* 全局样式,没有CSS modules
作用:
1. 覆盖Antd样式
2. 定义全局公共样式
*/
.myText {
color: red;
}
修改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")
);
重新启动项目,发现样式正常显示了,?
难道这就结束了吗??♂️ 不,离搭建一个完整的 React 应用有些距离。那么在下一篇文章中,我会告诉你如何在项目中集成图片/字体资源
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!