Webpack是现在最流行的Web构建框架,
本文章的讲解的基本技能内容图如下:
1、webpack基本使用
webapck 是什么
webpack 是一种前端资源构建的工具,一个静态模块打包器(module bundler)。在 webpack 看来,前端的所有资源文件(js / json / css / img / less/...)都会作为模块处理(chunk)。它根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)。
webpack 五个核心
entry
入口(entry)指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。
output
输出(output)指示 webpack打包后的资源 bundles 输出到哪里去,以及如何命名。
loader
loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只能够识别 JavaScript),比如css、less等,在这里可以理解为loader就是一个"翻译官"。
plugins
插件(plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等,在这里可以理解为比loader的功能更加强大。
mode
模式(mode)指示 webpack 使用相应模式的配置,只有两种模式,分别为开发模式(development)和生产模式(production)。
选项 | 描述 | 特点 |
development | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development。启用 NamedChunksPlugin 和NamedModulesPlugin。 | 能让代码本地调试运行的环境 |
production | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。启用 FagDependencyUsagePlugin,FlagIncludedChunksPlugin, ModuleConcatenationPlugin,NoEmitOnErrorsPlugin, OccurrenceOrderPlugin,SideEffectsFlagPlugin 和 TerserPlugin。 | 能让代码优化上线运行的环境 |
webpack 初体验
初始化配置
初始化 package.json文件,输入指令为 npm init。
在package.json加入Script,
"scripts": {
"build": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
},
下载并安装 webpack,输入指令为 npm i webpack webpack-cli -D
编译打包
创建文件:
在src目录下创建一个入口文件index.js。
运行指令:
- 直接在终端运行 webpack,若想让运行环境为开发环境,需要将 mode 改为 development。
功能:webpack 能够编译打包 js 和 json 文件,并且将 ES6 的模块化语法转换成浏览器能识别的语法 - 直接在终端运行 webpack,若想让运行环境为生产环境,需要将 mode 改为 production。
功能:在开发配置功能上多一个功能,就是压缩代码,减小了构建后的代码体积。 结论: - webpack 能够编译打包 js 和 json 文件。
- 能将 ES6 模块化语法转换成浏览器能识别的语法。
- 能压缩代码,减少构建后的代码体积,加快代码运行速度。 问题:
- 不能编译打包 css、img 等文件。
- 不能将 ES6 基本语法转化为 ES5 以下语法
开发环境 webpack.config.js 的相关代码如下
// webpack.config.js文件
// resolve 用来拼接绝对路径的方法
const { resolve } = require('path')
module.exports = {
entry: './src/index.js', // 入口文件 单入口
output:{ //输出配置
filename:'./build.js',// 输出文件名
path: resolve(__dirname,'build') // 输出路径配置 使用绝对路径
},
// loader
module:{
rules: []
},
plugins:[], // plugins 插件
mode: 'development' // 生产环境
}
运行:
npm run build
输出结果:
如果把mode改成production的话,输出:
从中可以看出,mode 的不同,构建后的代码大小也就不同,production 环境下内部会自动打包压缩 js 代码。从而提高在生产环境中打包速度。
2、webpack开发环境配置
打包样式资源
创建文件:
index.css
index.less
在index.js中引入相关的css、less
import './style.css';
import './index.less';
下载安装 loader 包:
npm i css-loader style-loader less-loader less -D
修改 webpack.config.js 配置文件:
const { resolve } = require('path')
module.exports = {
entry: './src/index.js', // 入口文件 单入口
output:{
filename:'./build.js', // 输出文件名
path: resolve(__dirname,'build') // 输出路径 使用绝对路径
},
// loader
module:{
rules: [
//详细loader配置 不同文件必须配置不同的loader处理
{
test: /\.css$/,//正则表达式匹配哪些文件
use: [
// use数组中的执行loader顺序:下到上、右到左
// style-loader作用:创建style标签,将js中的样式资源进行插入,添加daoheader中生效
'style-loader',
// css-loader作用:将css文件变成commonjs模块加载js中,里面内容时样式字符串
'css-loader'
]
},
{ //匹配less样式资源
test: /\.less$/,
use: [
'style-loader',
'css-loader',
// 将less文件编译成css文件
// 需要下载less-loader和less
'less-loader'
]
}
]
},
// plugins 配置
plugins:[
//详细的plugins配置
],
//模式
mode: 'development'
}
运行指令:wepack
打包HTML资源
创建文件:
在src目录下创建 index.html。
下载安装 plugin 包:
npm i html-webpack-plugin -D
const HtmlWebpackPlugin = require('html-webpack-plugin')
....
// plugins 配置
plugins:[
//html-webpack-plugin
//功能:默认会创建一个空的html,自动引入打包输出的所有资源(JS/CSS)
//需求:需要有结构的html文件
new HtmlWebpackPlugin({
//以这个为模板创建html文件,并自动引入输出的所有资源(JS/CSS)
template: './src/index.html'
})
],
....
打包图片资源
创建文件:
在src目录下放入图片
下载 loader 包:
npm i html-loader url-loader file-loader -D
修改 webpack.config.js 配置文件:
打包其他资源
创建文件:加入incon图标等文件
修改 webpack.config.js 配置文件:
module: {
....
{ // 排除 css/js/html 资源
exclude: /\.(css|js|html|less)$/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]'
}
}
....
}
devServer
下载安装包:
npm i webpack-dev-server -D
修改 webpack.config.js 配置文件:
...
// 开发服务器devServer 用来自动化(自动编译、自动打开浏览器、自动刷新浏览器)
// 特点:只会在内存中编译打包、不会有任何输出
devServer: {
// 启动gzip压缩
compress: true,
// 端口号
port: 3000,
open: true,
},
...
运行指令:npm webpack-dev-server
开启devServer 用来自动编译、自动打开浏览器、自动刷新浏览器。
特点:只在内存中编译打包、不会有任何的输出,提高在开发中的效率。
开发环境配置汇总
开发环境配置webpack.config.js的全部代码如下
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.js', // 入口文件 单入口
output:{
filename:'./build.js', // 输出文件名
path: resolve(__dirname,'build') // 输出路径 使用绝对路径
},
// loader
module:{
rules: [
// 详细loader配置 不同文件必须配置不同的loader处理
{
test: /\.css$/,//正则表达式匹配哪些文件
use: [
// use数组中的执行loader顺序:下到上、右到左
// style-loader作用:创建style标签,将js中的样式资源进行插入,添加到header中生效
'style-loader',
// css-loader作用:将css文件变成commonjs模块加载js中,里面内容时样式字符串
'css-loader'
]
},
{ // 匹配less样式资源
test: /\.less$/,
use: [
'style-loader',
'css-loader',
// 将less文件编译成css文件
// 需要下载less-loader和less
'less-loader'
]
},
{ // 处理图片资源
test: /\.(jpg|png|gif)$/,
// 使用一个loader
// 下载url-loader file-loader
loader: 'url-loader',
options: {//对loader进行相关配置
// 图片大小小于8kb,就会被base64进行处理 不会被打包输出
// 优点:减少请求数量(减轻服务器压力)
// 缺点:图片体积会更大(文件请求速度更慢)
limit: 8 * 1024,
// 加载html页面中的图片出现的问题
// 因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
// 解析时会出现:[object Module]
// 解决:关闭url-loader的es6模块化,使用commonjs解析
esModule: false,
// 给图片重命名
// 取hash前十位 ext取扩展名(后缀名)
name: '[hash:10].[ext]'
}
},
{ // 处理html中的图片
test: /\.html$/,
// 处理html文件中引入的img图片(负责引入img,从而能被url-loader进行处理)
loader: 'html-loader'
},
{ // 处理其他资源
// 排除css/js/html/less资源
exclude: /\.(css|less|js|html)$/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]'
}
}
]
},
// plugins 配置
plugins:[
//html-webpack-plugin
//功能:默认会创建一个空的html,自动引入打包输出的所有资源(JS/CSS)
//需求:需要有结构的html文件
new HtmlWebpackPlugin({
//以这个为模板创建html文件,并自动引入输出的所有资源(JS/CSS)
template: './src/index.html'
})
],
//模式
mode: 'development',
devServer: {
// 开启gzip压缩
compress: true,
// 端口号
port: 5000,
// 自动打开浏览器
open: true
}
}
3、webpack生产环境配置
提取 CSS 成单独文件
下载plugin插件:
npm install mini-css-extract-plugin -D
修改 webpack.config.js 配置文件:
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
entry: './src/index.js', // 入口文件 单入口
output:{
filename:'./build.js', // 输出文件名
path: resolve(__dirname,'build') // 输出路径 使用绝对路径
},
// loader
module:{
rules: [
{
test: /\.css$/,//正则表达式匹配哪些文件
use: [
// use数组中的执行loader顺序:下到上、右到左
// 这个 loader取代style-loader 作用:提取css中的css成单独文件
MiniCssExtractPlugin.loader,
// css-loader作用:将css文件变成commonjs模块加载js中,里面内容时样式字符串
'css-loader'
]
}
]
},
plugins:[
//功能:默认会创建一个空的html,自动引入打包输出的所有资源(JS/CSS)
//需求:需要有结构的html文件
new HtmlWebpackPlugin({
//以这个为模板创建html文件,并自动引入输出的所有资源(JS/CSS)
template: './src/index.html'
}),
new MiniCssExtractPlugin({
// 对输出的css文件进行重命名
filename: 'css/main.css'
})
],
//模式
mode: 'production',
}
运行指令:webpack
CSS 兼容性处理
下载loader:
npm install postcss-loader@3 postcss-preset-env@6 -D
修改 webpack.config.js 配置文件:
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
entry: './src/index.js', // 入口文件 单入口
output:{
filename:'./build.js', // 输出文件名
path: resolve(__dirname,'build') // 输出路径 使用绝对路径
},
// loader
module:{
rules: [
// 详细loader配置 不同文件必须配置不同的loader处理
{
test: /\.css$/,//正则表达式匹配哪些文件
use: [
// use数组中的执行loader顺序:下到上、右到左
// 这个 loader取代style-loader 作用:提取css中的css成单独文件
MiniCssExtractPlugin.loader,
// css-loader作用:将css文件变成commonjs模块加载js中,里面内容时样式字符串
'css-loader',
/* css兼容性处理 postcss --> postcss-loader postcss-preset-env
postcss-preset-env 帮助postcss找到package.josn中的browserslist里面的配置
通过加载指定的css兼容性样式
"browserslist": {
开发环境 设置node变量环境:process.env.NODE_ENV = 'development' 默认是production
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
生产环境 :处理兼容性问题的时候默认是生产环境
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
} */
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
// postcss 的插件
require('postcss-preset-env')()
]
}
}
]
}
]
},
// plugins 配置
plugins:[
//html-webpack-plugin
//功能:默认会创建一个空的html,自动引入打包输出的所有资源(JS/CSS)
//需求:需要有结构的html文件
new HtmlWebpackPlugin({
//以这个为模板创建html文件,并自动引入输出的所有资源(JS/CSS)
template: './src/index.html'
}),
new MiniCssExtractPlugin({
// 对输出的css文件进行重命名
filename: 'css/main.css'
})
],
//模式
mode: 'production',
}
修改 package.json 配置文件:
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
}
运行指令:webpack
压缩CSS
下载 plugin 包:
npm install optimize-css-assets-webpack-plugin@5 -D
修改 webpack.config.js 配置文件:
const OptimizeCssAssetsWebpackPlugin= require('optimize-css-assets-webpack-plugin')
....
plugins:[
// 压缩 css代码
new OptimizeCssAssetsWebpackPlugin()
]
....
运行指令:webpack
JS 语法检查
下载 loader 包
npm install eslint-loader@3 eslint@6 eslint-config-airbnb-base@15 eslint-plugin-import@2 -D
修改 webpack.config.js 配置文件:
module: {
...
{
//js语法检查 eslint-loader eslint
/*
注意:只检测自己写的源代码,不需要检查第三方库
设置检查规则:
package.json中eslintConfig中设置
"eslintConfig":{
"extends": "airbnb-base"
}
airbnb-> eslint-config-airbnb-base eslint-plugin-import eslint
*/
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
// 自动修复eslint的错误
fix: true
}
}
...
}
...
修改 package.json 配置文件:
"eslintConfig": {
"extends": "airbnb-base"
}
运行指令: webpack
JS 兼容性处理
下载 loader 包
npm install babel-loader @babel/core @babel/preset-env @babel/polyfill core-js -D
修改 webpack.config.js 配置文件:
.....
module:{
...
{
/*
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'
}
}
]
]
}
}
...
}
....
运行指令:webapck
JS 压缩
修改 webpack.config.js 配置文件:
....
mode:'production'
...
将 mode 的值改为 production,webpack 内部会通过插件自动会压缩 js 代码。
Html 压缩
修改 webpack.config.js 配置文件:
.....
plugins:[
//html-webpack-plugin
//功能:默认会创建一个空的html,自动引入打包输出的所有资源(JS/CSS)
//需求:需要有结构的html文件
new HtmlWebpackPlugin({
//以这个为模板创建html文件,并自动引入输出的所有资源(JS/CSS)
template: './src/index.html',
// 压缩html
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
}),
// 从js中分离css代码
new MiniCssExtractPlugin({
// 对输出的css文件进行重命名
filename: 'css/main.css'
}),
// 压缩 css代码
new OptimizeCssAssetsWebpackPlugin()
],
.....
运行指令:webpack
生产环境配置汇总
生产环境配置webpack.config.js的全部代码如下:
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/index.js', // 入口文件 单入口
output:{
filename:'./build.js', // 输出文件名
path: resolve(__dirname,'build') // 输出路径 使用绝对路径
},
// loader
module:{
rules: [
// 详细loader配置 不同文件必须配置不同的loader处理
{
test: /\.css$/,//正则表达式匹配哪些文件
use: [
// use数组中的执行loader顺序:下到上、右到左
// 这个 loader取代style-loader 作用:提取css中的css成单独文件
MiniCssExtractPlugin.loader,
// css-loader作用:将css文件变成commonjs模块加载js中,里面内容时样式字符串
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
// postcss 的插件
require('postcss-preset-env')()
]
}
}
]
},
{
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'
}
}
]
]
}
},
{
//js语法检查 eslint-loader eslint
/*
注意:只检测自己写的源代码,不需要检查第三方库
设置检查规则:
package.json中eslintConfig中设置
"eslintConfig":{
"extends": "airbnb-base"
}
airbnb-> eslint-config-airbnb-base eslint-plugin-import eslint
*/
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
// 自动修复eslint的错误
fix: true
}
}
]
},
// plugins 配置
plugins:[
//html-webpack-plugin
//功能:默认会创建一个空的html,自动引入打包输出的所有资源(JS/CSS)
//需求:需要有结构的html文件
new HtmlWebpackPlugin({
//以这个为模板创建html文件,并自动引入输出的所有资源(JS/CSS)
template: './src/index.html',
// 压缩html
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
}),
// 从js中分离css代码
new MiniCssExtractPlugin({
// 对输出的css文件进行重命名
filename: 'css/main.css'
}),
// 压缩 css代码
new OptimizeCssAssetsWebpackPlugin()
],
//模式
mode: 'production',
}
4、webpack优化环境配置
HMR
HMR(hot modules replacement)
热模块替换功能会在应用程序运行过程中,替换、添加或删除模块,而无需重新加载整个页面。主要通过以下集中方式,来显著加快开发速度:
- 保留在完全重新加载页面期间丢失的应用程序状态
- 只更新变更内容,以节省宝贵的开放时间
- 在源代码中对 CSS/JS 进行修改,会立刻在浏览器中进行更新,这几乎相当于在浏览器 devtools 直接更改样式。 HMR 有三种:样式文件、JS 文件、HTML 文件。
样式文件:
可以使用 HMR功能,因为style-loader内部实现了,所以在开发环境中一般使用 style-loader,而不使用 MiniCssExtractPlugin.loader 替代 style-loader 分离 CSS,从而加快了开发时的构建代码速度。
JS 文件:
默认没有 HMR 功能,需要修改 js 代码,添加支持 HMR 功能的代码
具体实现代码如下:
//在入口文件 index.js 配置
if (module.hot) {
// 一旦module.hot为true 说明开启了 HMR 功能,HMR 功能生效
module.hot.accept('./hot.js',function() {
add()
})
}
修改 webpack.config.js 配置
...
devServer: {
// 项目构建后路径
contentBase:resolve(__dirname,'build'),
// 开启gzip压缩
compress: true,
// 端口号
port: 5000,
// 自动打开浏览器
open: true,
// 开启热替换
hot: true
}
...
运行指令:npx webpack-dev-server。
HTML文件:
默认是不能使用 HMR 功能,同时会导致 html 不能及时热更新。因为只有一个 html 文件,所以不用做性能优化。解决:修改 entry 入口 将html文件引入,整体会全部刷新。
修改 webpack.config.js 配置
....
entry:['./src/index.js','.src/index.html']
..
source-map
是一种提供源代码到构建后代码映射技术(如果构建后代码出错了,通过映射关系可以追踪到源代码错误的位置)。
修改 webpack.config.js 配置
.....
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true,
port: 3000,
open: true,
hot: true
},
// 添加devtool 即可
devtool: 'eval-source-map'
....
[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
有以下几种搭配:
source-map:map文件在外部
错误代码准确信息 和 源代码的错误位置
1、inline-source-map:map 文件在构建后js代码里面,属于内联
错误代码准确信息 和 源代码的错误位置
2、hidden-source-map:外部
错误代码错误原因,但是没有错误位置
不能追踪源代码,只能提示到构建后代码的错误位置
3、 eval-source-map:内联
每一个文件都生成对应的source-map,都在eval
错误代码准确信息 和源代码的错误位置
4、nosources-source-map:外部
错误代码准确信息,但是没有任何源代码信息
5、 cheap-source-map:外部
错误代码准确信息 和 源代码的错误位置
只能精确到行
6、cheap-module-source-map:外部
错误代码准确信息 和 源代码的错误位置
module会将loader的source map 加入
内联和外部的区别: 1外部生成了文件,内联没有,2、内联构建速度更快。
开发环境: 要求速度快、调试更加友好
速度快:(eval > inline > cheap....)
推荐使用: eval-source-map、eval-cheap-module-source-map
生产环境: 隐藏源代码,不用调试友好。
推荐使用: source-map、cheap-module-source-map
oneOf
一个 loader 只会匹配一个文件,加快了构建代码的速度,相当于一个小的性能优化。
注意:不能有两个 loader (配置)处理同一种类型文件。
修改 webpack.config.js 文件配置
module: {
rules: [
{
//js语法检查 eslint-loader eslint
/*
注意:只检测自己写的源代码,不需要检查第三方库
设置检查规则:
package.json中eslintConfig中设置
"eslintConfig":{
"extends": "airbnb-base"
}
airbnb-> eslint-config-airbnb-base eslint-plugin-import eslint
*/
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
onforce: 'pre' // 优先执行
options: {
// 自动修复eslint的错误
fix: true
}
},
{
oneOf: [
// 详细loader配置 不同文件必须配置不同的loader处理
{
test: /\.css$/,//正则表达式匹配哪些文件
use: [
// use数组中的执行loader顺序:下到上、右到左
// 这个 loader取代style-loader 作用:提取css中的css成单独文件
MiniCssExtractPlugin.loader,
// css-loader作用:将css文件变成commonjs模块加载js中,里面内容时样式字符串
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
// postcss 的插件
require('postcss-preset-env')()
]
}
}
]
},
{
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'
}
}
]
]
}
},
]
}
]
},
缓存
缓存分为两种:babel 缓存 和 文件资源缓存。
JS babel 缓存:
当修改一个文件的时候不希望全部文件重新再次打包,类似于HMR。这个是在 production 环境中进行的,而 HMR 是在development 环境中进行的。
作用:第二次构建打包速度更快。
配置:直接在 js 的 loader 中添加 catchDirectory:true。
修改 webpack.config.js 文件:
....
// js 兼容性处理 babel-loader @babel/core
{
/*
1、基本js兼容性处理--> @babel/preset-env
问题:只能转换基本的语法 不能转换高级语法 比如 primose
2、 全部兼容js处理: 下载@babel/polyfill 只需要在入口文件引入即可
问题:全部都兼容 js体积变大
3、 按需兼容 -- core-js
*/
test: /\.js$/,
exclude: /node_modules/,
use: [
// 开启多进程
// 'thread-loader',
{
loader: 'babel-loader',
options: {
// 预设 通知babel做怎么样的兼容性处理
presets: [
[
'@babel/preset-env',
{
//按需加载
useBuiltIns: 'usage',
// 指定core-js版本
corejs: {
version: 3
},
// 兼容哪些浏览器
targets: {
chrome: '60',
safari: '10',
ie: '9',
firefox: '60',
edge: '17'
}
}
]
],
// 开启babel缓存 第二次构建的时候 会读取之前的缓存
cacheDirectory: true
}
}
]
}
....
文件资源缓存:
- hash 缓存: 每次 webpack 构建时会生成一个唯一的 hash 值。
问题:因为 css 和 js 同时使用一个hash值,如果重新打包会导致所有缓存失效(可能我只修改一个文件)。 - chunkhash 缓存: 根据 chunk 生成的 hash 值。如果打包来源同一个 chunk ,那么 hash 值就一样。 问题:在以上的代码中,因为 css 是在 js 中被引进来的,所有属于同一个 chunk。
- contenthash 缓存: 根据文件内容的不同而生成一个唯一值,相当于一个指纹。
作用: 让线上代码运行缓存,从而提高性能。
修改 webpack.config.js 配置
...
entry: './src/js/index.js',
output: {
filename: 'js/built.[contenthash:10].js',
path: resolve(__dirname, 'build')
},
...
plugins: [
new MiniCssExtractPlugin({
filename: 'css/built.[contenthash:10].css'
}),
new OptimizeCssAssetsWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
})
],
...
tree-shaking
通常用于描述移除 JavaScript 上下文中的未引用代码。去除无用的代码。
使用前提: JS 代码必须使用 ES6 模块化,开启 production 模式。
作用: 减少代码体积。
修改 package.json 配置
"sideEffects":["*.css"]
/*
"sideEffects": false 所有代码都没有副作用(都可以进行tree shaking)
问题:构建的时候可能会把css/ @babel/polyfill (副作用)文件干掉
*/
code split
code split(代码分割):将代码分成不同的包/块,然后可以按需加载,而不是加载包含所有内容的单个包。
1、单入口和多入口: 单入口默认只有一个(bundle)输出文件。多入口有几个文件就输出几个 bundle。
修改 webpack.config.js 配置
....
// 单入口
// entry: './src/js/index.js',
entry: {
// 多入口:有一个入口,最终输出就有一个bundle
index: './src/js/index.js',
test: './src/js/test.js'
},
....
2、多入口
修改 webpack.config.js 配置
....
// 单入口
// entry: './src/js/index.js',
entry: {
// 多入口:有一个入口,最终输出就有一个bundle
index: './src/js/index.js',
test: './src/js/test.js'
},
/*
1. 可以将node_modules中代码单独打包一个chunk最终输出
2. 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk
*/
optimization:{
splitChunks: {
chunks: 'all'
}
},
....
3、单入口: 在入口文件中配置,进行代码分割
index.js 入口文件配置
// 代码分割 单入口
// 通过js代码,让某个文件单独被打包成一个chunk import 动态语法 能将某个文件单独打包 代码分割
// /*webpackChunkName: 'hot'*/ 给分割后的文件命名
import(/* webpackChunkName:'hot'*/'./hot.js')
.then(()=>{
console.log('js文件分割成功');
})
.catch(()=>{
console.log('分割失败');
})
修改 webpack.config.js 配置
...
plugins: [
//html-webpack-plugin
//功能:默认会创建一个空的html,自动引入打包输出的所有资源(JS/CSS)
//需求:需要有结构的html文件
new HtmlWebpackPlugin({
//以这个为模板创建html文件,并自动引入输出的所有资源(JS/CSS)
template: './src/index.html',
// 压缩html
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
}),
// 从js中分离css代码
new MiniCssExtractPlugin({
// 对输出的css文件进行重命名
filename: 'css/main.css'
}),
// 压缩 css代码
new OptimizeCssAssetsWebpackPlugin()
],
//模式
mode: 'production',
// 从node_modules中打包第三方库
optimization:{
splitChunks: {
chunks: 'all'
}
},
...
lazy loading
懒加载或者按需加载,是一种很好的优化网页或应用的方式。这方式实际上是先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或将引用另外一新的代码块。这样加快了应用的初始加载速度,减轻了它的总体积,因为某些代码块可能永远不会被加载。
正常加载: 可以认为是并行加载(同一时间加载多个文件)没有先后顺序,问题:没有用到的 js 也会加载就会浪费性能和构建速度。
JS 文件懒加载: 当需要的时候才加载
JS 文件预加载: 可以理解为等其他资源加载完毕,浏览器在空闲的时间偷偷的加载,问题就是兼容性比较差。
懒加载: 在html中 书写一个按钮 添加事件 当点击按钮的时候再加载对应的 js文件
。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1 id="title">webpack</h1>
<div id="box1"></div>
<div id="box2"></div>
<button id="btn">按钮</button>
</body>
</html>
index.js 文件配置
...
const btn = document.querySelector('#btn')
btn.addEventListener('click', () => {
import(/* webpackChunkName: 'hot'*/'./hot.js').then(({ add }) => {
console.log(add(2, 3))
}).catch(() => {
console.log('加载失败');
})
})
...
预加载:
...
const btn = document.querySelector('#btn')
btn.addEventListener('click', () => {
import(/* webpackChunkName: 'hot',webpackPrefetch: true*/'./hot.js').then(({ add }) => {
console.log(add(2, 3))
}).catch(() => {
console.log('加载失败');
})
})
...
pwa
渐进式网络开发应用程序(离线可访问)。问题:兼容性比较差。
下载plugin安装包
npm install workbox-webpack-plugin@5 -D
修改 webpack.config.js 配置
....
plugins: [
//html-webpack-plugin
//功能:默认会创建一个空的html,自动引入打包输出的所有资源(JS/CSS)
//需求:需要有结构的html文件
new HtmlWebpackPlugin({
//以这个为模板创建html文件,并自动引入输出的所有资源(JS/CSS)
template: './src/index.html',
// 压缩html
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
}),
// 从js中分离css代码
new MiniCssExtractPlugin({
// 对输出的css文件进行重命名
filename: 'css/main.css'
}),
// 压缩 css代码
new OptimizeCssAssetsWebpackPlugin(),
// 离线可访问
new WorkboxWebpackPlugin.GenerateSW({
/*
1、帮助serviceworker 开启启动
2、删除旧的 serviceworker
生成一个serviceworker 配置文件
*/
clientsClaim: true,
skipWaiting: true
})
],
....
在入口文件 index.js 进行配置
// 注册serviceworker 处理兼容性问题
/* 报eslint 错误
"eslintConfig": {
"extends": "airbnb-base",
"browser": true 支持浏览器的全局变量
}
*/
if ('serviceWorker' in navigator) {
window.addEventListener('loade', () => {
// 打包后自动生成一个service-worker.js文件 在这里注册引入即可
navigator.serviceWorker.register('/service-worker.js')
.then(() => {
console.log('注册成功');
})
.catch(() => {
console.log('注册失败');
})
})
}
多进程打包
下载loader安装包
npm install thread-loader@2 -D
webpack.config.js 进行配置
{
/*
1、基本js兼容性处理--> @babel/preset-env
问题:只能转换基本的语法 不能转换高级语法 比如 primose
2、 全部兼容js处理: 下载@babel/polyfill 只需要在入口文件引入即可
问题:全部都兼容 js体积变大
3、 按需兼容 -- core-js
*/
test: /\.js$/,
exclude: /node_modules/,
use: [
// 开启多进程打包
'thread-loader',
{
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'
}
}
]
]
}
}
],
},
注意:
开启多进程打包,进程启动大概为600ms,进程通信也有开销,只有工作消耗时间比较长,才需要多进程打包。
externals
externals(外部扩展),防止将某些 import 的包(package)打包到bundle中,而是在运行时(runtime)再去从外部获取这些扩展依赖。通常通过CDN在构建后的html中进行引入。
在 webpack.config.js 配置
....
externals: {
// 拒绝jQuery被打包进来
jquery: 'jQuery'
}
};
....
dll
DllPlugin 和 DllReferencePlugin 用某种方法实现了拆分 bundles,对第三方库进行打包,同时还大幅度提升了构建的速度。
创建一个 webpack.dll.js 文件
const { resolve } = require("path");
const webpack = require('webpack')
// 使用dll技术 对第三方库进行打包 需要通过webpack --config webpack.dll.js运行这个文件
module.exports = {
entry: {
jquery: ['jquery']
},
output: {
filenam: '[name].js',
path: resolve(__dirname,'dll'),
library:'[name]_[hash]'//打包的第三方库向外暴露的名字
},
plugins:[
// 打包生成一个manifest.json文件 提供和jquery映射
// 利用manifest.json文件通知webpack不需要打包jquery了
new webpack.DllPlugin({
name: '[name].[hash]',// 映射暴露库的名称
path: resolve(__dirname,'dll/manifest.json')
})
],
mode: 'production'
}
使用指令 webpack--config webpack.dll.js
下载 plugin 插件
npm i add-asset-html-webpack-plugin@3 -D
在 webpack.config.js 中再进行相关的配置
....
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
...
plugins: [
...
new webpack.DllReferencePlugin({
manifest:resolve(__dirname,'dll/manifest.json')
}),
// 将某个文件打包输出去,并在html中自动引入打包出去的资源
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname,'dll/jquery.js')
})
...
]
....
运行指令 webpack
可以看到在构建后的build文件夹中有 jquery.js 文件,同时 html 文件中自动引入了 jquery 文件。
开发环境性能优化
优化打包构建速度: HMR
优化代码调试: source-map
生产环境性能优化
优化打包构建速度: oneOf、bebel缓存、多进程打包、externals、dll
优化代码运行的性能: 缓存(hash-chunkhash-contenthash)、tree-shaking、code split、懒加载/预加载、pwa
5、webpack配置详解
entry配置
entry 入口起点有三种:string、array、object。
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
特殊用法:
entry: {
index:['./src/index.js','./src/count.js']
add:'./src/add.js'
}
output配置
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
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'
},
plugins: [new HtmlWebpackPlugin()],
mode: 'development'
};
module配置
module.exports = {
entry: './src/index.js',
output: {
filename: 'js/[name].js',
path: resolve(__dirname, 'build')
},
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: []
}
]
},
plugins: [new HtmlWebpackPlugin()],
mode: 'development'
};
resolve配置
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/[name].js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [new HtmlWebpackPlugin()],
mode: 'development',
// 解析模块的规则
resolve: {
// 配置解析模块路径别名: 优点简写路径 缺点路径没有提示
alias: {
$css: resolve(__dirname, 'src/css')
},
// 配置省略文件路径的后缀名
extensions: ['.js', '.json', '.jsx', '.css'],
// 告诉 webpack 解析模块是去找哪个目录
modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
}
};
devServer配置
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/[name].js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [new HtmlWebpackPlugin()],
mode: 'development',
resolve: {
alias: {
$css: resolve(__dirname, 'src/css')
},
extensions: ['.js', '.json', '.jsx', '.css'],
modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
},
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': ''
}
}
}
}
};
optimization配置
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin')
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'build'),
chunkFilename: 'js/[name].[contenthash:10]_chunk.js'
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [new HtmlWebpackPlugin()],
mode: 'production',
resolve: {
alias: {
$css: resolve(__dirname, 'src/css')
},
extensions: ['.js', '.json', '.jsx', '.css'],
modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
},
optimization: {
splitChunks: {
chunks: 'all'
// 默认值,可以不写~
/* minSize: 30 * 1024, // 分割的chunk最小为30kb
maxSiza: 0, // 最大没有限制
minChunks: 1, // 要提取的chunk最少被引用1次
maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量
maxInitialRequests: 3, // 入口js文件最大并行请求数量
automaticNameDelimiter: '~', // 名称连接符
name: true, // 可以使用命名规则
cacheGroups: {
// 分割chunk的组
// node_modules文件会被打包到 vendors 组的chunk中。--> vendors~xxx.js
// 满足上面的公共规则,如:大小超过30kb,至少被引用一次。
vendors: {
test: /[\\/]node_modules[\\/]/,
// 优先级
priority: -10
},
default: {
// 要提取的chunk最少被引用2次
minChunks: 2,
// 优先级
priority: -20,
// 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块
reuseExistingChunk: true
}
}*/
},
// 将当前模块的记录其他模块的hash单独打包为一个文件 runtime
// 解决:修改a文件导致b文件的contenthash变化 导致缓存失效
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
},
minimizer: [
// 配置生产环境的压缩方案:js和css
new TerserWebpackPlugin({
// 开启缓存
cache: true,
// 开启多进程打包
parallel: true,
// 启动source-map
sourceMap: true
})
]
}
};
作者:青峰10
链接:https://juejin.cn/post/7064771626599972877
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。