全局下载webpack
命令安装:npm install webpack webpack-cli -g
webpack -v
webpack-cli -v
1、webpack是什么?
官网的定义是:本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具 。
通俗点讲:webpack 是一个模块打包工具( webpack 会将模块、依赖和各种资源打包,生成静态资源文件。)
2、webpack有什么作用?
把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个浏览器可识别的JavaScript文件。
4、webpack 打包流程
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
初始化参数
:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数
开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译
确定入口:根据配置中的 entry 找出所有的入口文件
编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
4、webpack打包原理
首先,我们需要读到入口文件里的内容(也就是index.js的内容)
分析入口文件,递归的去读取模块所依赖的文件内容,生成AST语法树。
根据AST语法树,生成浏览器能够运行的代码
说下用到的几个babel包:
:用于将源码生成AST
:对AST节点进行递归遍历
:将获得的ES6的AST转化成ES5
parser.js中主要就三个方法:
: 将获取到的模块内容 解析成AST语法树
:遍历AST,将用到的依赖收集起来
:把获得的ES6的AST转化成ES5
如果说 Loader
负责文件转换,那么 Plugin
便是负责功能扩展。
webpack 五个核心概念
一、入口( entry )
入口(Entry)指示 webpack以哪个文件为入口起点开始打包,分析构建内部依赖图。
二、输出( output )
输出( output )指示 webpack打包后的资源 bundles 输出到哪里去,以及如何命名。
三、loader 加载器(解释器)
loader 让 webpack能够去处理那些非 JavaScript 文件( webpack自身只理解JavaScript )
四、插件( plugins )
插件( plugins )可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。
五、模式( mode )
模式( mode )指示 webpack使用相应模式的配置。
1. mode: 'development' 开发环境
2. mode: 'production' 生产环境

loader
loader 执行顺序,从右到左,从下到上依次解析
常用的loader:
- 1、style-loader :创建 style 标签,将css样式以style标签的形式插入到html中
- 2、css-loader:用于解析css文件
- 3、less-loader :将 less 文件解析成 css 文件
- 4、url-loader :处理 css 文件中的图片资源(缺点:默认处理不了 html 中的 img 图片(所以我们要使用 html-loader ))
- 5、html-loader :处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
- 6、file-loader :打包其他资源(除 html/css/js/图片 资源以外的资源)
- 7、postcss-loader : 用postcss来处理CSS
- 8、babel-loader : JS兼容性处理 ,将es6语法转为es5
- 9、thread-loader:多进程打包
plugins
常用的 plugins:
- 1、html-webpack-plugin:用于生成一个html文件,并将最终生成的js,css以及一些静态资源文件以script和link的形式动态插入其中
- 2、mini-css-extract-plugin:会将你项目中的css都单独打包,不会内嵌到js bunlde中,这样css和js即可并行加载,而代价就是额外的http请求。
- 3、optimize-css-assets-webpack-plugin:压缩css文件
- 4、eslint-webpack-plugin:JS语法检查(eslint-loader已被官方弃用,现5在使用 eslint-webpack-plugin 插件
) - 5、imagemin-webapack-plugin:顾名思义就是对项目中的图片进行压缩
- 6、compression-webpack-plugin:生产环境时可选择让代码压缩gzip
- 7、terser-webpack-plugin:对js代码压缩(改插件可以开启缓存,开启多线程打包,开启sourceMap,去除console.log等等)
- 8、webpack-bundle-analyzer: 可视化显示打包后文件中个文件的大小
- 9、speed-measure-webpack-plugin:简称 SMP,分析出 Webpack 打包过程中 Loader 和 Plugin 的耗时,有助于找到构建过程中的性能瓶颈。
- 10、size-plugin:每次 打包后,size-plugin 都会输出本次构建的资源体积(gzip 过后),以及上次构建相比体积变化了多少。可以帮助我们监控资源体积的变化。
devServer
devServer基本配置
开发服务器 devServer:用来自动化(自动编译,自动打开游览器,自动刷新游览器)
下载 npm i webpack-dev-server -D
特点:只会在内存中编译打包,不会有任何输出
启动 devServer 指令为:npx webpack serve
devServer: {
// 项目构建后的路径
contentBase: resolve(__dirname,"build/index.html"),
// 启动 gzip 压缩
compress: true,
// 端口号
port: 3000,
// 自动打开游览器
open: true,
// 开启 HMR 功能,新配置要想生效,必须重启 webpack 服务
hot: true,
}
source-map
- source-map 一种 提供源代码到构建后代码映射 技术(如果构建后代码出错了,通过映射可以追踪源代码的错误)
/**
* source-map 一种 提供源代码到构建后代码映射 技术(如果构建后代码出错了,通过映射可以追踪源代码的错误)
*
* [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
*
* 内联 和 外部 的区别:1.外部生成了文件,内联没有 2.内联构建速度更快
* source-map:外部
* 提示:错误代码的准确信息 和源代码的错误位置
* inline-source-map:内联 ==》 只生成一个 source-map
* 提示:错误代码的准确信息 和源代码的错误位置
* hidden-source-map:外部
* 提示:错误代码的错误原因,但是没有错误位置
* 不能追踪源代码的错误,只能提示到构建后代码的错误位置
* eval-source-map:内联 ==》 每一个文件都生成对应的 source-map,都在 eval
* 提示:错误代码的准确信息 和源代码的错误位置
* nosources-source-map:外部
* 提示:错误代码的准确信息,但是没有任何源代码信息
* cheap-source-map:外部
* 提示:错误代码的准确信息 和源代码的错误位置 (只能精确到行)
* cheap-module-source-map:外部
* 提示:错误代码的准确信息 和源代码的错误位置
*
* 开发环境:速度快,调试更友好。 ===》 eval-source-map
* 生产环境:源代码要不要隐藏? 调试要不要更友好? 内联会让代码体积变大,所以在生产环境不用内联 ===》 source-map
*/
devtool: "source-map"//和entry、output同级
resolve
//解析模块的规则
resolve: {
// 配置解析模块路径别名,优点:简写路径,缺点:路径没有提示
alias: {
$css: resolve(__dirname,"src/css"),
},
//配置省略文件路径的后缀名
extensions: [".js",".json",".css"],
// 告诉 webpack 解析模板是去哪个目录
modules: [resolve(__dirname,"../../node_modules"),"node_modules"]
},
webpack性能优化
开发环境性能优化
- 优化打包构建速度
- HMR 热模块替换
- gzip 压缩
- 优化代码调试
- source-map
devServer: {
// 项目构建后的路径
contentBase: resolve(__dirname,"build/index.html"),
// 启动 gzip 压缩
compress: true,
// 端口号
port: 3000,
// 自动打开游览器
open: true,
// 开启 HMR 功能,新配置要想生效,必须重启 webpack 服务
hot: true,
},
devtool: "source-map"//和entry、output同级
开启热模块后,在js文件中需要判断该js模块是否需要开区HMR功能
//一旦 module.hot为true,说明开启了HMR功能。 --> 让HMR功能代码生效
if(module.hot){
//方法会监听print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。
//会执行后面的回调函数
module.hot.accept("./print.js", function(){
print();
})
}
生产环境性能优化
优化打包构建速度
- babel缓存
cacheDirectory: true
{ test: /\.js$/, loader: "babel-loader", options: { 开启babel缓存 第二次构建时会读取之前的缓存 cacheDirectory: true, } },
- 多进程打包
- thread-loader (推荐使用这个)
- parallel-webpack(对ES6支持不好)
- HappyPack(作者已经不维护升级了)
thread-loader 会将您的loader
放置在一个 worker 池里面运行,以达到多线程构建。
/ **
* 开启多进程打包
* 进程启动时间为600ms,进程通信也有开销。只有工作消耗时间比较长。才需要多进程打包
*/
{
loader: "thread-loader",
options: {
works: 2,//进程2个
}
},
- externals
首先在webpack.config.js文件中
externals: {
// 忽略库名 -- npm下载的 包名
jquery: "jQuery",
}
然后在index.html中,通过script方式引入
<!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>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
</head>
<body>
</body>
</html>
- dll
这是一个经常用到的缓存策略,单独配置一个webpack配置,将公共资源打包出来,等我们正式打包发布的时候,直接引用就好了,如此一来经过我们预编译打包的模块就不需要经过webpack打包 https://www.cnblogs.com/tugenhua0707/p/9520780.html
新建一个webpack.dll.js作为我们进行资源分包的webpack配置,以打包jquery模块为例:
DllPlugin的作用:打包生成一个 mainfest.json ====》 提供和 jquery 的映射
const {resolve} = require("path");
const webpack = require("webpack");
/**
* 使用 dll 技术,对某些库(第三方库:jQuery、react、vue...)进行单独打包
* 当你运行webpack 时,默认查找webpack.config.js配置文件
* 需求:需要运行webpack.dll.js文件
* --> webpack --config webpack.dll.js
*/
module.exports = {
entry: {
//最终打包生成的[name] -->jquery
// [ "jquery" ] -->要打包的库是jquery
jquery: ["jquery"],
},
output: {
filename: "[name].js",
path: resolve(__dirname,"dll"),
library: "[name]_[hash]",//打包的库里面向外暴露出去的内容叫什么名字
},
plugins:[
// 打包生成一个 mainfest.json ====》 提供和 jquery 的映射
new webpack.DllPlugin({
name: "[name]_[hash]",//映射的库的暴露的内容名称
path: resolve(__dirname,"dll/mainfest.json")//输出文件路径
})
],
mode: "production"
}
DllReferencePlugin 这个插件是在webpack.config.js中使用的,DllReferencePlugin 的作用是: 告诉 webpack 那些库不参与打包
add-asset-html-webpack-plugin 将某个文件打包输出去。并在html中自动引入该资源
const { resolve } = require("path")
const htmlWebpackPligin = require("html-webpack-plugin")
const AddAssetHtmlWebpackPlugin = require("add-asset-html-webpack-plugin")
const webpack = require("webpack")
module.exports = {
entry: "./src/index.js",
output: {
filename: "built.js",
path: resolve(__dirname,"build")
},
plugins: [
// html-webpack-plugin 打包 html 资源
// 功能:默认会创建一个空的 html,自动引入打包输出的所有资源(JS/CSS)
new htmlWebpackPligin({
// 复制 ./src/index.html 文件,自动引入打包输出的所有资源(JS/CSS)
template: "./src/index.html"
}),
//告诉 webpack 那些库不参与打包,同时使用的名称也得变
new webpack.DllReferencePlugin({
manifest: resolve(__dirname,"dll/mainfest.json")
}),
// 使用add-asset-html-webpack-plugin 将某个文件打包输出去。并在html中自动引入该资源
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname,"dll/jquery.js")
})
],
mode: "production"
}
优化打包体积
- 压缩css
// 功能:压缩css文件
const optimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
plugins:[
new optimizeCssAssetsWebpackPlugin(),
]
- 压缩图片
const ImageminPlugin= require("image-min-plugin");
plugins: [
new ImageminPlugin({
test: /\.(jpe?g|png|gif|svg)$/i,
disable: process.env.NODE_ENV !== 'production', // Disable during development
pngquant: {
quality: '90-100'
},
gifsicle: {
optimizationLevel: 2,
interlaced: true
},
optipng: {
optimizationLevel: 4,
},
jpegtran: {
progressive: true
}
}),
]
- tree shaking
将用到的方法,模块打包,不用到的方法或者模块就不打包
原理
根据DCE的规则去执行,当满足DCE的代码或者模块就不去打包。
在ES6的模块机制下才有效,因为ES6的模块机制是静态的不是动态的,DCE匹配的代码会在uglify阶段被擦除,不会被打包。
DCE规则- 代码不被执行,不会被到达的
- 代码执行结果不会被用到的
- 代码只写不读的
用法
JS–Tree shaking
在webpack4.x以上的版本中,当mode==“pdoduction”,默认开启,即生产模式自动开启,若想在开发环境开启,那么就需要.babelrc里设置modules:false即可
CSS–Tree shaking
Purgecss: 原理—遍历CSS代码、识别已经用到的CSS class,搭配mini-css-extract-plugin(此plugin能将css文件打包成单独的.css文件)
/**
* tree shaking 去除无用代码
* 前提: 1、必须使用ES6模块化 2、开启production环境
* 作用:去除程序中没有使用的代码,减少代码体积
*
* 在package.json中配置
* "sideEffects": false 所有代码都没有副作用(都可以进行tree shaking)
* 问题:可能会把css/@babel/polyfill 当做副作用文件给干掉
* 解决办法: sideEffects": ["*.css","*.less"]
*/
- code split
如果不对webpack打包进行优化,当某个模块被多个入口模块引用时,它就会被打包多次(在最终打包出来的某几个文件里,它们都会有一份相同的代码)。当项目业务越来越复杂,打包出来的代码会非常冗余,文件体积会非常庞大。大体积文件会增加编译时间,影响开发效率;如果直接上线,还会拉长请求和加载时长,影响网站体验。
优化前,打包出来js一共有633KB。(因为a.js和index.js文件中都包含了c.js、jquery.js文件的代码)
优化前
优化后,打包出来js只有330KB。(因为a.js和index.js文件中都包含了c.js、jquery.js文件的代码被抽离出来了)
优化后
optimization: {
splitChunks: {
chunks: "all",
// 一下都是默认值,可以不写~~~~~~~~~~~~~~~~~~~~~~~~~~
minSize: 30 * 1024,//分割的chunk最小为30kb
maxSize: 0,//最大没有限制
minChunks: 1,//要提取的 chunks 最少要被引用1次
maxAsyncRequests: 5,//按需加载时,并行加载的文件的最大数量
maxInitialRequests: 3,//入口JS文件最大并行请求数量
automaticNameDelimiter: "~",//名称连接符
cacheGroups: {//分割chunk的组
// node_modules文件会被打包到vendors组的chunk中 ---> vendors~xxx.js
//并且要满足上面的公共规则
vendors: {
test: /[\\/]node_modules[\\/]/,
name: "vendors",
// 优先级
priority: -10,
},
default: {
name: "common",
//要提取的 chunks 最少要被引用2次
minChunks: 2,
// 优先级
priority: -20,
//如果当前要打包的模块。和之前己经被提取的模块是同一个,就会复用,而不是重新打包模块
reuseExistingChunk: true,
}
}
},
//将当前模块记录其他模块的 hash 单独打包为一个文件 runtime
//解决:修改 optimization打包时 a文件导致b文件的contenthash变化
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
},
},
- JS压缩
1、uglifyjs-webpack-plugin
缺点:
uglifyjs-webpack-plugin 使用的 uglify-es 已不再维护,而uglify-js又不支持es6+,不能识别 const语法、不支持ES6的语法
优点: 老项目支持(兼容 IOS10)
2、webpack-parallel-uglify-plugin
缺点: 老项目不支持(不兼容 IOS10)
优点:
ParallelUglifyPlugin插件则会开启多个子进程,把对多个文件压缩的工作分别给多个子进程去完成,但是每个子进程还是通过UglifyJS去压缩代码。无非就是变成了并行处理该压缩了,并行处理多个子任务,效率会更加的提高。
3、terser-webpack-plugin
缺点: 老项目不支持(不兼容 IOS10)
优点:
- 和ParallelUglifyPlugin一样,并行处理多个子任务,效率会更加的提高。
- webpack4官方推荐,有人维护。
该插件使用 terser 来压缩 JavaScript。
如果你使用的是 webpack v5 或以上版本,你不需要安装这个插件。webpack v5 自带最新的 terser-webpack-plugin。如果使用 webpack v4,则必须安装 terser-webpack-plugin v4 的版本。
const TerserWebpackPlugin = require("terser-webpack-plugin");
optimization: {
splitChunks: {
chunks: "all",
},
minimizer: [
//配置生产环境的压缩方案:js和lcss
new TerserWebpackPlugin({
// 多进程打包
parallel: true,
})
],
},
优化代码运行的性能
- 文件缓存(hash-chunkhash-contenthash)
/**
* 缓存:
* 1、babel缓存
* cacheDirectory: true
* 作用:让第二次打包构建速度更快
* 2、文件资源媛存
* hash:每次wepack构建时会生成一个唯一的hash值。
* 问题:因为js和css同时使用一个hash值。
* 如果重新打包,会导致所有缓存失效。(可能我却只改动一个文件)
* chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样
* 因为css是在js中被引入的,所以同属于一个chunk
* contenthash:根据文件的内容生成hash值。不同文件hash值一定不一样 (常用这个)
* 作用:让代码上线运行缓存更好使用
*/
output: {
filename: "js/built.[contenthash].js",
path: resolve(__dirname,"build")
},
plugins:[
new MiniCssExtractPlugin({
// 对输出的css进行重命名并指定输出路径
filename: "css/built.[contenthash].css"
}),
],
- 懒加载/预加载
document.querySelector("#btn").onclick = function(){
// 懒加载~:当文件需要使用时才加载
// 预加载 webpackPrefetch:会在使用之前,提前加载js文件
// 正常加载可以认为是并行加载(同一时间加载多个文件)
// 预加载 webpackPrefetch :等其他资源加载完毕,游览器空闲了,再偷偷加载资源
import(/* webpackChunkName: 'test', webpackPrefetch: true */"./test").then(({add}) => {
console.log(add(22,33))
})
}
网友评论