这是我参与更文挑战的第4天,活动详情查看: 更文挑战
前言
在 webpack5 的使用(一):起步 文章里,介绍了如何管理 html 和 js,接触了 entry、output 和 plugins 等属性。这篇文章将补充如何管理 css、图片等资源以及 loader 的使用。
传统的加载资源方式,都是通过在 html 写上对应的标签进行加载资源,如:<style>
。 webpack 并不是这样加载,它是以 js 模块为主导,通过不同的 loader 解析 js 中对应 import 的资源。
加载 css
加载方式1:style 标签
加载 css 需要安装 css-loader 和 style-loader。
npm i -D css-loader style-loader
css-loader 用来识别并加载 css,style-loader 用来将 css 转化为 html 的 style 节点。
webpack.config.js,我们需要新增一个属性 module,在里面配置 loader。
module.exports = {
...
module: {
rules: [
{
test: /\.css$/i,
use: [
'style-loader', 'css-loader'
]
}
]
}
}
我们先在 src 目录创建一个 css 目录,在 css 目录新建一个 index.css,在里面添加测试代码。
body {
background: yellowgreen;
}
在 index.js 添加引入 css。
import '../css/index.css'
先用 npm run dev 测试一下,可以发现背景颜色已经更变了,打开控制台,也可以看见有 style 标签加入。
我们停止刚才的 dev 服务,再运行 npm run build,打开 dist/index.html。
好像并没有 <style>
标签,那 <style>
标签从哪里来呢?
我们再打开 dist/main.js 看看。
可以发现这样一句样式代码,说明 <style>
是由 js 代码动态插入的。
加载方式2:独立 css 文件
一般来说,我们都希望 css 代码是独立在一个 css 文件里,与 html 和 js 分开来,而 mini-css-extract-plugin 插件就是用来将 css 提取到单独文件。
npm i -D mini-css-extract-plugin
既然我们决定要用 mini-css-extract-plugin,那么 style-loader 就不需要用到了。
webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
...
plugins: [
...
new MiniCssExtractPlugin()
],
module: {
rules: [
{
test: /\.css$/i,
use: [
//'style-loader', 'css-loader'
MiniCssExtractPlugin.loader, 'css-loader'
]
}
]
}
}
运行 npm run build。
可以发现 dist 目录多了一个 main.css 文件,而且 index.html 里自动引入了 main.css 文件。
补充,有一个插件也是起到类似的功能,插件名叫 extract-text-webpack-plugin,不过在 webpack4+ 里,这个插件已经不被官方推荐了。
压缩 css 代码
细心的人可能会发现,打包后的 dist 目录下的 js 代码已经被压缩过了,但是 css 代码并没有压缩。现在我们可以利用 css-minimizer-webpack-plugin 来进行压缩 css 代码。
安装 css-minimizer-webpack-plugin
npm i -D css-minimizer-webpack-plugin
注意了,因为 development 环境不需要压缩代码,因此,我们只需要在 webpack.prod.js 写上压缩配置即可。
webpack.prod.js
const { merge } = require('webpack-merge')
const common = require('./webpack.config.js')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = merge(common, {
mode: 'production',
optimization: {
minimizer: [
new CssMinimizerPlugin(),
],
},
})
重新 build 一下,可以发现我们的 css 代码已经被压缩了
但是我们再打开 main.js 看看。
咦?为什么 js 没有压缩?
官方文档里的代码是这样写的,有这么一行注释
optimization: {
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
// `...`,
new CssMinimizerPlugin(),
],
},
我们试试按照官方,把注释代码取消注释。
optimization: {
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
`...`,
new CssMinimizerPlugin(),
],
},
再 build 一下,可以发现 js 代码也被压缩了。
在 webpack5 里 production 环境模式是会自动启用 terser-webpack-plugin,也即是压缩 js 代码,webpack5 是自带最新的 terser-webpack-plugin,因此不需要另外安装和启用。这里有点疑惑的是为什么 webpack5 不顺便把 css-minimizer-webpack-plugin 也自带一下,压缩 css 代码也很重要呀。
此外,也有一款不错压缩 js 的插件 uglifyjs-webpack-plugin,我没有深究 uglifyjs-webpack-plugin 和 terser-webpack-plugin 的区别,有欲望的小伙伴可以深究一下。
加载 scss、sass
安装 sass sass-loader
npm i -D sass sass-loader
我们在 webpack.config.js 添加多一个 rule,用来加载 scss、sass
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: [
//'style-loader', 'css-loader'
MiniCssExtractPlugin.loader, 'css-loader'
]
},
{
test: /\.s[ac]ss$/i,
use: [
MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'
]
}
]
}
}
在 src 创建多一个 scss 文件夹,再创建一个 index.scss 文件,里面写上测试代码。
body {
background: burlywood;
}
index.js 引入 index.scss
import '../scss/index.scss'
build 一下。
发现 dist/main.css 引入了 scss 的代码,说明加载 scss成功。
整理文件
我们发现 dist 目录里的文件又是 html,又是 js,又是 css,杂乱无章,实际部署的项目肯定不能这样。
我们调整一下配置。
webpack.config.js
module.exports = {
...
output: {
filename: 'js/[name].[fullhash].js',
...
},
plugins: [
...
new MiniCssExtractPlugin({
filename: 'css/[name].[fullhash].css'
})
],
...
}
生成的不同的文件就会放到不同的文件夹里。
补充
loader 是有顺序加载的,比如
{
test: /\.s[ac]ss$/i,
use: [
MiniCssExtractPlugin.loader, 'css-loader',
'sass-loader'
]
}
加载顺序:sass-loader > css-loader > MiniCssExtractPlugin.loader
规律是:从下往上,从右到左
完整代码
目录
index.js
import '../css/index.css'
import '../scss/index.scss'
console.log('这是一个入口文件')
console.log('环境变量:', process.env.NODE_ENV)
webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
console.log('环境变量:', process.env.NODE_ENV)
module.exports = {
// entry: path.resolve(__dirname, '../src/js/index.js'),
entry: {
main: path.resolve(__dirname, '../src/js/index.js'),
header: path.resolve(__dirname, '../src/js/header.js'),
footer: path.resolve(__dirname, '../src/js/footer.js'),
},
output: {
// filename: 'main.js',
filename: 'js/[name].[fullhash].js',
path: path.resolve(__dirname, '../dist')
},
// devServer: {
// port: 3000,
// hot: true,
// contentBase: '../dist'
// },
plugins: [
// new HtmlWebpackPlugin({
// title: '首页'
// }),
// 配置多个 HtmlWebpackPlugin,有多少个页面就配置多少个
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../src/html/index.html'),
filename: 'index.html',
chunks: ['main'] // 与入口文件对应的模块名(entry 配置),这里可以理解为引入 main.js
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../src/html/header.html'),
filename: 'header.html',
chunks: ['header']
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../src/html/footer.html'),
filename: 'footer.html',
chunks: ['footer']
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: 'css/[name].[fullhash].css'
})
],
module: {
rules: [
{
test: /\.css$/i,
use: [
//'style-loader', 'css-loader'
MiniCssExtractPlugin.loader, 'css-loader'
]
},
{
test: /\.s[ac]ss$/i,
use: [
MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'
]
}
]
}
}
webpack.prod.js
const { merge } = require('webpack-merge')
const common = require('./webpack.config.js')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = merge(common, {
mode: 'production',
optimization: {
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
`...`,
new CssMinimizerPlugin(),
],
},
})
系列文章
webpack5 的使用(零):概念
webpack5 的使用(一):起步
webpack5 的使用(二):多个环境配置
webpack5 的使用(三):加载 css
webpack5 的使用(四):加载资源文件
webpack5 的使用(五):babel 转译 js 代码
webpack5 的使用(六):优化