性能优化篇之Webpack构建速度优化的建议
如何输出Webpack构建分析
输出Webpack构建信息的.json文件:webpack --profile --json > stats.json
--profile
:记录构建中的耗时信息--json
:以json格式输出构建结果,最后只输出一个json文件(包含所有的构建信息)
web可视化查看构建分析:得到了webpack构建信息文件stats.json,如何进行很好的可视化查看?
- 方案一:通过可视化分析工具Webpack Analyse,是个在线Web应用,上传stats.json文件就可以;不过好像需要翻墙;
- 方案二:安装
webpack-bundle-analyzer
工具npm i -g webpack-bundle-analyzer
,生成stats.json后直接在其文件夹目录执行webpack-bundle-analyzer
后,浏览器会打开对应网页并展示构建分析webpack-bundle-analyzer stats.json -p 8888
文档地址webpack-bundle-analyzer - webpack-dashboard是一款统计和优化webpack日志的工具,可以以表格形势展示日志信息。其中包括构建过程和状态、日志以及涉及的模块列表
- jarvis是一款基于webapck-dashboard的webpack性能分析插件,性能分析的结果在浏览器显示,比webpack-bundler-anazlyer更美观清晰GitHub文档地址
npm i -D webpack-jarvis
webpack.config.js
配置:
const Jarvis = require("webpack-jarvis"); plugins: [ new Jarvis({ watchOnly: false, port: 3001 // optional: set a port }) ];
port:监听的端口,默认1337,监听面板将监听这个端口,通常像http://localhost:port/
host:域名,默认localhost,不限制域名。
watchOnly:仅仅监听编译阶段。默认为true,如果高为false,jarvis不仅仅运行在编译阶段,在编译完成后还保持运行状态。
界面:看到构建时间为:Time: 11593ms
(作为优化时间对比)
webpack配置优化
webpack在启动时会从配置的Entry出发,解析出文件中的导入语句,再递归解析。
对于导入语句Webpack会做出以下操作:
- 根据导入语句寻找对应的要导入的文件;
- 在根据要导入的文件后缀,使用配置中的Loader去处理文件(如使用ES6需要使用babel-loader处理)
针对这两点可以优化查找途径
1、优化Loader配置
Loader处理文件的转换操作是很耗时的,所以需要让尽可能少的文件被Loader处理
{ test: /\.js$/, use: [ 'babel-loader"htmlcode">resolve: { modules: [path.resolve(__dirname, 'node_modules')], }3、优化resolve.extensions配置
在导入没带文件后缀的路径时,webpack会自动带上后缀去尝试询问文件是否存在,而
resolve.extensions
用于配置尝试后缀列表;默认为extensions:['js','json']
;及当遇到
require('./data')
时webpack会先尝试寻找data.js
,没有再去找data.json
;如果列表越长,或者正确的后缀越往后,尝试的次数就会越多;所以在配置时为提升构建优化需遵守:
- 频率出现高的文件后缀优先放在前面;
- 列表尽可能的小;
- 书写导入语句时,尽量写上后缀名
因为项目中用的
jsx
较多,所以配置extensions: [".jsx",".js"],
基本配置后查看构建速度:
Time: 10654ms
;配置前为Time: 11593ms
使用DllPlugin优化
在使用webpack进行打包时候,对于依赖的第三方库,如react,react-dom等这些不会修改的依赖,可以让它和业务代码分开打包;
只要不升级依赖库版本,之后webpack就只需要打包项目业务代码,遇到需要导入的模块在某个动态链接库中时,就直接去其中获取;而不用再去编译第三方库,这样第三方库就只需要打包一次。
接入需要完成的事:
- 将依赖的第三方模块抽离,打包到一个个单独的动态链接库中
- 当需要导入的模块存在动态链接库中时,让其直接从链接库中获取
- 项目依赖的所有动态链接库都需要被加载
接入工具(webpack已内置)
DllPlugin插件:用于打包出一个个单独的动态链接库文件;
DllReferencePlugin:用于在主要的配置文件中引入
DllPlugin
插件打包好的动态链接库文件配置webpack_dll.config.js构建动态链接库
const path = require('path'); const DllPlugin = require('webpack/lib/DllPlugin'); module.exports = { mode: 'production', entry: { // 将React相关模块放入一个动态链接库 react: ['react','react-dom','react-router-dom','react-loadable'], librarys: ['wangeditor'], utils: ['axios','js-cookie'] }, output: { filename: '[name]-dll.js', path: path.resolve(__dirname, 'dll'), // 存放动态链接库的全局变量名,加上_dll_防止全局变量冲突 library: '_dll_[name]' }, // 动态链接库的全局变量名称,需要可output.library中保持一致,也是输出的manifest.json文件中name的字段值 // 如react.manifest.json字段中存在"name":"_dll_react" plugins: [ new DllPlugin({ name: '_dll_[name]', path: path.join(__dirname, 'dll', '[name].manifest.json') }) ] }webpack.pro.config.js中使用
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin'); ... plugins: [ // 告诉webpack使用了哪些动态链接库 new DllReferencePlugin({ manifest: require('./dll/react.manifest.json') }), new DllReferencePlugin({ manifest: require('./dll/librarys.manifest.json') }), new DllReferencePlugin({ manifest: require('./dll/utils.manifest.json') }), ]注意:在
webpack_dll.config.js
文件中,DllPlugin
中的name参数必须和output.library
中的一致;因为DllPlugin
的name参数影响输出的manifest.json的name;而webpack.pro.config.js中的DllReferencePlugin
会读取manifest.json
的name,将值作为从全局变量中获取动态链接库内容时的全局变量名执行构建
webpack --progress --colors --config ./webpack.dll.config.js
webpack --progress --colors --config ./webpack.prod.js
html中引入dll.js文件
构建时间对比:
["11593ms","10654ms","8334ms"]
HappyPack并行构建优化
核心原理:将webpack中最耗时的loader文件转换操作任务,分解到多个进程中并行处理,从而减少构建时间。
HappyPack
接入HappyPack
安装:
npm i -D happypack
重新配置
rules
部分,将loader
交给happypack
来分配:const HappyPack = require('happypack'); const happyThreadPool = HappyPack.ThreadPool({size: 5}); //构建共享进程池,包含5个进程 ... plugins: [ // happypack并行处理 new HappyPack({ // 用唯一ID来代表当前HappyPack是用来处理一类特定文件的,与rules中的use对应 id: 'babel', loaders: ['babel-loader"_blank" href="https://github.com/gdborton/webpack-parallel-uglify-plugin" rel="external nofollow" >webpack-parallel-uglify-plugin可以并行的执行配置参数:
uglifyJS: {}
:用于压缩 ES5 代码时的配置,Object 类型test: /.js$/g
:使用正则去匹配哪些文件需要被 ParallelUglifyPlugin 压缩,默认是 /.js$/include: []
:使用正则去包含被压缩的文件,默认为 [].exclude: []
: 使用正则去包含不被压缩的文件,默认为 []cacheDir: ''
:缓存压缩后的结果,下次遇到一样的输入时直接从缓存中获取压缩后的结果并返回,默认不会缓存,开启缓存设置一个目录路径workerCount: ''
:开启几个子进程去并发的执行压缩。默认是当前运行电脑的 CPU 核数减去1sourceMap: false
:是否为压缩后的代码生成对应的Source Map, 默认不生成... minimizer: [ // webpack:production模式默认有配有js压缩,但是如果这里设置了css压缩,js压缩也要重新设置,因为使用minimizer会自动取消webpack的默认配置 new optimizeCssPlugin({ assetNameRegExp: /\.css$/g, cssProcessor: require('cssnano'), cssProcessorOptions: { discardComments: { removeAll: true } }, canPrint: true }), new ParallelUglifyPlugin({ cacheDir: '.cache/', uglifyJS:{ output: { // 是否输出可读性较强的代码,即会保留空格和制表符,默认为输出,为了达到更好的压缩效果,可以设置为false beautify: false, //是否保留代码中的注释,默认为保留,为了达到更好的压缩效果,可以设置为false comments: false }, compress: { //是否在UglifyJS删除没有用到的代码时输出警告信息,默认为输出 warnings: false, //是否删除代码中所有的console语句,默认为不删除,开启后,会删除所有的console语句 drop_console: true, //是否内嵌虽然已经定义了,但是只用到一次的变量,比如将 var x = 1; y = x, 转换成 y = 1, 默认为否 collapse_vars: true, } } }), ]构建结果对比:
["11593ms","10654ms","8334ms","7734ms"]
整体构建速度从
12000ms
降到现在的8000ms
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
下一篇:es6 filter() 数组过滤方法总结
人们对于笔记本电脑有一个固有印象:要么轻薄但性能一般,要么性能强劲但笨重臃肿。然而,今年荣耀新推出的MagicBook Pro 16刷新了人们的认知——发布会上,荣耀宣布猎人游戏本正式回归,称其继承了荣耀 HUNTER 基因,并自信地为其打出“轻薄本,更是游戏本”的口号。
众所周知,寻求轻薄本的用户普遍更看重便携性、外观造型、静谧性和打字办公等用机体验,而寻求游戏本的用户则普遍更看重硬件配置、性能释放等硬核指标。把两个看似难以相干的产品融合到一起,我们不禁对它产生了强烈的好奇:作为代表荣耀猎人游戏本的跨界新物种,它究竟做了哪些平衡以兼顾不同人群的各类需求呢?