# 前言
本文是第二篇,如果你还没有阅读《Webpack系列(基础篇)》,建议阅读之后,再继续阅读本文。
那么在上一期,我们介绍了webpack的概念和webpack几大比较关键的配置项,并且我们配置了babel,html-webpack-plugin,webpack-dev-server等等,本文将介绍配置样式文件的处理、图片的处理、字体的处理,以及一些项目常用相关的知识点和配置。
所以将会引入很多webpack配置项,如果文中有任何错误,欢迎在评论区指正,我会尽快修正。
# 样式文件处理
webpack不能直接处理css,需要借助loader。如果是.css,我们需要的loader通常有: style-loader、css-loader,考虑到兼容性问题,我们可能还需要postcss-loader,而如果是less或者是sass的话,还需要less-loader和sass-loader,这里我们配置一下less和css文件(sass的话将less替换为sass即可):
我们先安装一下需要使用的依赖:
|
pnpm install style-loader less-loader css-loader postcss postcss-loader postcss-preset-env autoprefixer less -D
| |
| -- |
在module配置中添加这些loader
|
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path')
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'), //必须是绝对路径
// 输出文件目录(将来所有资源输出的公共目录,包括css和静态文件等等) path: path.resolve(__dirname, "dist"), //默认 // 入口文件名称(指定名称+目录) filename: "[name].js", // 默认 // 所有资源引入公共路径前缀,一般用于生产环境,小心使用 (例如,你最终编译出来的代码部署不是在根目录下,例如:https://www.xxxx.com/my-app/ 那么 publicPath需要设置为 ‘/my-app/’)
publicPath: '/',
/ 非入口文件chunk的名称。所谓非入口即import动态导入形成的chunk或者optimization中的splitChunks提取的公共chunk 它支持和 filename 一致的内置变量 /
chunkFilename: '[contenthash:10].chunk.js',
clean: true, // 打包前清空输出目录,相当于clean-webpack-plugin插件的作用,webpack5新增。
/ 当用 Webpack 去构建一个可以被其他模块导入使用的库时需要用到library /
// library: {
// name: '[name]', //整个库向外暴露的变量名
// type: 'window', //库暴露的方式
// },
},
module: {
rules: [
{
test: /.js$/, // 这告诉 webpack 编译器(compiler) 如下信息:
// “嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.js' 的路径时,在你对它打包之前,先 use(使用) babel-loader 转换一下。”
use: ['babel-loader'],
exclude: /node_modules/, //排除 node_modules 目录
},
// 新增代码
{
test: /.(le|c)ss$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'postcss-preset-env',
{
// 其他选项
},
],
],
},
},
},
'less-loader',
],
exclude: /node_modules/,
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html',
minify: {
removeAttributeQuotes: false, // 是否删除属性的双引号
collapseWhitespace: false, // 是否折叠空白
},
hash: true,
}),
],
}
| |
| -- |
我们测试一下效果,新建一个less文件,src/index.less
**project**
webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- main.js
|- index.html
|- /src
- |- index.less
|- sum.js
|- index.js
|- /node_modules
再在入口文件中引入此less;
|
import './index.less';
| |
| -- |
我们修改了入口文件和配置文件,我们需要pnpm run serve重启开发服务器。这时候我们可以看到页面的背景色变成了红色。
OK,我们简单说一下上面的配置:
* style-loader 动态创建 style 标签,将 css 插入到 head 中.
* css-loader 负责处理 @import 等语句。
* postcss-loader,自动生成浏览器兼容性前缀 —— 2022了,应该没人去自己徒手去写浏览器前缀了吧
* less-loader 负责处理编译 .less 文件,将其转为 css
**注意:**
`loader` 的执行顺序是从右向左执行的,也就是后面的 `loader` 先执行,上面 `loader` 的执行顺序为: `less-loader` ---> `postcss-loader` ---> `css-loader` ---> `style-loader`
所以也就是说模块 loader 可以链式调用。链中的每个 loader 都将对资源进行转换。链会逆序执行。第一个 loader 将其结果(被转换后的资源)传递给下一个 loader,依此类推。最后,webpack 期望链中的最后的 loader 返回 JavaScript。
应保证 loader 的先后顺序:['style-loader'](https://webpack.docschina.org/loaders/style-loader) 在前,而 ['css-loader'](https://webpack.docschina.org/loaders/css-loader) 在后。如果不遵守此约定,webpack 可能会抛出错误。
现在的一切看起来都很完美,但是假设我们的文件中使用了本地的图片,例如:
|
body{
background: url('./my-image.png');
}
| |
| -- |
你就会发现,报错啦啦啦,那么我们要怎么处理图片或是本地的一些其它资源文件呢。不用想,肯定又需要 `loader` 出马了。
# 图片/字体文件处理
## 图片处理
在webpack5中,我们处理图片时已经不需要下载某个特定的loader,可以使用内置的 [Asset Modules](https://webpack.docschina.org/guides/asset-modules/),我们可以轻松地将这些内容混入我们的系统中:
|
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path')
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'), //必须是绝对路径
// 输出文件目录(将来所有资源输出的公共目录,包括css和静态文件等等) path: path.resolve(__dirname, "dist"), //默认 // 入口文件名称(指定名称+目录) filename: "[name].js", // 默认 // 所有资源引入公共路径前缀,一般用于生产环境,小心使用 (例如,你最终编译出来的代码部署不是在根目录下,例如:https://www.xxxx.com/my-app/ 那么 publicPath需要设置为 ‘/my-app/’)
publicPath: '/',
/ 非入口文件chunk的名称。所谓非入口即import动态导入形成的chunk或者optimization中的splitChunks提取的公共chunk 它支持和 filename 一致的内置变量 /
chunkFilename: '[contenthash:10].chunk.js',
clean: true, // 打包前清空输出目录,相当于clean-webpack-plugin插件的作用,webpack5新增。
/ 当用 Webpack 去构建一个可以被其他模块导入使用的库时需要用到library /
// library: {
// name: '[name]', //整个库向外暴露的变量名
// type: 'window', //库暴露的方式
// },
},
module: {
rules: [
{
test: /.js$/, // 这告诉 webpack 编译器(compiler) 如下信息:
// “嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.js' 的路径时,在你对它打包之前,先 use(使用) babel-loader 转换一下。”
use: ['babel-loader'],
exclude: /node_modules/, //排除 node_modules 目录
},
{
test: /.(le|c)ss$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
plugins: function () {
return [
require('autoprefixer')({
overrideBrowserslist: ['>0.25%', 'not dead'],
}),
]
},
},
},
'less-loader',
],
exclude: /node_modules/,
}, // 新增代码
{
test: /.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html',
minify: {
removeAttributeQuotes: false, // 是否删除属性的双引号
collapseWhitespace: false, // 是否折叠空白
},
hash: true,
}),
],
}| | | -- | 现在,在 import MyImage from './my-image.png' 时,此图像将被处理并添加到 output 目录,并且 MyImage 变量将包含该图像在处理后的最终 url。 **project**
webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist|- 0baac8c1cb0255b4e99b.png
|- main.js
|- index.html
|- /src
|- sum.js
|- index.js
|- index.less
|- /node_modules我们向项目添加一个图片,然后看它是如何工作的,你可以使用任何你喜欢的图片: **project**
webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- main.js
|- index.html
|- /src|- my-image.png
|- sum.js
|- index.js
|- index.less
|- /node_modules**src/index.js** |
import { sum } from './sum.js'
// 新增代码
import MyImage from './my-image.png'
class Animal {
constructor(name) {
this.name = name
}
getName() {
return this.name
}
}
const dog = new Animal('dog')
console.log('dog', dog)
const res = sum(1, 2)
console.log('res', res)// 新增代码
// 将图像添加到我们已经存在的 div 中。
const myIcon = new Image()
myIcon.src = MyImagedocument.body.appendChild(myIcon)
| | | -- | 重新构建并查看 [http://localhost:8080/](http://localhost:8080/), |
PS C:\Users\xxxxx\Desktop\webpack-demo> pnpm run serve
[email protected] serve
webpack-dev-server[webpack-dev-server] Project is running at:
[webpack-dev-server] Loopback: http://localhost:8081/
[webpack-dev-server] On Your Network (IPv4): http://10.5.1.230:8081/
[webpack-dev-server] Content not from webpack is served from 'C:\Users\xxxxx\Desktop\webpack-demo\public' directory
asset main.js 528 KiB [emitted] (name: main)
asset 0baac8c1cb0255b4e99b.png 109 KiB [emitted] [immutable] [from: src/my-image.png] (auxiliary name: main)
asset index.html 338 bytes [emitted]
runtime modules 26 KiB 11 modules
modules by path ./node_modules/.pnpm/ 161 KiB
modules by path ./node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/webpack-dev-server/client/ 56.8 KiB 12
modules
modules by path ./node_modules/.pnpm/[email protected][email protected]/node_modules/webpack/hot/*.js 4.3 KiB 4 modules
modules by path ./node_modules/.pnpm/[email protected]/node_modules/html-entities/lib/*.js 81.3 KiB./node_modules/.pnpm/[email protected]/node_modules/html-entities/lib/index.js 7.74 KiB [built] [code generated] ./node_modules/.pnpm/[email protected]/node_modules/html-entities/lib/named-references.js 72.7 KiB [built] [code generated] ./node_modules/.pnpm/[email protected]/node_modules/html-entities/lib/numeric-unicode-map.js 339 bytes [built] [code generated] ./node_modules/.pnpm/[email protected]/node_modules/html-entities/lib/surrogate-pairs.js 537 bytes [built] [code generated]
./node_modules/.pnpm/[email protected]/node_modules/ansi-html-community/index.js 4.16 KiB [built] [code generated]
./node_modules/.pnpm/[email protected]/node_modules/events/events.js 14.5 KiB [built] [code generated]
modules by path ./src/ 538 bytes (javascript) 109 KiB (asset)
./src/index.js 447 bytes [built] [code generated]
./src/sum.js 49 bytes [built] [code generated]
./src/my-image.png 42 bytes (javascript) 109 KiB (asset) [built] [code generated]
webpack 5.66.0 compiled successfully in 5163 ms
assets by status 109 KiB [cached] 1 asset
assets by path *.js 529 KiB
asset main.js 528 KiB [emitted] (name: main)
asset main.556e610d583f620ca5ff.hot-update.js 854 bytes [emitted] [immutable] [hmr] (name: main)
asset index.html 338 bytes [emitted]
asset main.556e610d583f620ca5ff.hot-update.json 28 bytes [emitted] [immutable] [hmr]
Entrypoint main 529 KiB (109 KiB) = main.js 528 KiB main.556e610d583f620ca5ff.hot-update.js 854 bytes 1 auxiliary asset
cached modules 162 KiB (javascript) 109 KiB (asset) [cached] 25 modules
runtime modules 26 KiB 11 modules
webpack 5.66.0 compiled successfully in 28 ms| | | -- | 如果一切顺利,你现在应该看到了你的myimage图片作为img元素已经被添加到了body中,如果检查此元素,你将看到实际的 文件名已经变更为了 这意味着webpack在src文件夹中找到了我们的文件并对其进行了处理。 ## 字体处理 那么,像字体这样的其他资源如何处理呢?使用 Asset Modules 可以接收并加载任何文件,然后将其输出到构建目录。这就是说,我们可以将它们用于任何类型的文件,也包括字体。让我们更新 webpack.config.js 来处理字体文件: |
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path')
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
entry: './src/index.js',
output: {path: path.resolve(__dirname, 'dist'), //必须是绝对路径 // 输出文件目录(将来所有资源输出的公共目录,包括css和静态文件等等) path: path.resolve(__dirname, "dist"), //默认 // 入口文件名称(指定名称+目录) filename: "[name].js", // 默认 // 所有资源引入公共路径前缀,一般用于生产环境,小心使用 (例如,你最终编译出来的代码部署不是在根目录下,例如:https://www.xxxx.com/my-app/ 那么 publicPath需要设置为 ‘/my-app/’) publicPath: '/', /* 非入口文件chunk的名称。所谓非入口即import动态导入形成的chunk或者optimization中的splitChunks提取的公共chunk 它支持和 filename 一致的内置变量 */ chunkFilename: '[contenthash:10].chunk.js', clean: true, // 打包前清空输出目录,相当于clean-webpack-plugin插件的作用,webpack5新增。 /* 当用 Webpack 去构建一个可以被其他模块导入使用的库时需要用到library */ // library: { // name: '[name]', //整个库向外暴露的变量名 // type: 'window', //库暴露的方式 // },
},
module: {rules: [ { test: /\.js$/, // 这告诉 webpack 编译器(compiler) 如下信息: // “嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.js' 的路径时,在你对它打包之前,先 use(使用) babel-loader 转换一下。” use: ['babel-loader'], exclude: /node_modules/, //排除 node_modules 目录 }, { test: /\.(png|svg|jpg|jpeg|gif)$/i, type: 'asset/resource', },
// 新增代码
- {
- test: /.(woff|woff2|eot|ttf|otf)$/i,
- type: 'asset/resource',
},
],
},
plugins: [new HtmlWebpackPlugin({ template: './public/index.html', filename: 'index.html', minify: {
removeAttributeQuotes: false, // 是否删除属性的双引号
collapseWhitespace: false, // 是否折叠空白}, hash: true, }),
],
}
| | | -- | 在项目中添加一些字体文件: **project**
webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- bundle.js
|- index.html
|- /src- |- my-font.woff
|- my-font.woff2
|- icon.png
|- style.css
|- index.js
|- /node_modules配置好 loader 并将字体文件放在合适的位置后,你可以通过一个 `@font-face` 声明将其混合。本地的 `url(...)` 指令会被 webpack 获取处理,就像它处理图片一样: |
@color: red;
@font-face {
font-family: 'MyFont';
src: url('./my-font.woff2') format('woff2'),
url('./my-font.woff') format('woff'),
font-weight: normal;
font-style: normal;
}
body{
background: @color;
transition: all 2s;
font-family: 'MyFont';
background: url('./my-image.png');
}
| | | -- | 现在,让我们重新构建,然后看下 webpack 是否处理了我们的字体: |
PS C:\Users\sdasd\Desktop\webpack-demo> pnpm run serve
[email protected] serve
webpack-dev-server[webpack-dev-server] Project is running at:
[webpack-dev-server] Loopback: http://localhost:8081/
[webpack-dev-server] On Your Network (IPv4): http://10.5.1.230:8081/
[webpack-dev-server] Content not from webpack is served from 'C:\Users\sdasd\Desktop\webpack-demo\public' directory
assets by info 382 KiB [immutable]
asset f20ea7374f9ec83ea6ad.woff 193 KiB [emitted] [immutable] [from: src/my-font.woff] (auxiliary name: main)
asset 0baac8c1cb0255b4e99b.png 109 KiB [emitted] [immutable] [from: src/my-image.png] (auxiliary name: main)
asset 98a02ea1befce507ba6f.woff2 80.3 KiB [emitted] [immutable] [from: src/my-font.woff2] (auxiliary name: main)
asset main.js 596 KiB [emitted] (name: main)
asset index.html 338 bytes [emitted]
runtime modules 26 KiB 11 modules
modules by path ./node_modules/.pnpm/ 170 KiB
modules by path ./node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/webpack-dev-server/client/ 56.8 KiB 12
modules
modules by path ./node_modules/.pnpm/[email protected][email protected]/node_modules/style-loader/dist/runtime/*.js 5.75 KiB 6 modules
modules by path ./node_modules/.pnpm/[email protected][email protected]/node_modules/webpack/hot/*.js 4.3 KiB 4 modules
modules by path ./node_modules/.pnpm/[email protected]/node_modules/html-entities/lib/*.js 81.3 KiB 4 modules
modules by path ./node_modules/.pnpm/[email protected][email protected]/node_modules/css-loader/dist/runtime/*.js 3.52 KiB 3 modules
2 modules
modules by path ./src/ 6.12 KiB (javascript) 382 KiB (asset)
javascript modules 6 KiBmodules by path ./src/*.js 494 bytes 2 modules modules by path ./src/*.less 5.51 KiB 2 modules
asset modules 126 bytes (javascript) 382 KiB (asset) 3 modules
webpack 5.66.0 compiled successfully in 7825 ms| | | -- | 重新打开 [http://localhost:8080/](http://localhost:8080/),看看我们的body内是否换上了新的字体。如果一切顺利,你应该能看到变化。 目前的配置有一个问题,打包之后不管是图片还是字体都是在dist根目录下的,我们能不能指定的将文件打包生成在特定的文件夹内呢,答案是当然可以。 |
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path')
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
entry: './src/index.js',
output: {path: path.resolve(__dirname, 'dist'), //必须是绝对路径 // 输出文件目录(将来所有资源输出的公共目录,包括css和静态文件等等) path: path.resolve(__dirname, "dist"), //默认 // 入口文件名称(指定名称+目录) filename: "[name].js", // 默认 // 所有资源引入公共路径前缀,一般用于生产环境,小心使用 (例如,你最终编译出来的代码部署不是在根目录下,例如:https://www.xxxx.com/my-app/ 那么 publicPath需要设置为 ‘/my-app/’) publicPath: '/', /* 非入口文件chunk的名称。所谓非入口即import动态导入形成的chunk或者optimization中的splitChunks提取的公共chunk 它支持和 filename 一致的内置变量 */ chunkFilename: '[contenthash:10].chunk.js', clean: true, // 打包前清空输出目录,相当于clean-webpack-plugin插件的作用,webpack5新增。 /* 当用 Webpack 去构建一个可以被其他模块导入使用的库时需要用到library */ // library: { // name: '[name]', //整个库向外暴露的变量名 // type: 'window', //库暴露的方式 // },
},
module: {rules: [ { test: /\.js$/, // 这告诉 webpack 编译器(compiler) 如下信息: // “嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.js' 的路径时,在你对它打包之前,先 use(使用) babel-loader 转换一下。” use: ['babel-loader'], exclude: /node_modules/, //排除 node_modules 目录 }, { test: /\.(le|c)ss$/, use: [ 'style-loader', 'css-loader', { loader: 'postcss-loader', options: { postcssOptions: { plugins: [ [ 'postcss-preset-env', { // 其他选项 }, ], ], }, }, }, 'less-loader', ], exclude: /node_modules/, }, { test: /\.(png|svg|jpg|jpeg|gif)$/i, type: 'asset/resource', generator: { filename: 'assets/images/[hash][ext][query]', }, }, { test: /\.(woff|woff2|eot|ttf|otf)$/i, type: 'asset/resource', generator: { filename: 'assets/fonts/[hash][ext][query]', }, }, ],
},
plugins: [new HtmlWebpackPlugin({ template: './public/index.html', filename: 'index.html', minify: { removeAttributeQuotes: false, // 是否删除属性的双引号 collapseWhitespace: false, // 是否折叠空白 }, hash: true, }),
],
}| | | -- | 我们只需要在特定的loader中添加 generator即可,使用此配置,将所有的特定的文件发送到输出目录中的特定的文件夹内。 接下来我们运行pnpm run build,之后我们就可以看到dist文件夹中多了一个assets文件夹,内部有images文件夹和fonts文件夹,分别存放我们的图片和字体。 # 静态资源拷贝 有些时候,我们需要使用已有的JS文件、CSS文件(本地文件),但是不需要 webpack 编译。例如,我们在 public/index.html 中引入了 public 目录下的 js 或 css 文件。这个时候,如果直接打包,那么在构建出来之后,肯定是找不到对应的 js / css 了。 这时候,我们 npm run build,部署后会发现有找不到该资源文件的报错信息。 对于这个问题,我们可以手动将其拷贝至构建目录,然后在配置 CleanWebpackPlugin 时,注意不要清空对应的文件或文件夹即可,但是如若这个静态文件时不时的还会修改下,那么依赖于手动拷贝,是很容易出问题的。 不要过于相信自己的记性,依赖于手动拷贝的方式,大多数人应该都有过忘记拷贝的经历,你要是说你从来没忘过。 幸运的是,webpack 为我们这些记性不好又爱偷懒的人提供了好用的插件 [CopyWebpackPlugin](https://link.juejin.cn/?target=https%3A%2F%2Fwebpack.js.org%2Fplugins%2Fcopy-webpack-plugin%2F),它的作用就是将单个文件或整个目录复制到构建目录。 |
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyPlugin = require("copy-webpack-plugin");
const path = require('path')
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
entry: './src/index.js',
output: {path: path.resolve(__dirname, 'dist'), //必须是绝对路径 // 输出文件目录(将来所有资源输出的公共目录,包括css和静态文件等等) path: path.resolve(__dirname, "dist"), //默认 // 入口文件名称(指定名称+目录) filename: "[name].js", // 默认 // 所有资源引入公共路径前缀,一般用于生产环境,小心使用 (例如,你最终编译出来的代码部署不是在根目录下,例如:https://www.xxxx.com/my-app/ 那么 publicPath需要设置为 ‘/my-app/’) publicPath: '/', /* 非入口文件chunk的名称。所谓非入口即import动态导入形成的chunk或者optimization中的splitChunks提取的公共chunk 它支持和 filename 一致的内置变量 */ chunkFilename: '[contenthash:10].chunk.js', clean: true, // 打包前清空输出目录,相当于clean-webpack-plugin插件的作用,webpack5新增。 /* 当用 Webpack 去构建一个可以被其他模块导入使用的库时需要用到library */ // library: { // name: '[name]', //整个库向外暴露的变量名 // type: 'window', //库暴露的方式 // },
},
module: {rules: [ { test: /\.js$/, // 这告诉 webpack 编译器(compiler) 如下信息: // “嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.js' 的路径时,在你对它打包之前,先 use(使用) babel-loader 转换一下。” use: ['babel-loader'], exclude: /node_modules/, //排除 node_modules 目录 }, { test: /\.(le|c)ss$/, use: [ 'style-loader', 'css-loader', { loader: 'postcss-loader', options: { postcssOptions: { plugins: [ [ 'postcss-preset-env', { // 其他选项 }, ], ], }, }, }, 'less-loader', ], exclude: /node_modules/, }, { test: /\.(png|svg|jpg|jpeg|gif)$/i, type: 'asset/resource', generator: { filename: 'assets/images/[hash][ext][query]', }, }, { test: /\.(woff|woff2|eot|ttf|otf)$/i, type: 'asset/resource', generator: { filename: 'assets/fonts/[hash][ext][query]', }, }, ],
},
plugins: [new HtmlWebpackPlugin({ template: './public/index.html', filename: 'index.html', minify: { removeAttributeQuotes: false, // 是否删除属性的双引号 collapseWhitespace: false, // 是否折叠空白 }, hash: true, }), new CopyPlugin({ patterns: [ { from: "'public/js/*.js'", to: path.resolve(__dirname, 'dist', 'js'),}, ], }),
],
}| | | -- | 此时,重新执行 npm run build,报错信息已经消失。 # ProvidePlugin ProvidePlugin 在我看来,是为懒人准备的,不过也别过度使用,毕竟全局变量不是什么“好东西”。ProvidePlugin 的作用就是不需要 import 或 require 就可以在项目中到处使用。 ProvidePlugin 是 webpack 的内置插件,使用方式如下: |
new webpack.ProvidePlugin({
identifier1: 'module1', identifier2: ['module2', 'property2']
});
| | | -- | 默认寻找路径是当前文件夹 ./** 和 node_modules,当然啦,你可以指定全路径。 React 大家都知道的,使用的时候,要在每个文件中引入 React,不然立刻抛错给你看。还有就是 jquery, lodash 这样的库,可能在多个文件中使用,但是懒得每次都引入,好嘛,一起来偷个懒,修改下 webpack 的配置: |
const webpack = require('webpack');
module.exports = {//... plugins: [ new webpack.ProvidePlugin({ React: 'react', Component: ['react', 'Component'], Vue: ['vue/dist/vue.esm.js', 'default'], $: 'jquery', _map: ['lodash', 'map'] }) ]
}
| | | -- | 这样配置之后,你就可以在项目中随心所欲的使用 $、_map了,并且写 React 组件时,也不需要 import React 和 Component 了,如果你想的话,你还可以把 React 的 Hooks 都配置在这里。 另外呢,Vue 的配置后面多了一个 default,这是因为 vue.esm.js 中使用的是 export default 导出的,对于这种,必须要指定 default。React 使用的是 module.exports 导出的,因此不要写 default。 另外,就是如果你项目启动了 eslint 的话,记得修改下 eslint 的配置文件,增加以下配置: |
{
"globals": {
"React": true,
"Vue": true,
//....
}
}| | | -- | 当然啦,偷懒要有个度,你要是配一大堆全局变量,最终可能会给自己带来麻烦,对自己配置的全局变量一定要负责到底。 # 抽离CSS CSS打包我们前面已经说过了,不过呢,有些时候,我们可能会有抽离CSS的需求,即将CSS文件单独打包,这可能是因为打包成一个JS文件太大,影响加载速度,也有可能是为了缓存(例如,只有JS部分有改动),还有可能就是“我高兴”:我想抽离就抽离,谁也管不着。 不管你是因为什么原因要抽离CSS,只要你有需求,我们就可以去实现。 首先,安装 loader: |
pnpm install mini-css-extract-plugin -D
| | | -- | mini-css-extract-plugin 和 extract-text-webpack-plugin 相比: 1. 异步加载 2. 不会重复编译(性能更好) 3. 更容易使用 4. 特别针对 CSS 开发 建议 mini-css-extract-plugin 与 [css-loader](https://webpack.docschina.org/loaders/css-loader/) 一起使用。 之后将 loader 与 plugin 添加到你的 webpack 配置文件中。 例如: |
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const path = require('path')
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
entry: './src/index.js',
output: {path: path.resolve(__dirname, 'dist'), //必须是绝对路径 // 输出文件目录(将来所有资源输出的公共目录,包括css和静态文件等等) path: path.resolve(__dirname, "dist"), //默认 // 入口文件名称(指定名称+目录) filename: "[name].js", // 默认 // 所有资源引入公共路径前缀,一般用于生产环境,小心使用 (例如,你最终编译出来的代码部署不是在根目录下,例如:https://www.xxxx.com/my-app/ 那么 publicPath需要设置为 ‘/my-app/’) publicPath: '/', filename: 'bundle.[hash].js', /* 非入口文件chunk的名称。所谓非入口即import动态导入形成的chunk或者optimization中的splitChunks提取的公共chunk 它支持和 filename 一致的内置变量 */ chunkFilename: '[contenthash:10].chunk.js', clean: true, // 打包前清空输出目录,相当于clean-webpack-plugin插件的作用,webpack5新增。 /* 当用 Webpack 去构建一个可以被其他模块导入使用的库时需要用到library */ // library: { // name: '[name]', //整个库向外暴露的变量名 // type: 'window', //库暴露的方式 // },
},
module: {rules: [ { test: /\.js$/, // 这告诉 webpack 编译器(compiler) 如下信息: // “嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.js' 的路径时,在你对它打包之前,先 use(使用) babel-loader 转换一下。” use: ['babel-loader'], exclude: /node_modules/, //排除 node_modules 目录 }, { test: /\.(le|c)ss$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', { loader: 'postcss-loader', options: { postcssOptions: { plugins: [ [ 'postcss-preset-env', { // 其他选项 }, ], ], }, }, }, 'less-loader', ], exclude: /node_modules/, }, { test: /\.(png|svg|jpg|jpeg|gif)$/i, type: 'asset/resource', generator: { filename: 'assets/images/[hash][ext][query]', }, }, { test: /\.(woff|woff2|eot|ttf|otf)$/i, type: 'asset/resource', generator: { filename: 'assets/fonts/[hash][ext][query]', }, }, ],
},
plugins: [new HtmlWebpackPlugin({ template: './public/index.html', filename: 'index.html', minify: { removeAttributeQuotes: false, // 是否删除属性的双引号 collapseWhitespace: false, // 是否折叠空白 }, hash: true, }), new MiniCssExtractPlugin({ filename: 'css/[name].css', //个人习惯将css文件放在单独目录下 //publicPath:'../' //如果你的output的publicPath配置的是 './' 这种相对路径,那么如果将css文件放在单独目录下,记得在这里指定一下publicPath }),
],
}| | | -- | 现在,我们重新编译:pnpm run build,目录结构如下所示:
├── dist
│ ├── assets
│ │ ├── fonts
│ │ └── images
│ ├── css
│ │ ├── main.css
│ ├── bundle.e1fe7c49b5e79f754e74.js
│ └── index.html# 压缩css文件 使用mini-css-extract-plugin,css文件默认不会被压缩,如果想要压缩需要配置optimization,首先需要安装 [css-minimizer-webpack-plugin](https://webpack.docschina.org/plugins/css-minimizer-webpack-plugin/)。 |
pnpm install css-minimizer-webpack-plugin --save-dev
| | | -- | 这个插件使用 [cssnano](https://cssnano.co/) 优化和压缩 CSS。就像 [optimize-css-assets-webpack-plugin](https://github.com/NMFR/optimize-css-assets-webpack-plugin) 一样,但在 source maps 和 assets 中使用查询字符串会更加准确,支持缓存和并发模式下运行。 接着在 `webpack` 配置中加入该插件。示例: |
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");module.exports = {
module: {rules: [
{
test: /\.(le|c)ss$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', { loader: 'postcss-loader', options: { postcssOptions: { plugins: [ [ 'postcss-preset-env', { // 其他选项 }, ], ], }, }, }, 'less-loader', ], exclude: /node_modules/, }, ],
},
optimization: {minimizer: [ // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释 // `...`, new CssMinimizerPlugin(), ],
},
plugins: [new MiniCssExtractPlugin()],
};| | | -- | 这将仅在生产环境开启 CSS 优化。 如果还想在开发环境下启用 CSS 优化,请将 `optimization.minimize` 设置为 `true`: |
module.exports = {
optimization: {// [...] minimize: true,
},
};| | | -- | CSS压缩通常是去除无用的空格等,因为很难去修改选择器、属性的名称、值等; # 按需加载 很多时候我们不需要一次性加载所有的JS文件,而应该在不同阶段去加载所需要的代码。webpack内置了强大的分割代码的功能可以实现按需加载。 比如,我们在点击了某个按钮之后,才需要使用使用对应的JS文件中的代码,需要使用 import() 语法: |
document.getElementById('btn').onclick = function () {
import('./sum.js').then((fn) => {const res = fn.default(1, 2) console.log('res', res)
})
}| | | -- | import() 语法,需要 @babel/plugin-syntax-dynamic-import 的插件支持,但是因为当前 @babel/preset-env 预设中已经包含了 @babel/plugin-syntax-dynamic-import,因此我们不需要再单独安装和配置。 直接 pnpm run serve 进行构建,构建结果如下: |
PS C:\Users\sdasd\Desktop\webpack-demo> pnpm run serve
[email protected] serve C:\Users\sdasd\Desktop\webpack-demo
webpack-dev-server[webpack-dev-server] Project is running at:
[webpack-dev-server] Loopback: http://localhost:8080/
[webpack-dev-server] On Your Network (IPv4): http://10.5.1.230:8080/
[webpack-dev-server] Content not from webpack is served from 'C:\Users\sdasd\Desktop\webpack-demo\public' directory
(node:11200) [DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_HASH] DeprecationWarning: [hash] is now [fullhash] (also consider using [chunkhash] or [contenthash], see documentation for details)
(Usenode --trace-deprecation ...
to show where the warning was created)
assets by info 941 KiB [immutable]
assets by path assets/ 382 KiBasset assets/fonts/f20ea7374f9ec83ea6ad.woff 193 KiB [emitted] [immutable] [from: src/my-font.woff] (auxiliary name: main) asset assets/images/0baac8c1cb0255b4e99b.png 109 KiB [emitted] [immutable] [from: src/my-image.png] (auxiliary name: main) asset assets/fonts/98a02ea1befce507ba6f.woff2 80.3 KiB [emitted] [immutable] [from: src/my-font.woff2] (auxiliary name: main)
assets by path *.js 559 KiB
asset bundle.d404a2475c781bb780ba.js 557 KiB [emitted] [immutable] (name: main) asset c9015285cb.chunk.js 1.8 KiB [emitted] [immutable]
asset css/main.css 1.41 KiB [emitted] (name: main)
asset index.html 539 bytes [emitted]
Entrypoint main 559 KiB (382 KiB) = css/main.css 1.41 KiB bundle.d404a2475c781bb780ba.js 557 KiB 3 auxiliary assets
runtime modules 44.8 KiB 23 modules
orphan modules 5.69 KiB (javascript) 273 KiB (asset) [orphan] 6 modules
cacheable modules 168 KiB (javascript) 109 KiB (asset) 456 bytes (css/mini-extract)
modules by path ./node_modules/.pnpm/ 167 KiBmodules by path ./node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/webpack-dev-server/client/ 56.8 KiB 12 modules modules by path ./node_modules/.pnpm/[email protected][email protected]/node_modules/webpack/hot/*.js 4.3 KiB 4 modules modules by path ./node_modules/.pnpm/[email protected]/node_modules/html-entities/lib/*.js 81.3 KiB 4 modules modules by path ./node_modules/.pnpm/[email protected][email protected]/node_modules/mini-css-extract-plugin/dist/hmr/*.js 5.97 KiB 2 modules 2 modules
modules by path ./src/ 1.11 KiB (javascript) 109 KiB (asset) 456 bytes (css/mini-extract)
javascript modules 1.07 KiB 3 modules ./src/my-image.png 42 bytes (javascript) 109 KiB (asset) [built] [code generated] [build time executed] css ./node_modules/.pnpm/[email protected][email protected]/node_modules/css-loader/dist/cjs.js!./node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/less-loader/dist/cjs.js!./src/index.less 456 bytes [built] [code generated]
webpack 5.66.0 compiled successfully in 7701 ms
| | | -- | webpack 遇到 import(****) 这样的语法的时候,会这样处理: * 以**** 为入口新生成一个 Chunk * 当代码执行到 import 所在的语句时,才会加载该 Chunk 所对应的文件(如这里的c9015285cb.chunk.js) 大家可以在浏览器中的控制台中,在 Network 的 Tab页 查看文件加载的情况,只有点击之后,才会加载对应的 JS 。 # browserslistrc 用于在不同前端工具之间共享目标浏览器和 Node.js 版本的配置文件,用于: * [Autoprefixer](https://github.com/postcss/autoprefixer) * [Babel ](https://github.com/babel/babel/tree/master/packages/babel-preset-env) * [postcss-preset-env](https://github.com/jonathantneal/postcss-preset-env) * [eslint-plugin-compat](https://github.com/amilajack/eslint-plugin-compat) * [stylelint-no-unsupported-browser-features](https://github.com/ismay/stylelint-no-unsupported-browser-features) * [postcss-normalize](https://github.com/jonathantneal/postcss-normalize) * [obsolete-webpack-plugin](https://github.com/ElemeFE/obsolete-webpack-plugin) 在项目根目录创建一个.browserslistrc 文件,这样可以多个 loader 共享配置,所以,动手在根目录下新建文件 (.browserslistrc),内容如下(你可以根据自己项目需求,修改为其它的配置): |
last 2 version
0.25%
not dead| | | -- | # 总结 下一篇将会补充:**《webpack 进阶篇》** 大致内容包括: 1. 优化构建速度 * 优化 resolve 配置 * extensions * 缩小范围 * noParse * 多进程配置 * 利用缓存 2. 优化构建结果 * 构建结果分析 * 压缩 JS * 清除无用的 CSS 3. 优化运行时体验 * 入口点分割(多页打包 * splitChunks 分包配置 * 代码懒加载 * prefetch 与 preload ## 参考资料 * [webpack中文文档](https://webpack.docschina.org/configuration/mode)