共计 7298 个字符,预计需要花费 19 分钟才能阅读完成。
自从 webpack4
当前,官网帮咱们集成了很多个性,比方在生产模式下 代码压缩
主动开启等,这篇文章咱们一起来探讨一下 webpack
给咱们提供的高级个性助力开发。
摸索 webpack 的高级个性
个性:treeShaking
顾名思义 treeShaking
,就是摇树,那么体现在代码模块外面就是摇掉那些没有被内部成员援用的代码,指的留神的是在生产环境下treeShaking
会主动开启。
treeShaking 初体验
比方咱们在代码中引入 lodash
库,咱们只用到了 once
办法,那对于 lodash
其余的功能模块,在生产环境下打包,并不会输入到 bundle.js
文件外面,比方咱们在 bundle.js
外面去找 lodash
的一个办法debounce
,他是齐全能够找失去的。
delelopment 模式下打包的 bundle.js
production 模式下打包的 bundle.js
在这里你可能会说了 production
模式下会开启 n
多插件,解决打包后果,怎么就能阐明是 treeShaking
做的呢,的确这种做法不能阐明是 treeShaking
做的,咱们能够把 mode
设置为 none
再试一下,不过这里须要咱们手动去开启treeShaking
,开启的形式如下。
// webpack.config.js
module.exports = {
...
optimization: {
usedExports: true, // 只导出内部成员援用模块
// 此属性用于模块导入合并,因为独自的模块导入要应用_webpack_require_函数。// 此属性就是能够利用_webpack_require_一次性导入所有模块,也叫作用域晋升。concatenateModules: true,
minimize: true, // 开启代码压缩
}
...
}
none 模式下打包的 bundle.js
所以 none
模式下,打包的后果仍然如此。
扩大
因为 treeShaking
是依赖于 ESM
的,如果我的项目中有配合应用 babel-loader
那么 treeShaking
是不是会生效呢?咱们能够在配置文件外面增加 babel-loader
来辅以测试。
// 装置
npm i babel-loader @babel/core @babel/preset-env -D
// webpack.config.js
module.exports = {
...
module:[
{
test:/\.js$/,
use:{
loader:'babel-loader',
options:{
presets:[['@babel/preset-env']
]
}
}
}
]
}
文件成果
咱们能够看到没有应用的代码,仍然是被移除掉了。
起因剖析
因为 babel-loader
禁用了对 ESM
转化插件,所以通过 babel-loader
解决生成的仍旧是 ESM
代码,如果你想应用代码转换性能,那你就须要像上面这样配置,只不过这样 treeShaking
就会生效了。
// 装置
npm i babel-loader @babel/core @babel/preset-env -D
// webpack.config.js
module.exports = {
...
module:{
rules:[
{
test:/\.js$/,
use:{
loader:'babel-loader',
options:{
presets:[
// 强制应用 commonjs 转换
['@babel/preset-env', {modules: 'commonjs'}]
]
}
}
}
]
}
}
那么 treeShaking
生效了,应该怎么办?不要怕,即便生效了还会有其余插件提供了相似 treeShaking
性能,比方代码压缩。
个性: sideEffect
sideEffect
示意的意思就是副作用,了解起来并不难,比方内部成员援用了以后模块,那么以后模块必定是不会被 treeShaking
的,如果在以后模块外面写了冗余的代码,那么 sideEffect
就是去除这些冗余代码的,以达到更高的提效能力。
sideEffect 的根底实际
这里咱们应该在 webpack.config.js
外面开启 sideEffect
,在package.json
外面指定具备副作用的模块。
// webpack.config.js
module.exports = {
...
optimization: {sideEffect: true}
...
}
// package.json
{"scripts": {},
"sideEffect": [
// 告知 webpack 此文件具备副作用
"./src/app.js",
// * 通配符 css 文件
"*.css"
]
}
个性: CodeSplitting 分包策略
CodeSplitting
分包策略旨在解决单入口打包导致 bundle.js
文件过大,从而导致浏览器 http
加载速度过慢造成页面短暂 白屏
状况,分包策略
具备三种常见施行形式。
- 依据我的项目背景,多入口打包。
- 联合
ESM
的Dynamic import
个性,按需加载模块。 - 对第三方包应用拆包策略。
多入口打包的具体实际
多入口打包体现在多页利用,每一个页面依赖于一个打包文件,对于模块中的公共代码进行提取到公共后果中。参考 webpack 视频解说:进入学习
module.exports = {
entry: {
index: "./src/index.js",
add: "./src/add.js",
},
optimization: {
splitChunks: {
// 主动提取到一个公共的 bundle.js 中
chunks: "all"
}
}
plugins:[
...
new HtmlWebpackPlugin({
filename: 'anout.html',
template: './aout.html',
chunks:['add']
}),
new HtmlWebpackPlugin({
titie: 'title',
template: './index.html',
meta:{viewport: 'width=device-widt, initial-scale=2.0'},
filename: 'index.html',
publicPath: './',
scriptLoading: 'module',
chunks:['index']
})
...
],
...
}
比方在 index.js、add.js
外面抖音用到了 once
办法,webpack
就会提取公共的 lodash
到单的文件外面,在两个页面外面会通过 script
引入。
Dynamic import 的按需加载实际
在选项卡切换场景下,在利用程序运行的过程中,只有当用户点击某个模块,才会对应去加载某个模块,大大的缩小了启动时须要加载模块的体积,升高了浏览器网路的带宽的占用,进步了利用的响应率。
const hash = window.location.hash;
const container = document.getElementById('app');
switch(hash){
case 'title_1':
import('./title_1.js').then({default:title_1}=>{container.appednChild(title_1())
});
break;
case 'title_2':
import('./title_2.js').then({default:title_1}=>{container.appednChild(title_2())
});
break;
case 'title_3':
import('./title_3.js').then({default:title_1}=>{container.appednChild(title_3())
});
break;
default:
}
按需加载
的确不须要在首屏的时候一次性把文件 全副
加载结束,因为首屏并 不须要
所有模块,加载了也是 节约
。
第三方包拆包策略
所谓三方包,在在多入口外面也提到过 optimization.splitChunks
只是一种提取三方包的形式,咱们当初要讲的是插件层面的 DllPlugin
和DllReferencePlugin
,这个插件的意义更为广大一点,比方相似 vue
,react
等三方包,配合着咱们的我的项目代码,只须要首次构建一次,再次构建 webpack
就会跳过这些依赖包,只有咱们不手动降级依赖包,那将会是永久性的缓存。
应用步骤
- 新建
webpck.dll.config.js
文件,写上如下内容。
const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');
module.exports = {
// 入口文件
mode: "development",
entry: {
// 我的项目中用到该两个依赖库文件
lodash: ['lodash'],
},
// 输入文件
output: {
// 文件名称
filename: '[name].dll.js',
// 将输入的文件放到 dist 目录下
path: path.resolve(__dirname, 'vandor'),
/* 寄存相干的 dll 文件的全局变量名称,比方对于 lodash 来说的话就是 _dll_lodash, 在后面加 _dll 是为了避免全局变量抵触。*/
library: '_dll_[name]'
},
plugins: [
// 应用插件 DllPlugin
new DllPlugin({
/* 该插件的 name 属性值须要和 output.library 保留统一,该字段值,也就是输入的 manifest.json 文件中 name 字段的值。比方在 jquery.manifest 文件中有 name: '_dll_jquery' */
name: '_dll_[name]',
/* 生成 manifest 文件输入的地位和文件名称 */
path: path.join(__dirname, 'vandor', '[name].manifest.json')
})
]
};
- 在 package.json 外面新增命令 =>
"dll": "webpack --config webpack.dll.config.js"
并执行,生成文件。
- 引入文件的依赖关系
const webpack = require('webpack');
module.exports = {
plugins:[
...
new webpack.DllReferencePlugin({manifest: require('./vandor/lodash.manifest.json')
}),
...
]
}
个性: 魔法正文
在分包或者定义其余模块的时候,咱们想给模块定义一个名称,那就能够应用如下形式。
/* webpackChunkName:'<chunkName>' */
摸索 webpack 带来的前端性能优化
在前几篇文章外面咱们就晓得了 webpack
通过 mode
来提供了 none
、development
、production
三种预设配置。每一种配置都会选择性的加载某些插件来优化我的项目的构建,然而作为一个开发者咱们该当去关注非主动的性能配置,上面咱们来一起摸索一下在开发中应用到的配置能带来肯定的 性能优化
。
为什么要进行性能优化
性能优化是前端开发的永久性话题,高性能利用的开发这是咱们的指标,然而指标总就是指标,具体实施还是要一步一块板砖,webpack
在实际如此多的新个性的同时,会给咱们的打包后果带来具备影响的内容,比方sourceMap
,上有政策下有对策,那么咱们的种种可优化的点就是解决问题的对策。
具体对策
那么咱们应该怎么样来进步构建速度与打包后果呢?
理论的开发中你总会见到咱们会对不同的环境配置不同的文件,依据 env
的不同来启用不同的配置。
// webpack.development.config.js
module.exports = {
mode:"development",
detool:"source-map"
...
}
// webpack.production.config.js
module.exports = {
mode:"production",
detool:"nosources-source-map"
...
}
// webpack.config.js
module.exports = (env, args) => {
// 公共配置
const config = {module:{},
plugins:[],
...
}
env === "development" ? require('./webpack.development.config.js'):require('./webpack.production.config.js')
return config;
}
-
DefinePlugin
定义全局变量,可用作baseUrl
。... plugin:[ new webpack.DefinePlugin({API_BASEURL:'https://www.yixizhishi.com'}) ] ...
MiniCssExtractplugin
用来从 js 代码中提取 css 代码。
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
...
plugin:[new MiniCssExtractPlugin()
],
module:{
rules:[
{
test:/\.css/,
use:[
// 通过 link 标签引入到页面中
MiniCssExtractPlugin.loader,
cssloader
]
}
]
}
-
optimizeCssAssetsWebpackPlugin
,用来压缩css
代码。webpack
中所谓压缩就是压缩js
文件的,而css
文件,须要咱们独自解决。
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
...
// 非 plugin 中应用
optimization:[
minimizer:[new OptimizeCssAssetsWebpackPlugin()
new terser-webpack-plugin()]
]
-
terser-webpack-plugin
用于压缩js
代码。- 如果在
optimization
选项中开启了minimizer
属性,则会笼罩掉webpack
自身的压缩性能,所以咱们须要手动增加压缩插件。
- 如果在
const terserWebpackPlugin = require('terser-webpack-plugin');
...
optimization:[
minimizer:[
// 压缩 css
new OptimizeCssAssetsWebpackPlugin()
// 压缩 js
new terserWebpackPlugin()]
]
当然还有一些其余的配置呀,比方。
splitChunks
的一些配置呀,也就是按你的需要拆包呀。
splitChunks: {
cacheGroups: {
commons: {
chunks: "initial",// 雷同的 chunks 提出来
minChunks: 2,// 依赖了两个以上的关系
minSize: 0 // 这个依赖最小体积为 0
},
vendor: {
test: /node_modules/,
// 默认选项,示意只有有依赖的第三方包就要拆出去,跟 all 差不多
chunks: "initial",
name: "vendor",
enforce: true
}
},
}
cdn
的引入三方包呀。
module.exports = {
...
// 通过内部引入第三方包
externals:['jQuery','lodash']
...
}
-
多线程
打包的开启呀,比方happyPack
。happyPack
的工作原理就是把loader
加载调配多个线程去解决,最初在对立调度起来,解决实现之后告诉webpack
进行chunks
的组合,输入bundle.js
。 留神: 并不是说多过程打包就肯定好,因为创立多线程的时候也会有性能开销,所以还是斟酌而行。
- 应用
include
防止webpack
解决不须要解决的模块文件,进步编译效率。 - webpack5 提供了 webpack 资源模块,来代替个别的
loader
解决文件,益处是可能解决不同类型的文件并且不再须要针对性的配置loader
。
-
resolve 模块个别被人们忘掉了,不过在 vue/react 的脚手架中还是看见过它的身影,个别用于通知 webpack 以什么样的模式去解决文件,比方。
- 别名:
alias
- 文件类型:
extensions
- 解析的模块范畴:
modules
- 别名:
module.exports = {
resolve: {
alias:{'@':'root/src' // 指定别名 @,通过 @能够找到文件目录},
extensions:{['.jsx', '.tsx', '.vue'] // 指定 webpack 须要解析哪些类型的文件
},
modules:{['node_modules', 'root/src'] // 指定 webpack 须要解析那些范畴的文件
}
}
}
写在最初
因为下面的一些优化伎俩涵盖了 webpack5
以及 webpack5
以前的个性,那么在这里提及一下 webapck5
中开箱即用的个性以及 不再保护
的老版本的个性吧。
- 长久化缓存,应用
cache
之后咱们便不须要应用dll
拆包、cache-loader
了,而且是webpack5
中提供的性能。
module.exports = {
cache: {type: 'filesystem', // 文件系统},
}
thread-loader
开启多线程打包,上述代码中提到了happypack
,不过在webpack5
当中,曾经不再去保护happypack
了,咱们就应该应用thread-loader
来放慢构建过程。
总结
上述解说的内容均是在开发环境下的的配置的一步步实现,当然在 mode:"production"
下webpack
会主动帮咱们做,所以在不依赖他人的状况下,还是本人配比拟好玩。下一章咱们就一起来摸索一下各大成熟框架是怎么配置 webpack
的