项目比较小的状况下Webpack的性能问题几乎能够忽略,可是一旦项目复杂度上升,Webpack会有额外的性能损失须要咱们进行优化。javascript
经过前面内容的学习咱们能够知道Webpack主要干下面这些事情:css
所以,咱们能够从这方面入手进行优化,减小Webpack搜索文件的范围,减小没必要要的处理。java
在以前的内容中介绍过loader可使用test、include、exclude配置项来匹配须要Loader处理的文件,所以推荐给每一个loader定义test以后还定义include或exclude。node
module.exports = { module:{ rules:[ { test:/\.js$/, use:'babel-loader', include: path.resolve(__dirname, 'src'), // 只处理src目录下的js文件 } ] } };
导入未添加扩展名的模块时,Webpack会经过resolve.extensions后缀去检查文件是否存在。因为resolve.extensions是一个数组,若是数组项比较多,正确的后缀放置得越靠后,Webpack尝试次数就会越多,影响到性能。react
所以配置resolve.extensions时须要遵照如下规则:webpack
module.noParse能够告诉Webpack忽略未采用模块系统文件的处理,能够有效地提升性能。好比常见的jQuery很是大,又没有采用模块系统,让Webpack解析这类型文件彻底是浪费性能。web
所以咱们能够配置以下的module.noParse:npm
module.exports = { module:{ noParse:[/jQuery/] } };
在导入模块时,IgnorePlugin能够忽略指定模块的生成。好比moment.js在导入时会自动导入本地化文件,通常状况下几乎不使用并且又比较大,此时能够经过IgnorePlugin忽略对本地化文件的生成,减少文件大小。json
module.exports = { plugins:[ new webpack.IgnorePlugin(/\.\/local/, /moment/) ] };
使用过Windows操做系统的读者应该会常常看到以.dll扩展名的文件,这些文件叫作动态连接库,包含了其余程序或动态连接库的函数和数据。数组
Webpack的DllPlugin的思想是相似的,先将公共模块打包为独立的Dll模块,而后在业务代码中直接引用这些模块。采用DllPlugin以后会大大提高Webpack构建速度,缘由在于,包含大量复用模块的动态连接库只须要编译一次,以后的构建中会直接引用这些构建好的模块。
在Webpack中使用动态连接库有如下两个步骤:
下面以React项目为例进行说明。
Dll库须要单独构建,所以咱们须要一份单独的配置Webpack文件。
1.新建webpack.dll.config.js
const webpack = require('webpack'); module.exports = { entry:{ react: ['react', 'react-dom'] }, output:{ filename: '_dll_[name].js', // 输出的文件名 path: path.resolve(__dirname, 'dist'), // 输出到dist目录 library: '_dll_[name]' }, plugins: [ // name要等于output.library里的name new webpack.DllPlugin({ name: "_dll_[name]", path: path.resolve(__dirname, "dist", "manifest.json") // 清单文件路径 }) ] };
2.编辑webpack.config.js
const webpack = require('webpack'); module.exports = { entry: './src/main', output:{ filename: '[name].js', // 输出的文件名 path: path.resolve(__dirname, 'dist'), // 输出到dist目录 }, plugins: [ // 传入manifest.json new webpack.DllReferencePlugin({ manifest: path.resolve(__dirname, "dist", "manifest.json") // 清单文件路径 }) ] };
3.添加构建命令
{ "scripts":{ "build-dll":"webpack --config webpack.dll.config.js", "build":"webpack" } }
4.构建Dll
npm run build-dll
5.构建应用
npm run build
Dll须要先构建,不然应用将构建失败
Webpack默认状况下是单进程执行的,所以没法利用多核优点,经过HappyPack能够变成多进程构建,从而提高构建速度。下面咱们一块儿来看看如何使用happypack来加速构建。
1.安装happypack
npm isntall happypack
2.编辑配置文件,须要将Loader配置到HappyPack插件中,由HappyPack对Loader进行调用。
const HappyPackPlugin = require('happypack'); const path = require('path'); module.exports = { entry: './src/main', output:{ path: path.resolve(__dirname, 'build'), filename:'[name].js' }, module:{ rules:[ { test:/\.js$/, use:'happypack/loader?id=js', // 配置id为js include:[ path.resolve(__dirname,'src') ] }, { test:/\.scss$/, use:'happypack/loader?id=scss', // 配置id为scss include:[ path.resolve(__dirname,'src') ] }, { test:/\.css$/, use:'happypack/loader?id=css', // 配置id为css include:[ path.resolve(__dirname,'src') ] } ] }, plugins:[ new HappyPackPlugin({ id:'js', // id为js的loader配置 use:[ { loader:'babel-loader', options:{ plugins:['@babel/transform-runtime'], presets:['@babel/env'] } } ] }), new HappyPackPlugin({ id:'scss', // id为scss的loader配置 use:['style-loader','css-loader','sass-loader'] }), new HappyPackPlugin({ id:'css', // id为css的loader配置 use:['style-loader','css-loader'] }), ] };
Tree-Shaking原始的本意是”摇动树“,这样就会将一些分支”摇掉“,从而减小主干大小。而Webpack中的Tree-Shaking是相似的,在Webpack项目中,有一个入口文件,至关于树的主干,入口文件又依赖了许多模块。实际开发中,虽然依赖了某个模块,但其实只使用了其中的部分代码,经过Tree-Shaking,能够将模块中未使用的代码剔除掉,从而减小构建结果的大小。
注意:只有使用ES6模块系统的代码,在mode为production时,Tree-Shaking才会生效。所以,在编写代码时尽可能使用import/export的方式。
在开发中,咱们通常会将业务代码打包为app.js,其余第三方依赖打包为vendor.js。这样会有一个比较大的问题,若是依赖的第三方模块过多,vendor.js会愈来愈大,而在浏览器加载时须要彻底加载完vendor.js才能够,这样就会形成无谓的等待,由于咱们当前页面可能只使用了一部分代码。此时可使用Webpack来实现按需加载,只有在真正用到这个模块时才会加载相应的js。
好比基于echarts开发了一个数据可视化页面,能够在这个路由组件下面使用异步的方式加载echarts的代码:
import('echarts').then(modules => { const echarts = modules.default; const chart = echarts.init(document.querySelector('#chart')); });
不过使用按需加载时,构建代码中会包含Promise调用,所以低版本浏览器须要注入Promise的polyfill实现。
Webpack4中能够将多个公共模块打包一份,减小代码冗余,Webpack4以前的版本是使用webpack内置的CommonsChunkPlugin实现的,Webpack4直接配置optimization
便可。
module.exports = { optimization:{ splitChunks:{ cacheGroups:{ common:{ // 应用代码中公共模块 chunks: 'all', // 最小公共模块引用次数 minChunks: 2 }, vendor:{ // node_modules中第三方模块 test: /node_modules/, chunks: 'all', minChunks: 1 } } } } };
第三方库代码的变动通常比较少(经过package.json的版本能够指定依赖版本),所以构建出来的vendor.js基本不会变就能够利用浏览器的缓存机制进行缓存。
而应用代码的变动是比较频繁的,所以单独打包为common.js,浏览器能够单独缓存,若是应用代码发生变动,浏览器只用从新下载common.js文件,而不用从新下载vendor.js。
HMR(Hot Module Replacement)是Webpack提供的经常使用功能之一,它容许在运行时对模块进行修改,而无需刷新整个页面(LiveReload须要刷新页面才能加载),这样有如下优点:
使用如下配置便可打开内置的HMR功能:
const webpack = require('webpack'); module.exports = { devServer: { hot: true, // 启用热加载 contentBase: './dist', }, plugins:[ new webpack.NamedModulesPlugin(), // 打印更新的模块路径 new webpack.HotModuleReplacementPlugin() // 热更新插件 ] };
本文咱们对Webpack4最经常使用的性能优化技术进行了学习,这些优化方法对业务代码的侵入性很是小(只有按需加载优化会要求使用import()函数进行加载),在实际的开发中,能够结合这些技术进行针对性的优化,好比开发时编译慢,可能就须要使用HappyPack插件进行多进程编译以加快编译速度等等。