性能优化篇---Webpack构建速度优化

开发 前端
web可视化查看构建分析:得到了webpack构建信息文件starts.json,如何进行很好的可视化查看?

如何输出Webpack构建分析

  •  输出Webpack构建信息的.json文件:webpack --profile --json > starts.json 
  1.  --profile:记录构建中的耗时信息
  2.  --json:以json格式输出构建结果,只输出一个json文件(包含所有的构建信息)
  •  web可视化查看构建分析:得到了webpack构建信息文件starts.json,如何进行很好的可视化查看?

        1. 方案一:通过可视化分析工具Webpack Analyse,是个在线Web应用,上传starts.json文件就可以;

        2. 方案二:安装webpack-bundle-analyzer工具npm i -g webpack-bundle-analyzer,生成starts.json后直接在其文件夹目录执行webpack-bundle-analyzer后,浏览器会打开对应网页并展示构建分析文档地址webpack-bundle-analyzer

        3. webpack-dashboard是一款统计和优化webpack日志的工具,可以以表格形势展示日志信息。其中包括构建过程和状态、日志以及涉及的模块列表

        4. jarvis是一款基于webapck-dashboard的webpack性能分析插件,性能分析的结果在浏览器显示,比webpack-bundler-anazlyer更美观清晰GitHub文档地址

  •   npm i -D webpack-jarvis
  •   webpack.config.js配置:   
  1. const Jarvis = require("webpack-jarvis");  
  2.    plugins: [  
  3.      new Jarvis({  
  4.        watchOnly: false,  
  5.        port: 3001 // optional: set a port  
  6.      })  
  7.    ]; 
  •   port:监听的端口,默认1337,监听面板将监听这个端口,通常像http://localhost:port/
  •   host:域名,默认localhost,不限制域名。
  •   watchOnly:仅仅监听编译阶段。默认为true,如果高为false,jarvis不仅仅运行在编译阶段,在编译完成后还保持运行状态。
  •   界面:看到构建时间为:Time: 11593ms(作为优化时间对比)

webpack配置优化

  •  webpack在启动时会从配置的Entry出发,解析出文件中的导入语句,再递归解析。
  •  对于导入语句Webpack会做出以下操作:

        1. 根据导入语句寻找对应的要导入的文件;

        2. 在根据要导入的文件后缀,使用配置中的Loader去处理文件(如使用ES6需要使用babel-loader处理)

  •  针对这两点可以优化查找途径

1. 优化Loader配置

  •   Loader处理文件的转换操作是很耗时的,所以需要让尽可能少的文件被Loader处理  
    1. {    
    2.      test: /\.js$/,    
    3.      use: [    
    4.          'babel-loader?cacheDirectory',//开启转换结果缓存    
    5.      ],    
    6.      include: path.resolve(__dirname, 'src'),//只对src目录中文件采用babel-loader    
    7.      exclude: path.resolve(__dirname,' ./node_modules'),//排除node_modules目录下的文件    
    8.  },  

2. 优化resolve.modules配置

  •   resolve.modules用于配置webpack去哪些目录下寻找第三方模块,默认是['node_modules'],但是,它会先去当前目录的./node_modules查找,没有的话再去../node_modules最后到根目录;
  •   所以当安装的第三方模块都放在项目根目录时,就没有必要安默认的一层一层的查找,直接指明存放的绝对位置   
  1. resolve: {  
  2.             modules: [path.resolve(__dirname, 'node_modules')],  
  3.         } 

3. 优化resolve.extensions配置

  •   在导入没带文件后缀的路径时,webpack会自动带上后缀去尝试询问文件是否存在,而resolve.extensions用于配置尝试后缀列表;默认为extensions:['js','json'];
  •   及当遇到require('./data')时webpack会先尝试寻找data.js,没有再去找data.json;如果列表越长,或者正确的后缀越往后,尝试的次数就会越多
  •   所以在配置时为提升构建优化需遵守:

          1. 频率出现高的文件后缀优先放在前面;

          2. 列表尽可能的小;

          3. 书写导入语句时,尽量写上后缀名

  •   因为项目中用的jsx较多,所以配置extensions: [".jsx",".js"],
  •  基本配置后查看构建速度:Time: 10654ms;配置前为Time: 11593ms 

使用DllPlugin优化

  •  在使用webpack进行打包时候,对于依赖的第三方库,如react,react-dom等这些不会修改的依赖,可以让它和业务代码分开打包;
  •  只要不升级依赖库版本,之后webpack就只需要打包项目业务代码,遇到需要导入的模块在某个动态链接库中时,就直接去其中获取;而不用再去编译第三方库,这样第三方库就只需要打包一次。
  •  接入需要完成的事:

        1. 将依赖的第三方模块抽离,打包到一个个单独的动态链接库中

        2. 当需要导入的模块存在动态链接库中时,让其直接从链接库中获取

        3. 项目依赖的所有动态链接库都需要被加载

  •  接入工具(webpack已内置)

        1. DllPlugin插件:用于打包出一个个单独的动态链接库文件;

        2. DllReferencePlugin:用于在主要的配置文件中引入DllPlugin插件打包好的动态链接库文件

  •  配置webpack_dll.config.js构建动态链接库 
  1. const path = require('path');  
  2. const DllPlugin = require('webpack/lib/DllPlugin');  
  3. module.exports = {  
  4.     mode: 'production',  
  5.     entry: {  
  6.         // 将React相关模块放入一个动态链接库  
  7.         react: ['react','react-dom','react-router-dom','react-loadable'],  
  8.         librarys: ['wangeditor'],  
  9.         utils: ['axios','js-cookie']  
  10.     },  
  11.     output: {  
  12.         filename: '[name]-dll.js',  
  13.         path: path.resolve(__dirname, 'dll'),  
  14.         // 存放动态链接库的全局变量名,加上_dll_防止全局变量冲突  
  15.         library: '_dll_[name]'  
  16.     },  
  17.     // 动态链接库的全局变量名称,需要可output.library中保持一致,也是输出的manifest.json文件中name的字段值  
  18.     // 如react.manifest.json字段中存在"name":"_dll_react"  
  19.     plugins: [  
  20.         new DllPlugin({  
  21.             name: '_dll_[name]',  
  22.             path: path.join(__dirname, 'dll', '[name].manifest.json')  
  23.         })  
  24.     ]  
  •  webpack.pro.config.js中使用 
  1. const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');  
  2. ...  
  3. plugins: [  
  4.     // 告诉webpack使用了哪些动态链接库  
  5.         new DllReferencePlugin({  
  6.             manifest: require('./dll/react.manifest.json')  
  7.         }),  
  8.         new DllReferencePlugin({  
  9.             manifest: require('./dll/librarys.manifest.json')  
  10.         }),  
  11.         new DllReferencePlugin({  
  12.             manifest: require('./dll/utils.manifest.json')  
  13.         }),  
  •  注意:在webpack_dll.config.js文件中,DllPlugin中的name参数必须和output.library中的一致;因为DllPlugin的name参数影响输出的manifest.json的name;而webpack.pro.config.js中的DllReferencePlugin会读取manifest.json的name,将值作为从全局变量中获取动态链接库内容时的全局变量名
  •  执行构建

        1. webpack --progress --colors --config ./webpack.dll.config.js

        2. webpack --progress --colors --config ./webpack.prod.js

  •  html中引入dll.js文件
  •  构建时间对比:["11593ms","10654ms","8334ms"]

HappyPack并行构建优化

  •  核心原理:将webpack中最耗时的loader文件转换操作任务,分解到多个进程中并行处理,从而减少构建时间。
  •  HappyPack
  •  接入HappyPack

        1. 安装:npm i -D happypack

        2. 重新配置rules部分,将loader交给happypack来分配: 

  1. const HappyPack = require('happypack');  
  2. const happyThreadPool = HappyPack.ThreadPool({size: 5}); //构建共享进程池,包含5个进程  
  3. ...  
  4. plugins: [  
  5.     // happypack并行处理  
  6.     new HappyPack({  
  7.         // 用ID来代表当前HappyPack是用来处理一类特定文件的,与rules中的use对应  
  8.         id: 'babel',  
  9.         loaders: ['babel-loader?cacheDirectory'],//默认设置loader处理  
  10.         threadPool: happyThreadPool,//使用共享池处理  
  11.     }),  
  12.     new HappyPack({  
  13.         // 用唯一ID来代表当前HappyPack是用来处理一类特定文件的,与rules中的use对应  
  14.         id: 'css',  
  15.         loaders: [  
  16.             'css-loader',  
  17.             'postcss-loader',  
  18.             'sass-loader'],  
  19.             threadPool: happyThreadPool  
  20.     })  
  21. ],  
  22. module: {  
  23.     rules: [  
  24.     {  
  25.         test: /\.(js|jsx)$/,  
  26.         use: ['happypack/loader?id=babel'],  
  27.         exclude: path.resolve(__dirname,' ./node_modules'),  
  28.     },  
  29.     {  
  30.         test: /\.(scss|css)$/,  
  31.         //使用的mini-css-extract-plugin提取css此处,如果放在上面会出错  
  32.         use: [MiniCssExtractPlugin.loader,'happypack/loader?id=css'],  
  33.         include:[  
  34.             path.resolve(__dirname,'src'),  
  35.             path.join(__dirname, './node_modules/antd')  
  36.         ]  
  37.     },  
  •  参数:

        1. threads:代表开启几个子进程去处理这一类文件,默认是3个;

        2. verbose:是否运行HappyPack输出日志,默认true;

        3. threadPool:代表共享进程池,即多个HappyPack示例使用一个共享进程池中的子进程去处理任务,以防资源占有过多

代码压缩用ParallelUglifyPlugin代替自带的 UglifyJsPlugin插件

  •  自带的JS压缩插件是单线程执行的,而webpack-parallel-uglify-plugin可以并行的执行
  •  配置参数:

        1. uglifyJS: {}:用于压缩 ES5 代码时的配置,Object 类型

        2. test: /.js$/g:使用正则去匹配哪些文件需要被 ParallelUglifyPlugin 压缩,默认是 /.js$/

        3. include: []:使用正则去包含被压缩的文件,默认为 [].

        4. exclude: []: 使用正则去包含不被压缩的文件,默认为 []

        5. cacheDir: '':缓存压缩后的结果,下次遇到一样的输入时直接从缓存中获取压缩后的结果并返回,默认不会缓存,开启缓存设置一个目录路径

        6. workerCount: '':开启几个子进程去并发的执行压缩。默认是当前运行电脑的 CPU 核数减去1

        7. sourceMap: false:是否为压缩后的代码生成对应的Source Map, 默认不生成 

  1. ...  
  2. minimizer: [  
  3.     // webpack:production模式默认有配有js压缩,但是如果这里设置了css压缩,js压缩也要重新设置,因为使用minimizer会自动取消webpack的默认配置  
  4.     new optimizeCssPlugin({  
  5.         assetNameRegExp: /\.css$/g,  
  6.         cssProcessor: require('cssnano'),  
  7.         cssProcessorOptions: { discardComments: { removeAll: true } },  
  8.         canPrint: true  
  9.     }),  
  10.     new ParallelUglifyPlugin({  
  11.         cacheDir: '.cache/',  
  12.         uglifyJS:{  
  13.             output: {  
  14.            // 是否输出可读性较强的代码,即会保留空格和制表符,默认为输出,为了达到更好的压缩效果,可以设置为false  
  15.                 beautify: false,  
  16.         //是否保留代码中的注释,默认为保留,为了达到更好的压缩效果,可以设置为false  
  17.                 comments: false  
  18.             },  
  19.             compress: {  
  20.             //是否在UglifyJS删除没有用到的代码时输出警告信息,默认为输出  
  21.                 warnings: false,  
  22.             //是否删除代码中所有的console语句,默认为不删除,开启后,会删除所有的console语句  
  23.                 drop_console: true,  
  24.             //是否内嵌虽然已经定义了,但是只用到一次的变量,比如将 var x = 1y = x, 转换成 y = 1, 默认为否  
  25.                 collapse_vars: true,  
  26.             }  
  27.         }  
  28. }),  
  •  构建结果对比:["11593ms","10654ms","8334ms","7734ms"]
  •  整体构建速度从12000ms降到现在的8000ms

 “积跬步、行千里”—— 持续更新中~,喜欢的话留下个赞和关注哦!

  •  往期经典好文:
    •   你不知道的CORS跨域资源共享
    •   Koa日志中间件封装开发(log4js)
    •   团队合作必备的Git操作
    •   使用pm2部署node生产环境

 

 

责任编辑:庞桂玉 来源: segmentfault
相关推荐

2019-03-26 10:02:16

WebpackJavascript前端

2020-09-19 21:26:56

webpack

2021-09-06 06:45:06

Webpack优化MindMaster

2021-12-24 08:01:44

Webpack优化打包

2021-11-09 09:57:46

Webpack 前端分包优化

2021-07-05 14:55:28

前端优化图片

2021-09-03 09:44:13

移动端性能优化U-APM

2015-09-16 15:48:55

Android性能优化电量

2015-09-16 14:37:50

Android性能优化运算

2015-09-16 13:54:30

Android性能优化渲染

2021-08-02 10:50:57

性能微服务数据

2015-09-16 15:21:23

Android性能优化内存

2022-05-14 08:35:12

Webpack前端

2018-07-18 12:12:20

Spark大数据代码

2022-09-15 17:31:15

性能优化小程序Taro3

2017-04-18 21:27:01

AndroidAPP构建速度

2024-01-03 08:20:05

Java字符串性能

2019-03-18 15:35:45

WebCSS前端

2017-07-11 15:50:11

前端webpack2优化

2019-03-05 10:20:49

WebWebpack分离数据
点赞
收藏

51CTO技术栈公众号