在讲解性能优化的方案之前,我们需要了解一下webpack的整个工作流程,

方案一:减少模块解析

也就是省略了构建chunk依赖模块的这几个步骤

如果没有loader对该模块进行处理,该模块的源码就是最终打包结果的代码。不对某个模块进行解析,可以缩短构建时间

哪些模块不需要解析?

模块中无其他依赖

webpack配置

配置module.noParse,它是一个正则,被正则匹配到的模块不会解析

module.exports = {
mode: "development",
module: {
noParse: /test/
}
}

方案二:优化loader

1.对于某些库,不使用loader

例如:babel-loader可以转换ES6或更高版本的语法,可是有些库本身就是用ES5语法书写的,不需要转换,使用babel-loader反而会浪费构建时间

通过module.rule.excludemodule.rule.include,排除或仅包含需要应用loader的场景,可以直接排除掉node_modules的所有包,也可以仅排除单独的包

module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,// exclude: /lodash/
//或
// include: /src/,
use: "babel-loader"
}
]
}
}

2.利用cache-loader对模块进行缓存

如果某个文件内容不变,经过相同的loader解析后,解析后的结果也不变,所以可以将loader的解析结果保存下来,让后续的解析直接使用保存的结果

module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: ['cache-loader', 'babel-loader']
},
],
},
};

大家可能会感到疑惑,明明loader是从后往前执行的,那么cache-loader是怎么拿到babel-loader的结果的呢?

其实,每个loader上,还有一个pitch的静态方法

function loader(source){
return `new source`
} loader.pitch = function(filePath){
// 可返回可不返回
// 如果返回,返回源代码
} module.exports = loader;

loader真正执行的顺序是这样的:

loader1.pitch  => loader2.pitch => loader3.pitch  => loader3  => loader2 => loader1

因此,以['cache-loader', 'babel-loader']为例,

第一次打包:

  • 先调用cache-loader.pitch,发现无缓存,往后执行,
  • 调用babel-loader.pitch,也发现无缓存,往后执行,
  • 读取当前需要处理的模块的代码
  • 调用babel-loader,返回修改成es5的代码
  • 调用cache-loader,返回babel-loader处理的结果代码并缓存

第二次打包:

  • 先调用cache-loader.pitch,发现有缓存,则返回源代码
  • 直接返回上次处理好的源代码,不会继续往后走了

当然对于babel-loader,使用它本身的配置也是可以缓存的

module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader?cacheDirectory'
},
],
},
};

3.开启thread-loader

它会把后续的loader放到线程池的线程中运行,以提高构建效率

由于后续的loader会放到新的线程中,所以,后续的loader不能:

  • 使用 webpack api 生成文件 (loader上下文中的emitFile、emitError等api)
  • 无法使用自定义的 plugin api (某些插件提供了自身的plugin和loader,plugin会向webpack注入新的api,loader中会使用)
  • 无法访问 webpack的配置

注意,开启和管理线程需要消耗时间,在小型项目中使用thread-loader反而会增加构建时间

方案三:热替换

热替换并不能降低构建时间(可能还会稍微增加),但可以减少代码改动到效果呈现的时间

// webpack配置
module.exports = {
devServer:{
hot:true // 开启HMR
}
}
// index.js

if(module.hot){ // 是否开启了热更新
module.hot.accept() // 接受热更新
}

方案四:动态链接库

什么情况下使用?

当打包出来的多个bundle.js文件都有重复的第三方代码,会增加文件的体积,不利于传输

打包的过程:

1.使用output.library配置公共模块的全局变量名

// webpack.dll.config.js
module.exports = {
mode: "production",
entry: {
jquery: ["jquery"],
lodash: ["lodash"]
},
output: {
filename: "dll/[name].js",
library: "[name]"// 每个buldle暴露的全局变量名
}
};

打包结果

// dist/dll/lodash
var lodash=function(n){xxx}
// dist/dll/jquery
var jquery=function(n){xxx}

2.用DllPlugin创建资源清单(包含信息:全局变量名、node_modules对应包的路径)

// webpack.dll.config.js
module.exports = {
plugins: [
new webpack.DllPlugin({
path: path.resolve(__dirname, "dll", "[name].manifest.json"), //资源清单的保存位置
name: "[name]"//资源清单中,暴露的变量名
})
]
};

打包生成的资源清单

// dll/lodash.manifest.json
{
"name": "lodash",
"content": {
"./node_modules/lodash/lodash.js": {
xxx
}
}
}
// dll/jquery.manifest.json {
"name": "jquery",
"content": {
"./node_modules/jquery/dist/jquery.js": {
xxx
}
}
}

3.用DllReferencePlugin使用资源清单

在页面中手动引入公共模块

<script src="./dll/jquery.js"></script>
<script src="./dll/lodash.js"></script>

重新设置clean-webpack-plugin

如果使用了插件clean-webpack-plugin,为了避免它把公共模块清除,需要做出以下配置,webpack.config.js配置(注意不是和output.library、DllPlugin在同一个配置文件中哦):

new CleanWebpackPlugin({
// 要清除的文件或目录
// 排除掉dll目录本身和它里面的文件
cleanOnceBeforeBuildPatterns: ["**/*", '!dll', '!dll/*']
})

使用DllReferencePlugin

找到对应的资源清单,根据暴露的变量名(output.library)匹配第三方库在node_modules中的路径,不需要将代码打包到bundle.js中,webpack.config.js:
    new webpack.DllReferencePlugin({
manifest: require("./dll/jquery.manifest.json")
}),
new webpack.DllReferencePlugin({
manifest: require("./dll/lodash.manifest.json")
})

打包过程:首先要根据webpack.dll.config.js配置文件打包一次,之后再根据webpack.config.js打包

最终打包结果的格式:

(function(modules){
//...
})({
// index.js文件的打包结果并没有变化
"./src/index.js":
function(module, exports, __webpack_require__){
var $ = __webpack_require__("./node_modules/jquery/index.js")
var _ = __webpack_require__("./node_modules/lodash/index.js")
_.isArray($(".red"));
},
// 由于资源清单中存在,jquery的代码并不会出现在这里
"./node_modules/jquery/index.js":
function(module, exports, __webpack_require__){
module.exports = jquery;
},
// 由于资源清单中存在,lodash的代码并不会出现在这里
"./node_modules/lodash/index.js":
function(module, exports, __webpack_require__){
module.exports = lodash;
}
})

优点:

  • 极大提升自身模块的打包速度
  • 极大的缩小了自身文件体积
  • 有利于浏览器缓存第三方库的公共代码

缺点:

  • 使用非常繁琐
  • 如果第三方库中包含重复代码,则效果不太理想

方案五:抽离公共代码

有多个模块都引用了公共模块,当一个模块加载时,访问了公共模块,并缓存下来,另一个模块加载就可以直接使用缓存的结果。

module.exports = {
optimization: {
splitChunks: {
// 分包策略
chunks: "all",
cacheGroups: {
// 公共模块
common: {
mixSize: 0,
minChunks: 2, // 至少被几个文件引用
},
vendors: {
test: /[\\/]node_modules[\\/]/, // 当匹配到相应模块时,将这些模块进行单独打包
priority: 1 // 缓存组优先级,优先级越高,该策略越先进行处理,默认值为0
},
}
}
}
}

还可以抽离公共样式,使用MiniCssExtractPlugin

module.exports = {
optimization: {
splitChunks: {
chunks: "all",
cacheGroups: {
styles: {
test: /\.css$/, // 匹配样式模块
minSize: 0, // 覆盖默认的最小尺寸,这里仅仅是作为测试
minChunks: 2 // 覆盖默认的最小chunk引用数
}
}
}
},
module: {
rules: [{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] }]
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: "[name].[hash:5].css",
// chunkFilename是配置来自于分割chunk的文件名
chunkFilename: "common.[hash:5].css"
})
]
}

通过cdn方式引入js、css文件,将不怎么需要更新的第三方库脱离webpack打包,不被打入bundle中,从而减少打包时间,但又不影响运用第三方库的方式,例如import方式等。

const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');

module.exports = {
plugins: [
new HtmlWebpackExternalsPlugin({
externals: [{
module: 'vue',
entry: 'https://lib.baomitu.com/vue/2.6.12/vue.min.js',
global: 'Vue'
}]
})
],
}

最后看到在dist/index.html中动态添加了如下代码:

<script type="text/javascript" src="https://lib.baomitu.com/vue/2.6.12/vue.min.js"></script>

方案六:代码压缩

压缩js和css代码:

const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
optimization: {
// 是否要启用压缩,默认情况下,生产环境会自动开启
minimize: true,
minimizer: [ // 压缩时使用的插件
// 压缩js文件
new TerserPlugin({
parallel: true // 开启多线程压缩
}),
// 压缩css文件
new OptimizeCSSAssetsPlugin()
],
},
};

使用compression-webpack-plugin插件对打包结果进行预压缩,可以移除服务器的压缩时间

    new CmpressionWebpackPlugin({
test: /\.js/,
minRatio: 0.5
})

webpack之性能优化(webpack4)的更多相关文章

  1. webpack 打包性能优化

    webpack 打包性能优化 开启多线程打包 thread-loader https://www.npmjs.com/package/thread-loader https://github.com/ ...

  2. webpack打包性能优化

    1. 使用 gzip 压缩打包后的 js 文件 这个方法优化浏览器下载时的文件大小(打包后的文件大小没有改变) webpack.config.prod.js 中 var CompressionWebp ...

  3. Web 性能优化: 使用 Webpack 分离数据的正确方法

    摘要: Webpack骚操作. 原文:Web 性能优化: 使用 Webpack 分离数据的正确方法 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 制定向用户提供文件的最佳方式可能是一 ...

  4. 浅谈webpack4.0 性能优化(转)

    前言:在现实项目中,我们可能很少需要从头开始去配置一个webpack 项目,特别是webpack4.0发布以后,零配置启动一个项目成为一种标配.正因为零配置的webpack对项目本身提供的“打包”和“ ...

  5. 【译文】使用webpack提高网页性能优化

    这篇文章原文来自https://developers.google.com/web/fundamentals/performance/webpack/. 说是译文其实更像是笔者做的笔记,如有错误之处请 ...

  6. webpack 性能优化 dll 分包

    webpack 性能优化 dll 分包 html-webpack-externals-plugin DLLPlugin https://www.webpackjs.com/configuration/ ...

  7. 【前端构建】WebPack实例与前端性能优化

    计划把微信的文章也搬一份上来. 这篇主要介绍一下我在玩Webpack过程中的心得.通过实例介绍WebPack的安装,插件使用及加载策略.感受构建工具给前端优化工作带来的便利. 壹 | Fisrt 曾几 ...

  8. WebPack实例与前端性能优化

    [前端构建]WebPack实例与前端性能优化   计划把微信的文章也搬一份上来. 这篇主要介绍一下我在玩Webpack过程中的心得.通过实例介绍WebPack的安装,插件使用及加载策略.感受构建工具给 ...

  9. webpack性能优化——DLL

    Webpack性能优化的方式有很多种,本文之所以将 dll 单独讲解,是因为 dll 是一种最简单粗暴并且极其有效的优化方式. 在通常的打包过程中,你所引用的诸如:jquery.bootstrap.r ...

  10. 优化Webpack构建性能的几点建议

    Webpack 作为目前最流行的前端构建工具之一,在 vue/react 等 Framework 的生态圈中都占据重要地位.在开发现代 Web 应用的过程中,Webpack 和我们的开发过程和发布过程 ...

随机推荐

  1. python max()用法

    起因是看到一道面试题 "统计字符串中出现次数最多的字符,并返回出现次数" 问题很简单,刚开始没思路,只想到了循环统计,但是觉得太蠢了,直到我发现了max()的key用法,果然还是我 ...

  2. MySQL 利用时间(秒分时日月年)分组统计

    1.统计 七天 前 人数 select count(*) from your_table where last_login_time> date_sub(date(now()), interva ...

  3. jmeter非GUI模式压测并生成测试报告

    关于jmeter非GUI模式压测并生成测试报告 1.脚本调通后,在DOS命令栏进入脚本存在的位置 如果不想通过DOS进入脚本路径,则可以直接指定执行路径,命令如下: JMeter默认去当前目录寻找脚本 ...

  4. (linux笔记)开放防火墙端口

    关闭防火墙 CentOS 7.RedHat 7 之前的 Linux 发行版防火墙开启和关闭( iptables ): 即时生效,重启失效 #开启 service iptables start #关闭 ...

  5. HCIA-ICT实战基础10-广域网技术PPP

    HCIA-ICT实战基础-广域网技术PPP 目录 早期广域网技术概述 PPP协议原理与配置 1 早期广域网技术概述 1.1 什么是广域网 广域网是连接不同地区局域网的网络, 通常所覆盖的范围从几十公里 ...

  6. shell_Day02

    虽然差了不少天,但的确是第......一天 history 查看历史命令记录 !命令序号 查看命令并执行 -c 清空 关于命令历史的文件 关于命令历史的变量(环境变量) 命令补全 tab:制表符 \t ...

  7. [Unity3D 小Tricks] 如何修改Unity3d脚本默认模板?

    众所周知,unity默认的模板总是Update()和Start(),但往往我们并不需要,每次都要手动删除非常麻烦. 但可以更改如下模板文件 C:\Program Files\Unity 2020.3. ...

  8. STM32cubemx-HAL库串口断线问题

    STM32cubemx:version5.1 Chip: STM32F446RE IDE:Keil5 Q:小项目上写了个简单的通信包,波特率230400,数据量较大1600Byte/s,DMA的方式实 ...

  9. 项目实训 day15-16

    第一天我与灿哲沟通,我弄明白了真正的网络结构且如何运行的,自己记了下网络草图,开始初步用PlotNN绘制 第二天我发现pycore库表达能力不够,于是参考其他用tex写的例子,写了几个方法,最终能生成 ...

  10. react+antd upload实现图片宽高、视频宽高尺寸校验

    图片宽高校验方法: // 上传图片尺寸限制 const checkIconWH = (file: any) => { return new Promise<void>(functio ...