theme: channing-cyan
导语
来到这家公司之后,一直在使用webpack,也写了不少笔记,但是都比较零散,现在决定整理一下webpack相关的知识点,由浅入深,方便自己后续查漏补缺,后续会一直更新。
核心概念
Module
模块:在webpack中,万物皆是模块,可以理解成是我们手写和引入的一个个文件。
Chunk
代码库: 一个chunk由十多个相关联模块组合而成,当我们写的 module 源文件传到 webpack 进行打包时,webpack 会根据文件引用关系生成 chunk 文件,是打包过程的中间产物。
bundle
webpack 处理好 chunk 文件后,最后会输出 bundle 文件,这个 bundle 文件包含了经过加载和编译的最终源文件,所以它可以直接在浏览器中运行。
entry
打包的入口,在打包时,从entry开始寻找模块依赖。
output
打包的出口,打包完成之后,各个资源文件输出的位置。
mode
打包模式,有生产环境与测试环境两种,默认是测试环境
- 测试环境,打包配置以方便调试监控代码为主
- 生产环境,打包配置以压缩体积,优化加载速度为主
loader
模块转换器,webpack打包的时候只能识别.js的文件,对于其他文件,则需要借助loader来处理,loader的作用是把模块原内容按照需求转换成新内容,如图片,css等文件,有三种配置方式:内联 CLI config.js配置
注意:loader在处理数据的时候,其实是链式传递的,即所有loader形成一条流水线,当我处理不了时,交由下一个loader来处理。
Plugin
扩展插件,在webpack构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要做的事情,用于增加webpack的功能,本质上是一个JavaScript的对象。
基础配置
首先,我们来创建一个空项目,运行
代码语言:javascript复制npm init
npm install -D webpack webpack-cli
然后新建一个webpack.config.js文件,来编写我们的webpack配置
入口配置
我们先编写最基础的配置,入口和出口
代码语言:javascript复制const path = require('path');
module.exports = {
entry: path.resolve(__dirname, 'src/index.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: './'
}
}
- publicPath:指定基础路径,开发环境一般是项目的根路径,上线之后一般是CDN的路径
- __dirname:表示项目所在目录的根路径
现在我们可以来试一下最简单的打包操作
新建入口文件src/index.js,并且编写如下代码
代码语言:javascript复制const a = 12
const b = 12
function add(x, y) {
return x y
}
const c = add(a,b)
console.log(c)
然后在package.json中的script部分加入打包指令
代码语言:javascript复制{
"scripts": {
"build": "webpack --mode development"
},
}
执行打包
代码语言:javascript复制npm run build
在项目根目录下,便出现了dist文件夹,以及打包的内容bundle.js,目前我们还没做任何优化,所以所有的代码都会被进去。
使用模板 html
使用html-webpack-plugin 可以指定template模板文件,将会在output目录下,生成html文件,并引入打包后的js.
在配置文件中加入如下配置
代码语言:javascript复制const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
//...
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'src/index.html'),
filename:'webpack.html',
})
],
};
使用html-webpack-plugin时,如果不传东西,它会生成一个默认的html模板,也可以传入一个配置对象
- 使用template指定用到的html模板
- filename,指定打包后的页面名称
更多配置可以参阅: https://github.com/jantimon/html-webpack-plugin#options
配置 webpack-dev-server
webpack-dev-server可以帮我们启动一个本地的服务,并且有实时热更新的能力,之后我们只需要专注于代码的开发,无需在每次修改之后重新去打包,而webpack-dev-serve打包之后的代码不会放在dist文件夹中,只会放在一个缓存区中。
安装依赖
代码语言:javascript复制npm install --save-dev webpack-dev-server
在webpack中加入配置
代码语言:javascript复制{
//...
devServer: {
contentBase: path.join(__dirname, 'dist'),
port: '8080',
host: 'localhost',
},
};
- contentBase:代表打包后的访问资源的路径
除此之外,还有几个常用的配置:
- compress:传入一个 boolean 值,通知 server 是否启用 gzip 强烈建议使用 : 压缩后通常能帮我们减少响应 70% 左右的大小
- hot | hotOnly :传入一个 boolean 值,通知 server 是否启用 HMR。
- open :传入一个 boolean 值 运行之后自动打开默认浏览器
最后我们在package.json加入指令
代码语言:javascript复制"scripts": {
//....
"dev": "webpack-dev-server --mode development"
}
我们就可以通过 npm run dev , 来启动服务,通过localhost:8080来访问打包的资源。
更多配置可以参阅: https://webpack.js.org/configuration/dev-server/
配置模块热替换(HRM)
在使用webpack-dev-server之后,每次改完代码,页面会自动刷新,但有的时候,我们的改动其实很小,并不希望整个页面一起刷新,这个时候可以配置模块热替换。
配置热替换之后,当前端代码变动时,无需刷新整个页面,只把变化的部分替换掉,避免页面状态丢失。
在devServer中加入配置
代码语言:javascript复制devServer: {
//...
hot:true
};
更新packag.json命令
代码语言:javascript复制"scripts": {
//....
"dev": "webpack-dev-server --mode development --open --hot"
},
- --open表示启动后自动打开页面
- --hot表示启动模块热替换
loader配置
接下来,准备编写loader的配置,来支持编写其他类型的文件,首先,我们在webpack.config.js中加入用于编写loader的模块,其中,rules就是放置loader的数组。
代码语言:javascript复制{
//...
module: {
rules: [],
},
}
支持加载css
通过使用不同的 style-loader 和 css-loader, 可以将 css 文件转换成JS文件类型。
首先安装相关依赖
代码语言:javascript复制npm install --save-dev style-loader css-loader
然后在webpack.config.js文件中加入配置
代码语言:javascript复制module: {
rules: [
{
test: /.css/,
use: ['style-loader', 'css-loader'],
exclude: /node_modules/,
include: path.resolve(__dirname, 'src')
}
],
},
loader 可以配置以下参数:
- test: 匹配处理文件的扩展名的正则表达式
- use: loader名称
- include/exclude: 手动指定必须处理的文件夹或屏蔽不需要处理的文件夹
- query: 为loader提供额外的设置选项
解释一下这里两个loader各自的作用,css-loader的作用是识别css文件中的@import语句,以及url链接等,并将css文件中的样式编译为js模块,并导出,而style-loader的作用就是将导出的样式模块以style标签的方式插入DOM树中
支持加载图片
- file-loader: 解决CSS等文件中的引入图片路径问题,不使用的话,样式中的url路径打包后会参照html,而不是css文件。
- url-loader: 当图片小于limit的时候会把图片Base64编码直接打包进项目,大于limit参数的时候还是使用file-loader进行拷贝,这样做可以减少运行时的http请求。
如果希望图片存放在单独的目录下,那么需要指定outputPath
安装依赖:
代码语言:javascript复制npm install --save-dev url-loader file-loader
在 webpack.config.js 中增加 loader 的配置(增加在 module.rules 的数组中) ,在webpack5之后不再需要手动安装图片的loader。
代码语言:javascript复制module: {
rules: [
{
test: /.(gif|jpg|png|bmp|eot|woff|woff2|ttf|svg)/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
outputPath: 'images'
}
}
]
}
]
}
webpack5加载资源的配置
webpack5 新增资源模块(asset module),允许使用资源文件(字体,图标等)而无需配置额外的 loader。
资源模块支持以下四个配置:
- asset/resource 将资源分割为单独的文件,并导出 url,类似之前的 file-loader 的功能.
- asset/inline 将资源导出为 dataUrl 的形式,类似之前的 url-loader 的小于 limit 参数时功能.
- asset/source 将资源导出为源码(source code). 类似的 raw-loader 功能.
- asset 会根据文件大小来选择使用哪种类型,当文件小于 8 KB(默认) 的时候会使用 asset/inline,否则会使用 asset/resource
代码语言:javascript复制加载图片loader使用了use loader的写法,这样写我们可以给loader传递参数,放入配置对象的options属性中,比如在上面的代码中,传入了文件大小和输出路径两个参数。
// ...
module: {
rules: [
// ...
{
test: /.(jpe?g|png|gif)$/i,
type: 'asset',
generator: {
// 输出文件位置以及文件名
// [ext] 自带 "." 这个与 url-loader 配置不同
filename: "[name][hash:8][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 50 * 1024 //超过50kb不转 base64
}
}
},
{
test: /.(woff2?|eot|ttf|otf)(?.*)?$/i,
type: 'asset',
generator: {
// 输出文件位置以及文件名
filename: "[name][hash:8][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 // 超过100kb不转 base64
}
}
},
]
},
支持加载less
在实际项目中,使用less或者sass,会比直接编写css效率更高,这里以less为例。
安装对应的依赖:
代码语言:javascript复制npm install --save-dev less less-loader
在 webpack.config.js 中增加 loader 的配置(module.rules 数组中)。
代码语言:javascript复制module: {
rules: [
{
test: /.less/,
use: ['style-loader', 'css-loader', 'less-loader'],
exclude: /node_modules/,
include: path.resolve(__dirname, 'src')
},
]
}
支持转义 ES6/ES7/JSX
ES6/ES7/JSX 转义需要 Babel 的依赖,支持装饰器。
代码语言:javascript复制npm install --save-dev @babel/core babel-loader @babel/preset-env @babel/preset-react @babel/plugin-proposal-decorators
稍微解释一下每个依赖的含义
- babel-loader相当于联系babel和webpack的桥梁,通过它获取到webpack打包流程中的数据
- @babel/core是bable的核心,babel各类转义方法的来源
- @babel/preset-env 转义 es6
- @babel/preset-react 转义 react
- @babel/plugin-proposal-decorators babel处理装饰器语法的插件
然后在 webpack.config.js 中增加 loader 的配置(module.rules 数组中)。
代码语言:javascript复制module: {
rules: [
{
test: /.jsx?$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/react'],
plugins: [
[require("@babel/plugin-proposal-decorators"), { "legacy": true }]
]
}
}
],
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
},
]
}
这里的presets的字段代表着预设插件,babel可以转义很多种类型,它并不知道当前语句对应的是什么类型,所以我们需要预设几种类型,让它优先使用里面的插件来转义。
至此,我们的项目已经可以编写jsx,css,less等代码了
打包优化
现在我们已经实现了一个react项目打包的流程,但是对打包过程中的代码,还没有进行具体的约束,效率不高,接下来,将对打包流程进行优化。
压缩JS文件
安装依赖:
代码语言:javascript复制npm install --save-dev uglifyjs-webpack-plugin
npm install --save-dev optimize-css-assets-webpack-plugin
在 webpack.config.js 中增加 optimization 的配置,optimization意为优化,后续和优化相关的都会配置在这里
代码语言:javascript复制const UglifyWebpackPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
//other code
optimization: {
minimizer: [
new UglifyWebpackPlugin({
parallel: 4,
uglifyOptions: {
compress: {
drop_console: true,
},
})
]
}
}
- parallel代表同时运行的线程数
- compress代表对代码类型的筛选,drop_console设置为true就代表删除所有的console语句
webpack5已经不需要这么配置了,因为uglifyjs-webpack-plugin在webpack4之后已经不再维护了,webpack5内置了TerserWebpackPlugin插件,具有相同的压缩功能,和删除冗余代码功能。
代码语言:javascript复制new TerserWebpackPlugin({
// 配置移除的语句
terserOptions: {
compress: {
drop_console: true, //传true就是干掉所有的console.*这些函数的调用.
drop_debugger: true, //干掉那些debugger;
// pure_funcs: ['console.log'], // 如果你要干掉特定的函数比如console.info ,又想删掉后保留其参数中的副作用,那用pure_funcs来处理 } }
},
},
}),
分离CSS
因为CSS的下载和JS可以并行,当一个HTML文件很大的时候,可以把CSS单独提取出来加载
代码语言:javascript复制npm install --save-dev mini-css-extract-plugin
在 webpack.config.js 中增加 plugins 的配置,并且将 'style-loader' 修改为 { loader: MiniCssExtractPlugin.loader}。
CSS打包在单独目录,那么配置filename。
代码语言:javascript复制const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
//other code
module: {
rules: [
{
test: /.css/,
use: [{ loader: MiniCssExtractPlugin.loader}, 'css-loader'],
exclude: /node_modules/,
include: path.resolve(__dirname, 'src')
},
{
test: /.less/,
use: [{ loader: MiniCssExtractPlugin.loader }, 'css-loader', 'less-loader'],
exclude: /node_modules/,
include: path.resolve(__dirname, 'src')
},
{
test: /.scss/,
use: [{ loader: MiniCssExtractPlugin.loader }, 'css-loader', 'sass-loader'],
exclude: /node_modules/,
include: path.resolve(__dirname, 'src')
},
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].css'
})
]
}
压缩CSS文件
安装依赖:
代码语言:javascript复制npm install --save-dev optimize-css-assets-webpack-plugin
在 webpack.config.js 中的 optimization 中增加配置
代码语言:javascript复制const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
//other code
optimization: {
minimizer: [
new OptimizeCssAssetsWebpackPlugin()
]
}
}
打包前先清空输出目录
代码语言:javascript复制npm install --save-dev clean-webpack-plugin
在 webpack.config.js 中增加 plugins 的配置
代码语言:javascript复制const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
//other code
plugins: [
new CleanWebpackPlugin()
]
}
至此,已经完成了webpack的基本配置,能够满足项目的日常开发,完整的配置可以戳链接:
git@github.com:AdolescentJou/webpack4-base-demo.git
最后
感谢你能看到这里,希望这篇文章对你有所帮助,后续会陆续更新其他webpack相关的文章,如果能留下你的一个赞,笔者将感激不尽。
参考文章:
https://juejin.cn/post/6944668149849522213
https://github.com/YvetteLau/Blog/issues/1