引入 less、sass(scss)、stylus
less
、
sass
(
scss
)
、
stylus
是三个比较流行的
CSS Modules
预处理库。在
React
中,使用
CSS
Modules
的好处在于:
1.
避免全局样式冲突:使用
CSS Modules
可以确保样式只应用于特定组件,避免全局样式冲突。
2.
更好的可维护性:
CSS Modules
使得样式与组件代码紧密关联,方便代码维护。
3.
提高代码可重用性:
CSS Modules
可以轻松地将样式从一个组件复制到另一个组件,提高代码可
重用性。
4.
支持动态样式:使用
CSS Modules
可以轻松地生成动态样式,例如根据组件状态或属性更改样
式。
5.
更好的性能:
CSS Modules
使用模块化的方式加载样式,提高了页面加载速度和性能。
基本用法
先安装相关的依赖
pnpm i less less - loader sass - loader sass stylus stylus - loader - D
在 webpack.base.ts 添加相关的 loader :
const cssRegex = /\.css$/;
const sassRegex = /\.(scss|sass)$/;
const lessRegex = /\.less$/;
const stylRegex = /\.styl$/;
const styleLoadersArray = [
"style-loader",
{
loader: "css-loader",
options: {
modules: {
localIdentName: "[path][name]__[local]--[hash:5]",
},
},
},
];
const baseConfig: Configuration = {
// ...
module: {
rules: [
// ...
{
test: cssRegex, // 匹配css文件
use: styleLoadersArray,
},
{
test: lessRegex,
use: [
...styleLoadersArray,
{
loader: "less-loader",
options: {
lessOptions: {
// 如果要在less中写js的语法,需要加这一配置
javascriptEnabled: true,
},
},
},
],
},
{
test: sassRegex,
use: [
...styleLoadersArray,
{
loader: "sass-loader",
options: {
implementation: require("sass"), // 使用dart-sass代替node-sass
},
},
],
},
{
test: stylRegex,
use: [...styleLoadersArray, "stylus-loader"],
},
],
},
// ...
};
export default baseConfig;
然后就可以在业务中使用了:
@color: red;
.lessBox {
.box {
color: @color;
background-color: lightblue;
transform: translateX(100);
&:before {
@arr: "hello", "world";
content: ` @{arr}.join(" ") .toUpperCase() `;
}
}
}
$blue: #1875e7;
$side: left;
.scssBox {
margin: 20px 0;
.box {
color: $blue;
background-color: rgb(226, 223, 223);
border: 1px solid grey;
margin-#{$side}: 20px;
padding: (20px/2);
}
}
/* src/app.styl */
.stylBox
.box
color: red;
background-color: yellow;
在
App.tsx
中引入:
import '@/App.css'
import lessStyles from './app.less'
import scssStyles from './app.scss'
import stylStyles from './app.styl'
function App() {
return <div>
<h2>webpack5-react-ts</h2>
<div className={lessStyles['lessBox']}>
<div className={lessStyles['box']}>lessBox</div>
</div>
<div className={scssStyles['scssBox']}>
<div className={scssStyles['box']}>scssBox</div>
</div>
<div className={stylStyles['stylBox']}>
<div className={stylStyles['box']}>stylBox</div>
</div>
</div>
}
export default App
重启项目,就会发现生成了带有
hash
值的
class
类名,且里面包含了我们自定义的类名,方便日后调试 用。
同时在验证打包是否成功,运行
pnpm run build:dev
,然后通过
serve -S dist
查看。 当然,如果
你不希望每次写
less
的时候,都在文件名上加一个
.module
,可以在
less-loader
中添加如下配置:
const baseConfig: Configuration = {
module: {
rules: [
// ...
{
test: lessRegex,
use: [
...styleLoadersArray,
{
loader: "less-loader",
options: {
lessOptions: {
importLoaders: 2,
// 可以加入modules: true,这样就不需要在less文件名加module了
modules: true,
// 如果要在less中写类型js的语法,需要加这一个配置
javascriptEnabled: true
},
},
},
],
},
// ...
],
},
// ...
};
export default baseConfig;
至此,我们就完成了
less
、
sass
(
scss
)
,
stylus
的引入
declare module '*.css' {
const classes: { [key: string]: string };
export default classes;
}
declare module '*.scss' {
const classes: { [key: string]: string };
export default classes;
}
declare module '*.sass' {
const classes: { [key: string]: string };
export default classes;
}
declare module '*.less' {
const classes: { [key: string]: string };
export default classes;
}
declare module '*.styl' {
const classes: { [key: string]: string };
export default classes;
}
然后就可以删掉样式文件中的
.module
后缀了
处理CSS3前缀在浏览器中的兼容
虽然
css3
现在浏览器支持率已经很高了
,
但有时候需要兼容一些低版本浏览器,需要给
css3
加前缀
,
可 以借助插件来自动加前缀,
postcss-loader
就是来给
css3
加浏览器前缀的,安装依赖:
pnpm i postcss - loader autoprefixer - D
为了避免
webpack.base.ts
文件过于庞大,我们将一些
loader
配置提取成单独的文件来进行管理,根 目录新建 postcss.config.js
,作为
postcss-loader
的配置文件,会自动读取配置:
module.exports = {
ident: "postcss",
plugins: [require("autoprefixer")],
};
修改
webpack.base.ts
,在解析
css
和
less
的规则中添加配置:
const styleLoadersArray = [
"style-loader",
{
loader: "css-loader",
options: {
modules: {
localIdentName: "[path][name]__[local]--[hash:5]",
},
},
},
// 添加 postcss-loader
'postcss-loader'
];
配置完成后,需要有一份要兼容浏览器的清单,让
postcss-loader
知道要加哪些浏览器的前缀,在根 目录创建 .browserslistrc
文件:
IE 9 # 兼容 IE 9chrome 35 # 兼容 chrome 35
以兼容到
ie9
和
chrome35
版本为例,配置好后,在
app.module.less
中加入一些
CSS3
的语法,重新启动项目,就可以在浏览器的控制台-Elements
中看到配置成功了。
执行
pnpm run build:dev
打包,也可以看到打包后的
css
文件已经加上了
ie
和谷歌内核的前缀。
处理图片
对于图片文件,
webpack4
使用
file-loader
和
url-loader
来处理的,但
webpack5
不使用这两个
loader
了
,
而是采用自带的
asset-module
来处理,修改
webpack.base.ts
,添加图片解析配置
{
output: {
// ... 这里自定义输出文件名的方式是,将某些资源发送到指定目录
assetModuleFilename: 'images/[hash][ext][query]'
},
module: {
rules: [
// ...
{
test: /\.(png|jpe?g|gif|svg)$/i, // 匹配图片文件
type: "asset", // type选择asset
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转base64
}
},
generator:{
filename:'static/images/[hash][ext][query]', // 文件输出目录和命名
},
},
]
}
}
资源模块类型
|
描述
|
asset/resource
|
发送一个单独的文件,并导出
URL
,替代
file-loader
,相当于
file-loader ,
将文件转化成
Webpack
能识别的资源,其他不做处理。
|
asset/inline
|
导出一个资源的
data URI
,以前使用
url-loader
实现。
|
asset/source
|
导出资源的源代码 ,以前是使用
raw-loader
实现。
|
资源模块类型
|
描述
|
asset
|
相当于自动选择
asset/resource
或
asset/inline
,替换
url-loader 中的 limit
,相当于
url-loader
将文件转化成
Webpack
能识别的资源, 同时小于某个大小的资源会处理成 data URI
形式。
|
将文件编译为
Data URI
使用,可以节省
HTTP
请求,是一个性能优化的点。但是将图片文件经
过
base64
编码转为
Data URI
,体积会增加大约
33%
。所以,我们一般只针对小图片做
Base64
的处理,对于一些比较大的文件来说,转为
Data URI
会明显增加打包后文件的体积,反而会加
大对带宽资源和流量的需求。
由于我们希望通过
ES6
的新语法
ESModule
的方式导入资源,为了使
TypeScript
可以识别图片模
块,需要在
src/typings/global.d.ts
中加入声明:
/* IMAGES */
declare module '*.svg' {
const ref: string;
export default ref;
}
declare module '*.bmp' {
const ref: string;
export default ref;
}
declare module '*.gif' {
const ref: string;
export default ref;
}
declare module '*.jpg' {
const ref: string;
export default ref;
}
declare module '*.jpeg' {
const ref: string;
export default ref;
}
declare module '*.png' {
const ref: string;
export default ref;
}
然后在
App.tsx
中引入图片资源:
import '@/App.css'
import lessStyles from '@/app.less'
import scssStyles from '@/app.scss'
import stylStyles from '@/app.styl'
import smallImg from '@/assets/images/5kb_img.jpeg'
import bigImg from '@/assets/images/10kb_img.png'
function App() {
return <div>
<h2>webpack5-react-ts</h2>
<div className={lessStyles['lessBox']}>
<div className={lessStyles['box']}>lessBox
<img src={smallImg} alt="小于10kb的图片" />
<img src={bigImg} alt="大于于10kb的图片" />
<div className={lessStyles['smallImg']}>小图片背景</div>
<div className={lessStyles['bigImg']}>大图片背景</div>
</div>
</div>
<div className={scssStyles['scssBox']}>
<div className={scssStyles['box']}>scssBox</div>
</div>
<div className={stylStyles['stylBox']}>
<div className={stylStyles['box']}>stylBox</div>
</div>
</div>
}
export default App
在
app.less
文件中加入背景图片:
@color: red;
.lessBox {
.box {
color: @color;
background-color: lightblue;
transform: translateX(100);
&:before{
@arr: 'hello', 'world';
content: `@{arr}.join(' ').toUpperCase()`;
}
}
.smallImg {
width: 69px;
height: 75px;
background: url('@/assets/imgs/5kb_img.jpeg') no-repeat;
}
.bigImg {
width: 232px;
height: 154px;
background: url('@/assets/imgs/10kb_img.png') no-repeat;
}
}
重启项目,可以看到图片被正确地展示出来了,图片被打包进了我们指定的
static/images
文件里面。
webpack构建速度优化
webpack 进度条
webpackbar
这是一款个人感觉是个十分美观优雅的进度条,很多成名框架都用过他。而且使用起来也 极其方便,也可以支持多个并发构建是个十分强大的进度插件。
pnpm i webpackbar -D
最常用的属性配置其实就是这些,注释里也写的很清楚了,我们在 webpack.base.ts 中引入:
import WebpackBar from 'webpackbar';
const baseConfig: Configuration = {
// ...
// plugins 的配置
plugins: [
// ...
new WebpackBar({
color: "#85d", // 默认green,进度条颜色支持HEX
basic: false, // 默认true,启用一个简单的日志报告器
profile:false, // 默认false,启用探查器。
})
],
};
export default baseConfig;
当然里面还有一个属性就是
reporters
还没有写上,可以在里面注册事件,也可以理解为各种钩子函 数。如下:
{ // 注册一个自定义数组
start(context) {
// 在(重新)编译开始时调用
const { start, progress, message, details, request, hasErrors } = context
},
change(context) {
// 在 watch 模式下文件更改时调用
},
update(context) {
// 在每次进度更新后调用
},
done(context) {
// 编译完成时调用
},
progress(context) {
// 构建进度更新时调用
},
allDone(context) {
// 当编译完成时调用
},
beforeAllDone(context) {
// 当编译完成前调用
},
afterAllDone(context) {
// 当编译完成后调用
},
}
构建耗时
当进行优化的时候,肯定要先知道时间都花费在哪些步骤上了,而
speed-measure-webpackplugin
插 件可以帮我们做到,安装依赖:
pnpm i speed-measure-webpack-plugin -D
使用的时候为了不影响到正常的开发
/
打包模式,我们选择新建一个配置文件,新增
webpack
构建分析配 置文件 build/webpack.analy.ts
import { Configuration } from 'webpack'
import prodConfig from './webpack.prod' // 引入打包配置
import { merge } from 'webpack-merge' // 引入合并webpack配置方法
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin'); // 引入webpack
打包速度分析插件
const smp = new SpeedMeasurePlugin(); // 实例化分析插件
// 使用smp.wrap方法,把生产环境配置传进去,由于后面可能会加分析配置,所以先留出合并空位
const analyConfig: Configuration = smp.wrap(merge(prodConfig, {
}))
export default analyConfig
修改
package.json
添加启动
webpack
打包分析脚本命令,在
scripts
新增:
{
// ...
"scripts": {
// ...
"build:analy": "cross-env NODE_ENV=production BASE_ENV=production webpack -c
build/webpack.analy.ts"
}
// ...
}
执行
pnpm build:analy 命令,
可以在图中看到各
plugin
和
loader
的耗时时间
,
现在因为项目内容比较少,所以耗时都比较少,在真正
的项目中可以通过这个来分析打包时间花费在什么地方,然后来针对性的优化。
开启多线程 loader
webpack
的
loader
默认在单线程执行,现代电脑一般都有多核
cpu
,可以借助多核
cpu
开启多线程 loader 解析,可以极大地提升
loader
解析的速度,
thread-loader
就是用来开启多进程解析
loader
的,安装依赖
pnpm i thread-loader -D
使用时
,
需将此
loader
放置在其他
loader
之前。放置在此
loader
之后的
loader
会在一个独立的
worker
池中运行。
修改
webpack.base.ts
module: {
rules: [
{
test: /\.(ts|tsx)$/, // 匹配ts和tsx文件
use: [
// 开启多进程打包。
// 进程启动大概为600ms,进程通信也有开销。
// 只有工作消耗时间比较长,才需要多进程打包
{
loader: 'thread-loader',
options: {
wokers: 4 // 进程数
}
},
'babel-loader']
},
]
}
由于
thread-loader
不支持抽离
css
插件
MiniCssExtractPlugin.loader
(
下面会讲
)
,所以这里只配
置了多进程解析
ts
。
webpack构建产物优化
bundle 体积分析工具
webpack-bundle-analyzer
是分析
webpack
打包后文件的插件,使用交互式可缩放树形图可视化
webpack
输出文件的大小。通过该插件可以对打包后的文件进行观察和分析,可以方便我们对不完美的 地方针对性的优化,安装依赖:
pnpm i webpack-bundle-analyzer -D
修改 webpack.analy.ts :
import { Configuration } from "webpack";
import { merge } from "webpack-merge";
import prodConfig from "./webpack.prod";
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
// 引入webpack打包速度分析插件
const smp = new SpeedMeasurePlugin();
// 使用smp.wrap方法,把生产环境配置传进去,由于后面可能会加分析配置,所以先留出合并空位
const analyConfig: Configuration = smp.wrap(merge(prodConfig, {
plugins: [
new BundleAnalyzerPlugin() // 配置分析打包结果插件
]
}))
export default analyConfig;
配置好后,执行 pnpm run build:analy 命令,打包完成后浏览器会自动打开窗口,可以看到打包文件 的分析结果页面,可以看到各个文件所占的资源大小, 然后,我们就可以根据这个图上给出的信息,来针对性优化产物体积。