1. 前言
本文主要介绍在Webpack中如何使用CSS代码处理工具,以及这些工具的作用
2. Webpack
首先先来简单配置一下Webpack
js
const path = require('path');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
]
},
plugins:[
new CleanWebpackPlugin(),
]
}
CleanWebpack用来清理构建文件夹,这样就不用我们每次手动清理了
2.1 Webpack处理CSS资源
在Webpack中处理CSS文件,通常需要用到下面三个:
css-loader
:该 Loader 会将 CSS 等价翻译为形如module.exports = "${css}"
的JavaScript 代码,使得 Webpack 能够如同处理 JS 代码一样解析 CSS 内容与资源依赖style-loader
:该 Loader 将在产物中注入一系列 runtime 代码,这些代码会将 CSS 内容注入到页面的<style>
标签,使得样式生效mini-css-extract-plugin
:该插件会将 CSS 代码抽离到单独的.css
文件,并将文件通过<link>
标签方式插入到页面中
2.1.1 css- loader
我们先从css-loader开始,css-loader提供了很多处理CSS代码的基础能力,包括 CSS 到 JS 转译、依赖解析、Sourcemap、css-in-module 等,基于这些能力,Webpack 才能像处理 JS 模块一样处理 CSS 模块代码
先创建一个src/index.css文件
css
body{
background-color: red;
}
首先需要安装依赖
bash
pnpm install -D css-loader
之后在webpack.config.js配置文件中修改Webpack的配置,定义css文件的处理规则
js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: ["css-loader"],
},
],
},
};
有了css-loader,我们在使用Webpack构建之后,经过css-loader处理后的样式代码最终会被转译成一段JS字符被放入最后的输出文件中
如下,我创建了一个src/index.js文件,并且引入了index.css
js
import './index.css';
function foo() {
console.log('foo');
}
foo()
运行webpack之后,构建文件夹如下:
src/index.css代码和src/index.js代码都在bundle.js文件当中
字段字符串会被当作普通JS模块处理,但是还不会影响到页面样式,如果想要它影响到页面样式,我们还需要继续接入style-loader
2.1.2 style-loader
与其他Loader不同,style-loader加载时并不会对代码内容做任何修改,而是简单注入一系列运行时代码 runtime,用于将css-loader转译出的JS字符串插入到运行页面的style标签中
首先也需要先安装依赖
bash
pnpm install -D style-loader css-loader
之后修改Webpack中对样式文件的配置
js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
],
},
};
注意style-loader在前,css-loader在后,因为Webpack执行时相当于style-loader(css-loader(css))
链式调用,执行后样式代码会被转译为类似下面这样的代码:
js
// Part1: css-loader 处理结果,对标到原始 CSS 代码
const __WEBPACK_DEFAULT_EXPORT__ = (
"body {\n background: yellow;\n font-weight: bold;\n}"
);
// Part2: style-loader 处理结果,将 CSS 代码注入到 `style` 标签
injectStylesIntoStyleTag(
__WEBPACK_DEFAULT_EXPORT__
)
现在我们来测试一下style-loader + css-loader是否生效
我们需要使用html-webpack-plugin插件,这个插件默认将会在 output.path 的目录下创建一个 index.html 文件, 并把 webpack 打包后的静态文件自动插入到这个 html 文件当中
首先先来安装一下依赖
js
pnpm install -D html-webpack-plugin
Webpack配置如下,我们添加了html-webpack-plugin 插件
js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins:[
new HtmlWebpackPlugin()
]
}
运行Webpack后发现构建文件夹如下所示,有了一个index.html文件
index.html文件中也自动引入了bundle.js文件
运行页面可以看到样式在页面中生效
body背景变成了我们编写的css样式的红色,控制台中也输出了foo字符串
经过css-loader + style-loader处理后的样式代码最终会被写入Bundle.js文件,并在运行时通过style标签注入到页面,最终将JS、CSS代码合并进同一个产物文件
但是这个方式会有两个问题
- JS、CSS资源无法并行加载,从而降低页面性能
- 资源缓存粒度变大,JS、CSS任意一种变更都会导致缓存失效
由此,在生产环境中我们通常会使用mini-css-extract-plugin插件替代style-loader,将样式代码抽离为单独的CSS文件
2.1.3 mini-css-extract-plugin
首先还是需要安装依赖
bash
pnpm add -D mini-css-extract-plugin
之后添加Webpack的配置信息
js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HTMLWebpackPlugin = require('html-webpack-plugin')
module.exports = {
module: {
rules: [{
test: /\.css$/,
use: [
// 根据运行环境判断使用那个 loader
(process.env.NODE_ENV === 'development' ?
'style-loader' :
MiniCssExtractPlugin.loader),
'css-loader'
]
}]
},
plugins: [
new MiniCssExtractPlugin(),
new HTMLWebpackPlugin()
]
}
这个时候运行Webpack就能发现css文件被单独抽离出来了,并且在html文件中被引用
运行一下页面,也是符合预期的效果
但是, 以下是几点需要注意的:
- mini-css-extract-plugin库同时提供Loader、Plugin组件,需要同时使用
- mini-css-extract-plugin不能与style-loader混用,否则报错
- mini-css-extract-plugin需要与html-webpack-plugin同时使用,才能将产物路径以link标签方式插入到html中
生产环境使用mini-cssextract-plugin而不使用style-loader后,运行Webpack后将同时生成JS、CSS、HTML三种产物文件
总结,在Webpack中处理CSS,通常需要使用css-loader + style-loader或 css-loader + mini-css-extract-plugin 组合,两种方式最终都能实现加载样式代码的效果。
但鉴于原生 CSS 语言的种种缺陷,我们还可以在此基础上增加更多 Webpack 组件,更优雅、高效地编写页面样式,下面一一展开介绍。
2.2 CSS预处理器
原生 CSS 已经难以应对当代复杂 Web 应用的开发需求。为此,社区在 CSS 原生语法基础上扩展出一些更易用,功能更强大的 CSS 预处理器,下面我们以 Less 为例:
首先先安装依赖
bash
pnpm install -D less less-loader
其次,修改Webpack配置,添加.less处理规则
js
module.exports = {
module: {
rules: [{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
}]
}
}
这里我们需要安装三种依赖,其中less-loader用于将less代码转换为css代码,转换为的css代码随后会被css-loader于style-loader处理,最终在页面生效
2.3 PostCSS
与上面的预处理器类似,PostCSS也能够在原生CSS基础之上增加更多的表达力、可维护性、可读性更强的语言特性。
两者主要区别在于预处理器通常定义了一套 CSS 之上的超集语言;PostCSS 并没有定义一门新的语言,而是与 @babel/core
类似,只是实现了一套将 CSS 源码解析为 AST 结构,并传入 PostCSS 插件做处理的流程框架,具体功能都由插件实现
先来说说PostCss的一些常用插件:
- autoprefixer:基于 Can I Use 网站上的数据,自动添加浏览器前缀
- postcss-preset-env:一款将最新 CSS 语言特性转译为兼容性更佳的低版本代码的插件
- postcss-less:兼容 Less 语法的 PostCSS 插件,类似的还有:postcss-sass、poststylus
- stylelint:一个现代 CSS 代码风格检查器,能够帮助识别样式代码中的异常或风格问题
那么让我们来上手使用,依旧是先安装依赖
bash
pnpm install -D postcss postcss-loader
之后添加Webpack配置
js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
"style-loader",
"css-loader",
"postcss-loader"
],
},
],
}
};
不过,这个时候的 PostCSS 还只是个空壳,下一步还需要使用适当的 PostCSS 插件进行具体的功能处理,例如我们可以使用 autoprefixer
插件自动添加浏览器前缀,首先安装依赖
js
pnpm install -D autoprefixer
之后修改Webpack配置
js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: {
importLoaders: 1
}
},
{
loader: "postcss-loader",
options: {
postcssOptions: {
// 添加 autoprefixer 插件
plugins: [require("autoprefixer")],
},
},
}
],
},
],
}
};
之后,再次运行 Webpack 即可为 CSS 代码自动添加浏览器前缀,例如:
并且PostCSS与预处理器并非互斥关系,我们完全可以在同一个项目中同时使用两者
3.结尾
以上是笔者对一本掘金小册中内容的整理总结与实践,在输入和输出的过程中都学到了很多,感谢掘金🙏