文章目录
- 前言
- 第1章:webpack简介
- 第2章:webpack 的初体验
- 第3章:webpack 开发环境的基本配置
- 第 4 章:webpack 生产环境的基本配置
- 第 5 章:webpack 优化配置
- 第 6 章:webpack 配置详情
- 第 7 章:webpack5 介绍和使用
前言
webpack作为现代前端开发中最火的模块打包工具,广泛应用于前端工程领域,是前端工程师必备的技能之一。
webpack4配置,将会无缝切换到webpack5。
第1章:webpack简介
1.1 webpack是什么?
webpack
是一种前端资源构建工具,一个静态模块打包器(module bundler
)。
在 webpack
看来, 前端的所有资源文件(js/json/css/img/less/…)会构成一个依赖树,在依赖树中的每一个JS或其他资源文件节点都是一个module
模块。 它将根据模块的依赖关系进行静态分析,将互相依赖的一条JS资源整合成chunk
块,打包生成对应的静态bundle
资源。
例如:
我们写的less要编译成css,js中的一些ES6、ES7语法编译成浏览器认识的
1.2 webpack 五个核心概念
一般为主文件下index.js
引入依赖module
模块,引进来整合成chunk
(块),再对块各项操作,叫做打包,打包后的资源称为 bundles
1.2.1 Entry
入口(Entry)指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。
指定从哪开始打包
1.2.2 Output
输出(Output)指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。
指定文件的输出
1.2.3 Loader
Loader 让 webpack 能 够 去 处 理 那 些 非 JavaScript 文 件 (webpack 自 身 只 理 解 JavaScript)
翻译less、img等文件给浏览器可以识别
const path = require('path');
// 路径处理方法
function resolve(dir){
return path.join(__dirname, dir);
}
const config = {
//...
module: {
noParse: /jquery|lodash/,
rules: [
{
test: /\.js$/i,
include: resolve('src'),
exclude: /node_modules/,
use: [
'babel-loader',
]
},
// ...
]
}
};
- include:符合条件的模块进行解析
- exclude:排除符合条件的模块,不解析
- exclude 优先级更高
1.2.4 Plugins
插件(Plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩, 一直到重新定义环境中的变量等
比如样式文件的压缩处理
1.2.5 Mode
模式(Mode)指示 webpack 使用相应模式的配置。
选项 | 描述 | 特点 |
---|---|---|
development | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置 为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin。 | 开发模式,打包更加快速,省了代码优化步骤 |
production | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置 为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 TerserPlugin。 | 生产模式,打包比较慢,会开启 tree-shaking 和 压缩代码 |
1.3 占位符
占位符 | 解释 |
---|---|
ext | 文件后缀名 |
name | 文件名 |
path | 文件相对路径 |
folder | 文件所在文件夹 |
hash | 每次构建生成的唯一 hash 值 |
chunkhash | 根据 chunk 生成 hash 值 |
contenthash | 根据文件内容生成hash 值 |
hash
:任何一个文件改动,整个项目的构建 hash 值都会改变;chunkhash
:文件的改动只会影响其所在 chunk 的 hash 值;contenthash
:每个文件都有单独的 hash 值,文件的改动只会影响自身的 hash 值;
第2章:webpack 的初体验
2.1 初始化配置
- 初始化 package.json 输入指令:
npm init
- 下载并安装 webpack 输入指令:
npm install webpack webpack-cli -g //全局安装
npm install webpack webpack-cli -D // -D下载开发依赖
2.2 编译打包应用
开发环境指令:
webpack src/js/index.js -o build/js/built.js --mode=development
功能:webpack 能够编译打包 js 和 json 文件,并且能将 es6 的模块化语法转换成 浏览器能识别的语法。
生产环境指令:
webpack src/js/index.js -o build/js/built.js --mode=production
功能:在开发配置功能上多一个功能,压缩代码。
结论:webpack 能够编译打包 js 和 json 文件。 能将 es6 的模块化语法转换成浏览器能识别的语法。 能压缩代码。
问题:不能编译打包 css、img 等文件。 不能将 js 的 es6 基本语法转化为 es5 以下语法。
第3章:webpack 开发环境的基本配置
3.1 创建配置文件
- 创建文件 webpack.config.js
- 配置内容如下
const { resolve } = require('path'); // node 内置核心模块,用来处理路径问题。
module.exports = {
entry: './src/js/index.js', // 入口文件
output: { // 输出配置
filename: './built.js', // 输出文件名
path: resolve(__dirname, 'build/js') // 输出文件路径配置
},
mode: 'development' //开发环境
}
- 运行指令: webpack
- 结论: 此时功能与上节一致
3.2 打包样式资源
- webpack只能识别js资源
css-loader
来处理 CSS,翻译成js文件。style-loader
来将处理好的 css 通过 style 标签的形式添加到页面上less-loader
将less文件处理成css文件- loader执行顺讯是从下到上,从右到左
npm i css-loader style-loader less-loader less -D
修改配置文件
// resolve 用来拼接绝对路径的方法
const { resolve } = require('path');
module.exports = { // webpack 配置
// 入口起点
entry: './src/index.js',
// 输出
output: {
// 输出文件名
filename: 'built.js',
// 输出路径
// __dirname nodejs 的变量,代表当前文件的目录绝对路径
path: resolve(__dirname, 'build')
},
// loader 的配置
module: {
rules: [
// 详细 loader 配置
// 不同文件必须配置不同 loader 处理
{
// 匹配哪些文件
test: /\.css$/,
// 使用哪些 loader 进行处理
use: [
// use 数组中 loader 执行顺序:从右到左,从下到上 依次执行
// 创建 style 标签,将 js 中的样式资源插入进行,添加到 head 中生效
'style-loader',
// 将 css 文件变成 commonjs 模块加载 js 中,里面内容是样式字符串
'css-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
// 将 less 文件编译成 css 文件
// 需要下载 less-loader 和 less
'less-loader'
]
}
]
},
// plugins 的配置
plugins: [
// 详细 plugins 的配置
],
// 模式
mode: 'development',
// 开发模式
// mode: 'production'
}
需要在入口文件中引入css文件
import './main.css';
3.3 打包html资源
- 通过
html-webpack-plugin
插件来复制html模板,并将打包后的js或css引入html中
下载依赖:npm install html-webpack-plugin -D
配置:
/*
loader: 1. 下载 2. 使用(配置loader)
plugins: 1. 下载 2. 引入 3. 使用
*/
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
// loader的配置
]
},
plugins: [
// plugins的配置
// html-webpack-plugin
// 功能:默认会创建一个空的HTML,自动引入打包输出的所有资源(JS/CSS)
// 需求:需要有结构的HTML文件
new HtmlWebpackPlugin({
// 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS)
template: './src/index.html'
})
],
mode: 'development'
};
需要有结构的HTML文件
new HtmlWebpackPlugin({
// 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS)
template: './src/index.html'
})
3.4 打包图片资源
- 默认使用
url-loader
来处理图片, - url-loader又依赖于
file-loader
npm install --save-dev html-loader url-loader file-loader
修改配置文件
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.less$/,
// 要使用多个loader处理用use
use: ['style-loader', 'css-loader', 'less-loader']
},
{
// 问题:默认处理不了html中img图片
// 处理图片资源
test: /\.(jpg|png|gif)$/,
// 使用一个loader
// 下载 url-loader file-loader
loader: 'url-loader',
// loader的配置
options: {
// 图片大小小于8kb,就会被base64处理
// 优点: 减少请求数量(减轻服务器压力)
// 缺点:图片体积会更大(文件请求速度更慢)
limit: 8 * 1024,
// 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
// 解析时会出问题:[object Module]
// 解决:关闭url-loader的es6模块化,使用commonjs解析
esModule: false,
// 给图片进行重命名
// [hash:10]取图片的hash的前10位
// [ext]取文件原来扩展名
name: '[hash:10].[ext]'
}
},
{
test: /\.html$/,
// 处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
loader: 'html-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development'
};
use里面存数组代表引入多个loader,使用一个loader可以用loader
3.5 打包其他资源
引入字体、icon等文件
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
// 打包其他资源(除了html/js/css资源以外的资源)
{
// 排除css/js/html资源
exclude: /\.(css|js|html|less)$/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]'
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development'
};
3.6 打包前清空上次文件
每次打包的时候,打包目录都会遗留上次打包的文件,为了保持打包目录的纯净,我们需要在打包前将打包目录清空
这里我们可以使用插件 clean-webpack-plugin
来实现
1.安装npm install clean-webpack-plugin -D
2.配置
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 引入插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
// ...
plugins:[ // 配置插件
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin() // 引入插件
]
}
3.6 devServer
devServer
: 用来自动化
(自动编译,自动打开浏览器,自动刷新浏览器)/热部署
/ 解决开发环境跨域
问题- 在
内存中
运行,关闭程序后,不会生成任何东西。
devServer: {
// 运行代码的目录
// contentBase: resolve(__dirname, 'build'), // 已弃用
static: {
directory: path.join(__dirname, 'public'),
},
// 监视 contentBase 目录下的所有文件,一旦文件变化就会 reload
watchContentBase: true,
watchOptions: {
// 忽略文件
ignored: /node_modules/
},
// 启动gzip压缩
compress: true,
// 端口号
port: 5000,
// 域名
host: 'localhost',
// 自动打开浏览器
open: true,
// 开启HMR功能
hot: true,
// 不要显示启动服务器日志信息
clientLogLevel: 'none',
// 除了一些基本启动信息以外,其他内容都不要显示
quiet: true,
// 如果出错了,不要全屏提示~
overlay: false,
// 服务器代理 --> 解决开发环境跨域问题
proxy: {
// 一旦devServer(5000)服务器接受到 /api/xxx 的请求,就会把请求转发到另外一个服务器(3000)
'/api': {
target: 'http://localhost:3000',
// 发送请求时,请求路径重写:将 /api/xxx --> /xxx (去掉/api)
pathRewrite: {
'^/api': ''
}
}
}
}
运行指令: webpack serve
3.7 基本的开发环境配置
/*
开发环境配置:能让代码运行
运行项目指令:
webpack 会将打包结果输出出去
webpack serve 只会在内存中编译打包,没有输出
*/
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
// loader的配置
{
// 处理less资源
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
// 处理css资源
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
// 处理图片资源
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
// 关闭es6模块化
esModule: false,
outputPath: 'imgs'
}
},
{
// 处理html中img资源
test: /\.html$/,
loader: 'html-loader'
},
{
// 处理其他资源
exclude: /\.(html|js|css|less|jpg|png|gif)/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
outputPath: 'media'
}
}
]
},
plugins: [
// plugins的配置
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development',
devServer: {
// contentBase: resolve(__dirname, 'build'), // 已弃用
static: {
directory: path.join(__dirname, 'public'),
},
compress: true,
port: 3000,
open: true
}
};
运行指令: webpack serve
第 4 章:webpack 生产环境的基本配置
4.1 提取 css 成单独文件
- 默认打包会将css文件打包进js中,会使资源文件太大,造成闪屏现象
mini-css-extract-plugin
能提取css为单独的文件- 下载插件
npm install --save-dev mini-css-extract-plugin
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: [
// 创建style标签,将样式放入
// 'style-loader',
// 这个loader取代style-loader。作用:提取js中的css成单独文件
MiniCssExtractPlugin.loader,
// 将css文件整合到js文件中
'css-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
// 对输出的css文件进行重命名
filename: 'css/built.css'
})
],
mode: 'development'
};
4.2 css 兼容性处理
- 使用
postcss-loader
,自动添加 CSS3 部分属性的浏览器前缀 - 安装
npm install postcss postcss-loader postcss-preset-env -D
1.修改webpack
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 设置nodejs环境变量
// process.env.NODE_ENV = 'development';
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
filename: 'css/built.css'
})
],
mode: 'development'
};
2.创建 postcss 配置文件 postcss.config.js
// postcss.config.js
module.exports = {
plugins: [require('postcss-preset-env')]
}
3.创建 postcss-preset-env 配置文件 .browserslistrc
# 换行相当于 and
last 2 versions # 回退两个浏览器版本
> 0.5% # 全球超过0.5%人使用的浏览器,可以通过 caniuse.com 查看不同浏览器不同版本占有率
IE 10 # 兼容IE 10
4.3 压缩 css
optimize-css-assets-webpack-plugin
压缩css插件,减少css文件大小- 下载
npm install --save-dev optimize-css-assets-webpack-plugin
optimize-css-assets-webpack-plugin
在webpack5中已不在友好支持- 使用
css-minimizer-webpack-plugin
为webpack5支持版
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // 引入插件
// const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: [
// 添加loader
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
// postcss的插件
require('postcss-preset-env')()
]
}
}
]
}
]
},
// 优化 webpack4 以后
optimization: {
minimizer: [
new CssMinimizerPlugin(),
],
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
filename: 'css/built.css'
}),
// 压缩css
// new OptimizeCssAssetsWebpackPlugin()
],
mode: 'development'
};
4.4 js 语法检查
eslint-loader
eslint-plugin-import
规范JS代码- 下载安装包
npm install --save-dev eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
/*
语法检查: eslint-loader eslint
注意:只检查自己写的源代码,第三方的库是不用检查的
设置检查规则:
package.json中eslintConfig中设置~
"eslintConfig": {
"extends": "airbnb-base"
}
airbnb --> eslint-config-airbnb-base eslint-plugin-import eslint
*/
{
test: /\.js$/,
// 排除node_modules中的代码
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
// 自动修复eslint的错误
fix: true
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development'
};
配置 package.json
,继承airbnb库中的eslint规则
"eslintConfig": { "extends": "airbnb-base", "env": { "browser": true } }
4.5 js 兼容性处理
babel-loader
处理ES6等兼容性问题babel
还可以将jsx
装换成js
- 下载
npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/polyfill core-js
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
/*
js兼容性处理:babel-loader @babel/core
1. 基本js兼容性处理 --> @babel/preset-env
问题:只能转换基本语法,如promise高级语法不能转换
2. 全部js兼容性处理 --> @babel/polyfill
问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了~
3. 需要做兼容性处理的就做:按需加载 --> core-js
*/
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
// 预设:指示babel做怎么样的兼容性处理
presets: [
[
'@babel/preset-env',
{
// 按需加载
useBuiltIns: 'usage',
// 指定core-js版本
corejs: {
version: 3
},
// 指定兼容性做到哪个版本浏览器
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17'
}
}
]
]
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development'
};
解决方案
1. 基本js兼容性处理 --> @babel/preset-env
问题:只能转换基本语法,如promise高级语法不能转换
2. 全部js兼容性处理 --> @babel/polyfill
问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了~
3. 需要做兼容性处理的就做:按需加载 --> core-js
4.6 js 压缩
- 只需将mode换成
production
,就可以自动压缩js
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
// 生产环境下会自动压缩js代码
mode: 'production'
};
4.7 HTML 压缩
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
// 压缩html代码
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
})
],
mode: 'production'
};
4.8 生产环境配置
const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';
// 复用loader
const commonCssLoader = [
MiniCssExtractPlugin.loader,
'css-loader',
// css兼容性处理
{
// 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [require('postcss-preset-env')()]
}
}
];
// 1. 向外暴露对象
module.exports = {
// 2. 配置入口
entry: './src/js/index.js',
// 3. 配置出口
output: {
// 配置文件名和目录
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader']
},
/*
正常来讲,一个文件只能被一个loader处理。
当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
先执行eslint 在执行babel
*/
{
// 在package.json中eslintConfig --> airbnb
test: /\.js$/,
exclude: /node_modules/,
// 优先执行
enforce: 'pre',
loader: 'eslint-loader',
options: {
fix: true
}
},
// js兼容性处理
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {version: 3},
targets: {
chrome: '60',
firefox: '50'
}
}
]
]
}
},
// 处理图片资源
{
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPath: 'imgs',
esModule: false
}
},
// 处理html中的图片资源
{
test: /\.html$/,
loader: 'html-loader'
},
// 处理其他资源
{
exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
},
plugins: [
// css提取出单独文件
new MiniCssExtractPlugin({
filename: 'css/built.css'
}),
// css压缩处理
new OptimizeCssAssetsWebpackPlugin(),
// html压缩处理
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
})
],
mode: 'production'
};
当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序
第 5 章:webpack 优化配置
5.1 HMR (热模块替换)
- HMR:hot module replacement
热模块替换
/模块热替换
- 作用:在开发环境下,一个模块发生变化,
只会重新打包这一模块
(而不是打包所以模块) 极大提升构建速度 - 样式文件:可以使用HMR功能:因为style-loader内部实现了
- JS文件:默认不能使用HMR功能 --> 需要修改js代码,添加支持HMR功能的代码
- Html文件: 默认不能使用HMR功能.同时会导致问题:html文件不能热更新了。解决:修改entry入口,将html文件引入
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true,
port: 3000,
open: true,
// 开启HMR功能
// 当修改了webpack配置,新配置要想生效,必须重新webpack服务
hot: true
}
5.2 source-map (开发环境)
source-map:一种提供源代码到构建后代码映射技术(如果构建后代码出错了,通过映射可以追踪到源代码错误)
开发环境下调试
devtool: 'eval-source-map'
5.3 oneOf (生产环境)
使loader只会匹配一个
- 修改配置文件
const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';
// 复用loader
const commonCssLoader = [
MiniCssExtractPlugin.loader,
'css-loader',
{
// 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [require('postcss-preset-env')()]
}
}
];
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
// 在package.json中eslintConfig --> airbnb
test: /\.js$/,
exclude: /node_modules/,
// 优先执行
enforce: 'pre',
loader: 'eslint-loader',
options: {
fix: true
}
},
{
// 以下loader只会匹配一个
// 注意:不能有两个配置处理同一种类型文件
oneOf: [
{
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader']
},
/*
正常来讲,一个文件只能被一个loader处理。
当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
先执行eslint 在执行babel
*/
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {version: 3},
targets: {
chrome: '60',
firefox: '50'
}
}
]
]
}
},
{
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPath: 'imgs',
esModule: false
}
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/built.css'
}),
new OptimizeCssAssetsWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
})
],
mode: 'production'
};
5.4 缓存
5.4.1.babel的缓存:
babel
对JS代码进行了一个兼容性的处理,让第二次打包构建速度更快
const config = {
module: {
noParse: /jquery|lodash/,
rules: [
{
test: /\.js$/i,
include: resolve('src'),
exclude: /node_modules/,
use: [
// ...
{
loader: 'babel-loader',
options: {
cacheDirectory: true // 启用缓存
}
},
]
},
// ...
]
}
}
5.4.2 cache-loader
- 缓存一些性能开销比较大的
loader
的处理结果 - 缓存位置:
node_modules/.cache/cache-loader
- 安装
npm i -D cache-loader
const config = {
module: {
// ...
rules: [
{
test: /\.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件
use: [
// 'style-loader',
MiniCssExtractPlugin.loader,
'cache-loader', // 获取前面 loader 转换的结果
'css-loader',
'postcss-loader',
'sass-loader',
]
},
// ...
]
}
}
5.4.3 cache 持久化缓存
通过配置 cache
缓存生成的 webpack
模块和 chunk
,来改善构建速度。
const config = {
cache: {
type: 'filesystem',
},
};
5.4.4 资源的缓存
在资源文件名上使用filename: 'css/built.[contenthash:10].css'
hash
: 每次wepack构建时会生成一个唯一的hash值。
问题: 因为js和css同时使用一个hash值。
如果重新打包,会导致所有缓存失效。(可能我却只改动一个文件)chunkhash
:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样
问题: js和css的hash值还是一样的
因为css是在js中被引入的,所以同属于一个chunkcontenthash
: 根据文件的内容生成hash值。不同文件hash值一定不一样
让代码上线运行缓存更好使用
plugins: [
new MiniCssExtractPlugin({
filename: 'css/built.[contenthash:10].css'
}),
new OptimizeCssAssetsWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
})
],
5.5 Tree-shaking (树摇)
Tree-shaking
:剔除没有使用的代码,以降低包的体积webpack
默认支持,需要在.bablerc
里面设置model:false
,即可在生产环境下默认开启- 利用
ES Module
模块化可以进行静态分析
的特点来检测模块内容的导出、导入以及被使用的情况,保留Live Code
- 消除不会被执行和没有
副作用
(Side Effect) 的 Dead Code,即 DCE 过程 副作用
:函数被调用,同时因为访问了外部数据,尤其是因为对外部数据进行了写操作,从而一定程度地改变了系统环境。
5.6 代码分割
5.6.1 入口点分割
// 单入口
// entry: './src/js/index.js',
entry: {
// 多入口:有一个入口,最终输出就有一个bundle
index: './src/js/index.js',
test: './src/js/test.js'
},
output: {
// [name]:取文件名
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'build')
},
5.6.2 optimization.splitChunk 分包配置
optimization.splitChunks
是基于SplitChunksPlugin
插件实现的- 如果多个文件使用了同一个依赖,解决同一个依赖被多次打包的问题
webpack 将根据以下条件自动拆分 chunks:
- 新的 chunk 可以被共享,或者模块来自于
node_modules
文件夹 - 新的 chunk 体积大于 20kb(在进行 min+gz 之前的体积)
- 当按需加载 chunks 时,并行请求的最大数量小于或等于 30
- 当加载初始化页面时,并发请求的最大数量小于或等于 30
默认配置
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async', // 有效值为 `all`,`async` 和 `initial`
minSize: 20000, // 生成 chunk 的最小体积(≈ 20kb)
minRemainingSize: 0, // 确保拆分后剩余的最小 chunk 体积超过限制来避免大小为零的模块
minChunks: 1, // 拆分前必须共享模块的最小 chunks 数。
maxAsyncRequests: 30, // 最大的按需(异步)加载次数
maxInitialRequests: 30, // 打包后的入口文件加载时,还能同时加载js文件的数量(包括入口文件)
enforceSizeThreshold: 50000,
cacheGroups: { // 配置提取模块的方案
defaultVendors: {
test: /[\/]node_modules[\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
项目配置
const config = {
//...
optimization: {
splitChunks: {
cacheGroups: { // 配置提取模块的方案
default: false,
styles: {
name: 'styles',
test: /\.(s?css|less|sass)$/,
chunks: 'all',
enforce: true,
priority: 10,
},
common: {
name: 'chunk-common',
chunks: 'all',
minChunks: 2,
maxInitialRequests: 5,
minSize: 0,
priority: 1,
enforce: true,
reuseExistingChunk: true,
},
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
priority: 2,
enforce: true,
reuseExistingChunk: true,
},
// ... 根据不同项目再细化拆分内容
},
},
},
}
5.7 lazy loading (懒加载)
通常使用import()
实现JS代码的懒加载,import()
是个语法糖,返回值是一个Promise
对象,意味着这需要异步处理,你可以在.then()
中拿到真正的模块。
lazy loading
(懒加载):当文件需要时才加载prefetch
(预获取):浏览器空闲的时候进行资源的拉取preload
(预加载):提前加载后面会用到的关键资源- 正常加载可以认为是并行加载(同一时间加载多个文件)
// 点击时,才会去加载test.js
document.getElementById('btn').onclick = function() {
import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
console.log(mul(4, 5));
});
};
React中使用懒加载
和代码分割
实现效率提升
const Dashboard = lazy(() => import('components/Dashboard'));
const Settings = lazy(() => import('components/Settings'));
const Configurations = lazy(() => import('components/Configurations'));
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route path="/dashboard">
<Dashboard/>
</Route>
<Route path="/settings">
<Settings/>
</Route>
<Route path="/configuration">
<Configurations/>
</Route>
</Switch>
</Suspense>
5.8 多进程配置
- 优化打包速度,使用
thread-loader
开启多进程打包 - 在小型项目中,开启多进程打包反而会增加时间成本,因为启动进程和进程间通信都会有一定开销。
- 配置在
thread-loader
之后的loader
都会在一个单独的worker
池(worker pool)中运行
1.下载npm install --save-dev thread-loader
const path = require('path');
// 路径处理方法
function resolve(dir){
return path.join(__dirname, dir);
}
const config = {
//...
module: {
noParse: /jquery|lodash/,
rules: [
{
test: /\.js$/i,
include: resolve('src'),
exclude: /node_modules/,
use: [
{
loader: 'thread-loader', // 开启多进程打包
options: {
worker: 3,
}
},
'babel-loader',
]
},
// ...
]
}
};
5.9 优化 resolve(解析) 配置
5.9.1 alias 设置别名
alias
用的创建 import
或 require
的别名,用来简化模块引用,项目中基本都需要进行配置。
const path = require('path')
...
// 路径处理方法
function resolve(dir){
return path.join(__dirname, dir);
}
const config = {
...
resolve:{
// 配置别名
alias: {
'~': resolve('src'),
'@': resolve('src'),
'components': resolve('src/components'),
}
}
};
配置完成之后,我们在项目中就可以
// 使用 src 别名 ~
import '~/fonts/iconfont.css'
// 使用 src 别名 @
import '@/fonts/iconfont.css'
// 使用 components 别名
import footer from "components/footer";
5.9.2 externals
externals
防止将某些库打包
const config = {
//...
externals: {
jquery: 'jQuery',
},
};
从 CDN 引入 jQuery,而不是把它打包
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
5.10 dll (动态链接库)
- 使用dll技术,对某些库(第三方库:jquery、react、vue…)进行单独打包
- 当你运行 webpack 时,默认查找 webpack.config.js 配置文件
- 需要运行 webpack.dll.js 文件
- webpack5已不支持
5.11 webpack性能优化
- 开发环境性能优化
- 优化打包构建速度
HMR(一个模块发生变化,只修改这一个模块) - 优化代码调试
source-map(提供一种源代码到构建后代码的映射技术)
- 优化打包构建速度
- 生产环境性能优化
- 优化打包构建速度
oneOf(使loader只会匹配一个资源)
babel缓存(优化打包构建速度,处理JS兼容性)
多进程打包(提升打包速度)
externals(某些库不打包,通过CDN链接进来)
dll(先把库打包好,后面不打包这个库) - 优化代码运行的性能
缓存(hash:每次打包都会变-chunkhash:同一个chunk相同-contenthash:根据文件内容生成,内容不变就不会变)
tree shaking (去除程序中未使用的代码。前提:必须使用es6,开启生产环境。通过sideEffect注释掉不被去除的代码)
code split(optimization:将node_modules中代码单独打包一个bundle)
懒加载/预加载(懒加载:需要的时候才会加载。预加载:空闲的时候就会加载)
pwa(离线可以访问)
- 优化打包构建速度
第 6 章:webpack 配置详情
6.1 entry
- 起点或是应用程序的起点入口。
entry: 入口起点
1. string --> './src/index.js'
单入口
打包形成一个chunk。 输出一个bundle文件。
此时chunk的名称默认是 main
2. array --> ['./src/index.js', './src/add.js']
多入口
所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
--> 只有在HMR功能中让html热更新生效~
3. object
多入口
有几个入口文件就形成几个chunk,输出几个bundle文件
此时chunk的名称是 key
--> 特殊用法
{
// 所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
index: ['./src/index.js', './src/count.js'],
// 形成一个chunk,输出一个bundle文件。
add: './src/add.js'
}
6.2 output 输出
指示 webpack
如何去输出、以及在哪里输出你的「bundle
、asset
和其他你所打包或使用 webpack 载入的任何内容
output: {
// 文件名称(指定名称+目录)
filename: 'js/[name].js',
// 输出文件目录(将来所有资源输出的公共目录)
path: resolve(__dirname, 'build'),
// 所有资源引入公共路径前缀 --> 'imgs/a.jpg' --> '/imgs/a.jpg'
publicPath: '/',
chunkFilename: 'js/[name]_chunk.js', // 非入口chunk的名称
// library: '[name]', // 整个库向外暴露的变量名
// libraryTarget: 'window' // 变量名添加到哪个上 browser
// libraryTarget: 'global' // 变量名添加到哪个上 node
// libraryTarget: 'commonjs'
},
6.3 module(模块)
- 决定了如何处理项目中的不同类型的模块
module: {
rules: [
// loader的配置
{
test: /\.css$/,
// 多个loader用use
use: ['style-loader', 'css-loader']
},
{
test: /\.js$/,
// 排除node_modules下的js文件
exclude: /node_modules/,
// 只检查 src 下的js文件
include: resolve(__dirname, 'src'),
// 优先执行
enforce: 'pre',
// 延后执行
// enforce: 'post',
// 单个loader用loader
loader: 'eslint-loader',
options: {}
},
{
// 以下配置只会生效一个
oneOf: []
}
]
},
6.4 resolve 解析
- 设置模块如何被解析。
- 用于设置别名、省略文件路径后缀名、解析模块目录设置
// 解析模块的规则
resolve: {
// 配置解析模块路径别名: 优点简写路径 缺点路径没有提示
alias: {
// 配置css中的绝对路径
$css: resolve(__dirname, 'src/css')
},
// 配置省略文件路径的后缀名
extensions: ['.js', '.json', '.jsx', '.css'],
// 告诉 webpack 解析模块是去找哪个目录
modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
}
6.5 devServer 开发服务
- 用于开启开发环境配置
- 开启HMR热部署、解决开发环境跨域问题
devServer: {
// 运行代码的目录
contentBase: resolve(__dirname, 'build'),
// 监视 contentBase 目录下的所有文件,一旦文件变化就会 reload
watchContentBase: true,
watchOptions: {
// 忽略文件
ignored: /node_modules/
},
// 启动gzip压缩
compress: true,
// 端口号
port: 5000,
// 域名
host: 'localhost',
// 自动打开浏览器
open: true,
// 开启HMR功能
hot: true,
// 不要显示启动服务器日志信息
clientLogLevel: 'none',
// 除了一些基本启动信息以外,其他内容都不要显示
quiet: true,
// 如果出错了,不要全屏提示~
overlay: false,
// 服务器代理 --> 解决开发环境跨域问题
proxy: {
// 一旦devServer(5000)服务器接受到 /api/xxx 的请求,就会把请求转发到另外一个服务器(3000)
'/api': {
target: 'http://localhost:3000',
// 发送请求时,请求路径重写:将 /api/xxx --> /xxx (去掉/api)
pathRewrite: {
'^/api': ''
}
}
}
}
6.6 optimization 优化
- 从 webpack 4 开始,会根据你选择的 mode 来执行不同的优化,不过所有的优化还是可以手动配置和重写。
- 常用来配置压缩方案、代码切割
optimization: {
splitChunks: {
chunks: 'all'
},
// 将当前模块的记录其他模块的hash单独打包为一个文件 runtime
// 解决:修改a文件导致b文件的contenthash变化
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
},
minimizer: [
// 配置生产环境的压缩方案:js和css
new TerserWebpackPlugin({
// 开启缓存
cache: true,
// 开启多进程打包
parallel: true,
// 启动source-map
sourceMap: true
})
]
}
6.7 devtool 开发工具
- 此选项控制是否生成,以及如何生成
source map
。
// source-map 映射
devtool: 'eval-source-map'
第 7 章:webpack5 介绍和使用
此版本重点关注以下内容:
- 通过持久缓存提高构建性能.
- 使用更好的算法和默认值来改善长期缓存.
- 通过更好的树摇和代码生成来改善捆绑包大小.
- 清除处于怪异状态的内部结构,同时在 v4 中实现功能而不引入任何重大更改.
- 通过引入重大更改来为将来的功能做准备,以使我们能够尽可能长时间地使用 v5.
下载
- npm i webpack@next webpack-cli -D
自动删除 Node.js Polyfills
早期,webpack 的目标是允许在浏览器中运行大多数 node.js 模块,但是模块格局发生了变化,许多模块用途现在主要是为前端目的而编写的。webpack <= 4 附带了许多 node.js 核心模块的 polyfill,一旦模块使用任何核心模块(即 crypto 模块),这些模块就会自动应用。
尽管这使使用为 node.js 编写的模块变得容易,但它会将这些巨大的 polyfill 添加到包中。在许多情况下,这些 polyfill 是不必要的。
webpack 5 会自动停止填充这些核心模块,并专注于与前端兼容的模块。
迁移:
- 尽可能尝试使用与前端兼容的模块。
- 可以为 node.js 核心模块手动添加一个 polyfill。错误消息将提示如何实现该目标。
Chunk 和模块 ID
添加了用于长期缓存的新算法。在生产模式下默认情况下启用这些功能。
chunkIds: "deterministic", moduleIds: "deterministic"
Chunk ID
你可以不用使用 import(/* webpackChunkName: "name" */ "module")
在开发环境来为 chunk 命名,生产环境还是有必要的
webpack 内部有 chunk 命名规则,不再是以 id(0, 1, 2)命名了
Tree Shaking
- webpack 现在能够处理对嵌套模块的 tree shaking
// inner.js
export const a = 1;
export const b = 2;
// module.js
import * as inner from './inner';
export { inner };
// user.js
import * as module from './module';
console.log(module.inner.a);
在生产环境中, inner 模块暴露的 b
会被删除
- webpack 现在能够多个模块之前的关系
import { something } from './something';
function usingSomething() {
return something;
}
export function test() {
return usingSomething();
}
当设置了"sideEffects": false
时,一旦发现test
方法没有使用,不但删除test
,还会删除"./something"
- webpack 现在能处理对 Commonjs 的 tree shaking
Output
webpack 4 默认只能输出 ES5 代码
webpack 5 开始新增一个属性 output.ecmaVersion, 可以生成 ES5 和 ES6 / ES2015 代码.
如:output.ecmaVersion: 2015
SplitChunk
// webpack4
minSize: 30000;
// webpack5
minSize: {
javascript: 30000,
style: 50000,
}
Caching
// 配置缓存
cache: {
// 磁盘存储
type: "filesystem",
buildDependencies: {
// 当配置修改时,缓存失效
config: [__filename]
}
}
缓存将存储到 node_modules/.cache/webpack
监视输出文件
之前 webpack 总是在第一次构建时输出全部文件,但是监视重新构建时会只更新修改的文件。
此次更新在第一次构建时会找到输出文件看是否有变化,从而决定要不要输出全部文件。
默认值
entry: "./src/index.js
output.path: path.resolve(__dirname, "dist")
output.filename: "[name].js"
更多内容
https://github.com/webpack/changelog-v5