www.2527.com_澳门新葡8455手机版_新京葡娱乐场网址_
做最好的网站

构建性能优化策略小结_javascript技巧_脚本之家,

2020-02-02 01:01 来源:未知

时间: 2019-02-23阅读: 460标签: webpack功能类html-webpack-plugin

背景

ProgressPlugin(webpack自带):用于统计打包进度

Webpack progress using node.js API

自动生成html,基本用法:

如今前端工程化的概念早已经深入人心,选择一款合适的编译和资源管理工具已经成为了所有前端工程中的标配,而在诸多的构建工具中,webpack以其丰富的功能和灵活的配置而深受业内吹捧,逐步取代了grunt和gulp成为大多数前端工程实践中的首选,React,Vue,Angular等诸多知名项目也都相继选用其作为官方构建工具,极受业内追捧。但是,随者工程开发的复杂程度和代码规模不断地增加,webpack暴露出来的各种性能问题也愈发明显,极大的影响着开发过程中的体验。

IgnorePlugin(webpack自带):忽略本地的一些模块

https://webpack.js.org/plugins/ignore-plugin/

new HtmlWebpackPlugin({ filename: 'index.html', // 生成文件名 template: path.join(process.cwd(), './index.html') // 模班文件})

问题归纳

CleanWebpackPlugin:清理指定目录的文件

http://npm.taobao.org/package/clean-webpack-plugin

copy-webpack-plugin

历经了多个web项目的实战检验,我们对webapck在构建中逐步暴露出来的性能问题归纳主要有如下几个方面:

DllPlugin(webpack自带):预打包文件

webpack进阶——DllPlugin优化打包性能(基于vue-cli)

拷贝资源插件

代码全量构建速度过慢,即使是很小的改动,也要等待长时间才能查看到更新与编译后的结果; 随着项目业务的复杂度增加,工程模块的体积也会急剧增大,构建后的模块通常要以M为单位计算; 多个项目之间共用基础资源存在重复打包,基础库代码复用率不高; node的单进程实现在耗cpu计算型loader中表现不佳;

DllReferencePlugin(webpack自带):项目打包引用预打包生成的文件

webpack进阶——DllPlugin优化打包性能(基于vue-cli)

基本用法:

针对以上的问题,我们来看看怎样利用webpack现有的一些机制和第三方扩展插件来逐个击破。

AssetsWebpackPlugin:为打包生成的js等生成路径引用指引

http://npm.taobao.org/package/assets-webpack-plugin

new CopyWebpackPlugin([ { from: path.join(process.cwd(), './vendor/'), to: path.join(process.cwd(), './dist/'), ignore: ['*.json'] }])

慢在何处

HappyPack:运用多核加速打包

happypack

webpack-manifest-plugin assets-webpack-plugin

作为工程师,我们一直鼓励要理性思考,用数据和事实说话,“我觉得很慢”,“太卡了”,“太大了”之类的表述难免显得太笼统和太抽象,那么我们不妨从如下几个方面来着手进行分析:

ExtractTextPlugin:将打包中的css单独抽离出来

extract-text-webpack-plugin

俩个插件效果一致,都是生成编译结果的资源单,只是资源单的数据结构不一致而已。

从项目结构着手,代码组织是否合理,依赖使用是否合理; 从webpack自身提供的优化手段着手,看看哪些api未做优化配置; 从webpack自身的不足着手,做有针对性的扩展优化,进一步提升效率;

EnvironmentPlugin(webpack自带):在webpack层面定义项目中可以使用的全局变量

http://www.css88.com/doc/webpack2/plugins/environment-plugin/

webpack-manifest-plugin 基本用法:

在这里我们推荐使用一个wepback的可视化资源分析工具:webpack-bundle-analyzer,在webpack构建的时候会自动帮你计算出各个模块在你的项目工程中的依赖与分布情况,方便做更精确的资源依赖和引用的分析。

DefinePlugin(webpack自带):在webpack层面定义项目中可以使用的全局变量 与EnvironmentPlugin的形式不同而已

https://segmentfault.com/a/1190000011530718、

http://www.css88.com/doc/webpack2/plugins/define-plugin/

module.exports = { plugins: [ new ManifestPlugin() ]}

从上图中我们不难发现大多数的工程项目中,依赖库的体积永远是大头,通常体积可以占据整个工程项目的7-9成,而且在每次开发过程中也会重新读取和编译对应的依赖资源,这其实是很大的的资源开销浪费,而且对编译结果影响微乎其微,毕竟在实际业务开发中,我们很少会去主动修改第三方库中的源码,改进方案如下:

ProvidePlugin(webpack自带):自动加载模块。 任何时候,当 identifier 被当作未赋值的变量时, module 就会自动被加载,并且 identifier 会被这个 module 输出的内容所赋值

http://www.css88.com/doc/webpack2/plugins/define-plugin/

assets-webpack-plugin 基本用法:

方案一、合理配置 CommonsChunkPlugin

CopyWebpackPlugin:将指定目录的文件赋值到指定目录下

https://www.npmjs.com/package/copy-webpack-plugin1

module.exports = { plugins: [ new AssetsPlugin() ]}

webpack的资源入口通常是以entry为单元进行编译提取,那么当多entry共存的时候,CommonsChunkPlugin的作用就会发挥出来,对所有依赖的chunk进行公共部分的提取,但是在这里可能很多人会误认为抽取公共部分指的是能抽取某个代码片段,其实并非如此,它是以module为单位进行提取。

CommonsChunkPlugin(webpack自带optimize)将部分包单独打包为一个文件,高效利用缓存

webpack进阶——缓存与独立打包

clean-webpack-plugin

假设我们的页面中存在entry1,entry2,entry3三个入口,这些入口中可能都会引用如utils,loadash,fetch等这些通用模块,那么就可以考虑对这部分的共用部分机提取。通常提取方式有如下四种实现:

HotModuleReplacementPlugin(webpack自带):热更新模块

在编译之前清理指定目录指定内容。

1、传入字符串参数,由chunkplugin自动计算提取

NoEmitOnErrorsPlugin(webpack自带):在编译出现错误时,使用它来跳过输出阶段。这样可以确保输出资源不会包含错误

https://webpack.js.org/plugins/no-emit-on-errors-plugin/#src/components/Sidebar/Sidebar.jsx

基本用法:

new webpack.optimize.CommonsChunkPlugin

NamedModulesPlugin(webpack自带):为每个包hash命名

webpack进阶——缓存与独立打包

// 清理目录const pathsToClean = [ 'dist', 'build'] // 清理参数const cleanOptions = { exclude: ['shared.js'], // 跳过文件}module.exports = { // ... plugins: [ new CleanWebpackPlugin(pathsToClean, cleanOptions) ]}

这种做法默认会把所有入口节点的公共代码提取出来, 生成一个common.js

HtmlWebpackPlugin:webpack打包后自动生成html页面

https://www.npmjs.com/package/html-webpack-plugin

compression-webpack-plugin

2、有选择的提取公共代码

HtmlWebpackIncludeAssetsPlugin:加强版,可以订制访问路径

https://www.npmjs.com/package/html-webpack-include-assets-plugin

提供带 Content-Encoding 编码的压缩版的资源

new webpack.optimize.CommonsChunkPlugin('common.js',['entry1','entry2']);

ParallelUglifyPlugin:加速压缩

https://www.npmjs.com/package/webpack-parallel-uglify-plugin

基本用法:

只提取entry1节点和entry2中的共用部分模块, 生成一个common.js

module.exports = { plugins: [ new CompressionPlugin() ]}

3、将entry下所有的模块的公共部分提取到一个通用的chunk中

progress-bar-webpack-plugin

new webpack.optimize.CommonsChunkPlugin({ name: 'vendors', minChunks: function  { return ( module.resource && /.js$/.test && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 ) }});

编译进度条插件

提取所有node_modules中的模块至vendors中,也可以指定minChunks中的最小引用数;

基本用法:

4、抽取enry中的一些lib抽取到vendors中

module.exports = { //... plugins: [ new ProgressBarPlugin() ]}
entry = { vendors: ['fetch', 'loadash']};new webpack.optimize.CommonsChunkPlugin({ name: "vendors", minChunks: Infinity});

代码相关类webpack.ProvidePlugin

添加一个entry名叫为vendors,并把vendors设置为所需要的资源库,CommonsChunk会自动提取指定库至vendors中。

自动加载模块,如 $ 出现,就会自动加载模块;$ 默认为'jquery'的exports

方案二、通过 externals 配置来提取常用库

用法:

在实际项目开发过程中,我们并不需要实时调试各种库的源码,这时候就可以考虑使用external选项了。

new webpack.ProvidePlugin({ $: 'jquery',})

简单来说external就是把我们的依赖资源声明为一个外部依赖,然后通过script外链脚本引入。这也是我们早期页面开发中资源引入的一种翻版,只是通过配置后可以告知webapck遇到此类变量名时就可以不用解析和编译至模块的内部文件中,而改用从外部变量中读取,这样能极大的提升编译速度,同时也能更好的利用CDN来实现缓存。

webpack.DefinePlugin

external的配置相对比较简单,只需要完成如下三步:

定义全局常量

1、在页面中加入需要引入的lib地址,如下:

用法:

2、在webapck.config.js中加入external配置项:

new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify(process.env.NODE_ENV) }})
module.export = { externals: { 'react-router': { amd: 'react-router', root: 'ReactRouter', commonjs: 'react-router', commonjs2: 'react-router' }, react: { amd: 'react', root: 'React', commonjs: 'react', commonjs2: 'react' }, 'react-dom': { amd: 'react-dom', root: 'ReactDOM', commonjs: 'react-dom', commonjs2: 'react-dom' } }}

mini-css-extract-plugin extract-text-webpack-plugin

这里要提到的一个细节是:此类文件在配置前,构建这些资源包时需要采用amd/commonjs/cmd相关的模块化进行兼容封装,即打包好的库已经是umd模式包装过的,如在node_modules/react-router中我们可以看到umd/ReactRouter.js之类的文件,只有这样webpack中的require和import * from 'xxxx'才能正确读到该类包的引用,在这类js的头部一般也能看到如下字样:

提取css样式,对比:

if (typeof exports === 'object' && typeof module === 'object') { module.exports = factory;} else if (typeof define === 'function' && define.amd) { define;} else if (typeof exports === 'object') { exports["ReactRouter"] = factory;} else { root["ReactRouter"] = factory;}

mini-css-extract-plugin 为webpack4及以上提供的plugin,支持css chunkextract-text-webpack-plugin 只能在webpack3 及一下的版本使用,不支持css chunk

3、非常重要的是一定要在output选项中加入如下一句话:

基本用法 extract-text-webpack-plugin:

output: { libraryTarget: 'umd'}
const ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { module: { rules: [ { test: /.css$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", use: "css-loader" }) } ] }, plugins: [ new ExtractTextPlugin("styles.css"), ]}

由于通过external提取过的js模块是不会被记录到webapck的chunk信息中,通过libraryTarget可告知我们构建出来的业务模块,当读到了externals中的key时,需要以umd的方式去获取资源名,否则会有出现找不到module的情况。

基本用法 mini-css-extract-plugin:

通过配置后,我们可以看到对应的资源信息已经可以在浏览器的source map中读到了。

const MiniCssExtractPlugin = require("mini-css-extract-plugin");module.exports = { module: { rules: [ { test: /.css$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: '/' // chunk publicPath } }, "css-loader" ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: "[name].css", // 主文件名 chunkFilename: "[id].css" // chunk文件名 }) ]}

对应的资源也可以直接由页面外链载入,有效地减小了资源包的体积。

编译结果优化类wbepack.IgnorePlugin

方案三、利用 DllPlugin 和 DllReferencePlugin 预编译资源模块

忽略regExp匹配的模块

我们的项目依赖中通常会引用大量的npm包,而这些包在正常的开发过程中并不会进行修改,但是在每一次构建过程中却需要反复的将其解析,如何来规避此类损耗呢?这两个插件就是干这个用的。

用法:

简单来说DllPlugin的作用是预先编译一些模块,而DllReferencePlugin则是把这些预先编译好的模块引用起来。这边需要注意的是DllPlugin必须要在DllReferencePlugin执行前先执行一次,dll这个概念应该也是借鉴了windows程序开发中的dll文件的设计理念。

new webpack.IgnorePlugin(/^./locale$/, /moment$/)

相对于externals,dllPlugin有如下几点优势:

uglifyjs-webpack-plugin

dll预编译出来的模块可以作为静态资源链接库可被重复使用,尤其适合多个项目之间的资源共享,如同一个站点pc和手机版等; dll资源能有效地解决资源循环依赖的问题,部分依赖库如:react-addons-css-transition-group这种原先从react核心库中抽取的资源包,整个代码只有一句话:

代码丑化,用于js压缩

module.exports = require('react/lib/ReactCSSTransitionGroup');

用法:

却因为重新指向了react/lib中,这也会导致在通过externals引入的资源只能识别react,寻址解析react/lib则会出现无法被正确索引的情况。

module.exports = { //... optimization: { minimizer: [new UglifyJsPlugin({ cache: true, // 开启缓存 parallel: true, // 开启多线程编译 sourceMap: true, // 是否sourceMap uglifyOptions: { // 丑化参数 comments: false, warnings: false, compress: { unused: true, dead_code: true, collapse_vars: true, reduce_vars: true }, output: { comments: false } } }] }};

由于externals的配置项需要对每个依赖库进行逐个定制,所以每次增加一个组件都需要手动修改,略微繁琐,而通过dllPlugin则能完全通过配置读取,减少维护的成本;

optimize-css-assets-webpack-plugin

1、配置dllPlugin对应资源表并编译文件

css压缩,主要使用 cssnano 压缩器

那么externals该如何使用呢,其实只需要增加一个配置文件:webpack.dll.config.js:

用法:

const webpack = require;const path = require;const isDebug = process.env.NODE_ENV === 'development';const outputPath = isDebug ? path.join(__dirname, '../common/debug') : path.join(__dirname, '../common/dist');const fileName = '[name].js';// 资源依赖包,提前编译const lib = [ 'react', 'react-dom', 'react-router', 'history', 'react-addons-pure-render-mixin', 'react-addons-css-transition-group', 'redux', 'react-redux', 'react-router-redux', 'redux-actions', 'redux-thunk', 'immutable', 'whatwg-fetch', 'byted-people-react-select', 'byted-people-reqwest'];const plugin = [ new webpack.DllPlugin({ /** * path * 定义 manifest 文件生成的位置 * [name]的部分由entry的名字替换 */ path: path.join(outputPath, 'manifest.json'), /** * name * dll bundle 输出到那个全局变量上 * 和 output.library 一样即可。 */ name: '[name]', context: __dirname }), new webpack.optimize.OccurenceOrderPlugin { plugin.push( new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify, new webpack.optimize.UglifyJsPlugin({ mangle: { except: ['$', 'exports', 'require'] }, compress: { warnings: false }, output: { comments: false } }) )}module.exports = { devtool: '#source-map', entry: { lib: lib }, output: { path: outputPath, filename: fileName, /** * output.library * 将会定义为 window.${output.library} * 在这次的例子中,将会定义为`window.vendor_library` */ library: '[name]', libraryTarget: 'umd', umdNamedDefine: true }, plugins: plugin};

$ NODE_ENV=development webpack --config webpack.dll.lib.js --progress$ NODE_ENV=production webpack --config webpack.dll.lib.js --progress 
module.exports = { //... optimization: { minimizer: [new OptimizeCssAssetsPlugin({ cssProcessor: require('cssnano'), // css 压缩优化器 cssProcessorOptions: { discardComments: { removeAll: true } } // 去除所有注释 })] }};

即可分别编译出支持调试版和生产环境中lib静态资源库,在构建出来的文件中我们也可以看到会自动生成如下资源:

webpack-md5-hash

common├── debug│ ├── lib.js│ ├── lib.js.map│ └── manifest.json└── dist ├── lib.js ├── lib.js.map └── manifest.json

使你的chunk根据内容生成md5,用这个md5取代 webpack chunkhash。

lib.js可以作为编译好的静态资源文件直接在页面中通过src链接引入,与externals的资源引入方式一样,生产与开发环境可以通过类似charles之类的代理转发工具来做路由替换; manifest.json中保存了webpack中的预编译信息,这样等于提前拿到了依赖库中的chunk信息,在实际开发过程中就无需要进行重复编译;

var WebpackMd5Hash = require('webpack-md5-hash'); module.exports = { // ... output: { //... chunkFilename: "[chunkhash].[id].chunk.js" }, plugins: [ new WebpackMd5Hash() ]};

2、dllPlugin的静态资源引入

SplitChunksPlugin

lib.js和manifest.json存在一一对应的关系,所以我们在调用的过程也许遵循这个原则,如当前处于开发阶段,对应我们可以引入common/debug文件夹下的lib.js和manifest.json,切换到生产环境的时候则需要引入common/dist下的资源进行对应操作,这里考虑到手动切换和维护的成本,我们推荐使用add-asset-html-webpack-plugin进行依赖资源的注入,可得到如下结果:

CommonChunkPlugin 的后世,用于chunk切割。

在webpack.config.js文件中增加如下代码:

webpack 把 chunk 分为两种类型,一种是初始加载initial chunk,另外一种是异步加载 async chunk,如果不配置SplitChunksPlugin,webpack会在production的模式下自动开启,默认情况下,webpack会将 node_modules 下的所有模块定义为异步加载模块,并分析你的 entry、动态加载(import()、require.ensure)模块,找出这些模块之间共用的node_modules下的模块,并将这些模块提取到单独的chunk中,在需要的时候异步加载到页面当中,其中默认配置如下:

const isDebug = (process.env.NODE_ENV === 'development');const libPath = isDebug ? '../dll/lib/manifest.json' : '../dll/dist/lib/manifest.json';// 将mainfest.json添加到webpack的构建中module.export = { plugins: [ new webpack.DllReferencePlugin({ context: __dirname, manifest: require ]}
module.exports = { //... optimization: { splitChunks: { chunks: 'async', // 异步加载chunk minSize: 30000, maxSize: 0, minChunks: 1, maxAsyncRequests: 5, maxInitialRequests: 3, automaticNameDelimiter: '~', // 文件名中chunk分隔符 name: true, cacheGroups: { vendors: { test: /[\/]node_modules[\/]/, // priority: -10 }, default: { minChunks: 2, // 最小的共享chunk数 priority: -20, reuseExistingChunk: true } } } }};

配置完成后我们能发现对应的资源包已经完成了纯业务模块的提取

编译优化类DllPlugin DllReferencePlugin autodll-webpack-plugin

多个工程之间如果需要使用共同的lib资源,也只需要引入对应的lib.js和manifest.js即可,plugin配置中也支持多个webpack.DllReferencePlugin同时引入使用,如下:

dllPlugin 将模块预先编译,DllReferencePlugin 将预先编译好的模块关联到当前编译中,当 webpack 解析到这些模块时,会直接使用预先编译好的模块。

module.export = { plugins: [ new webpack.DllReferencePlugin({ context: __dirname, manifest: require, new webpack.DllReferencePlugin({ context: __dirname, manifest: require ]}

autodll-webpack-plugin 相当于 dllPlugin 和 DllReferencePlugin 的简化版,其实本质也是使用 dllPlugin DllReferencePlugin,它会在第一次编译的时候将配置好的需要预先编译的模块编译在缓存中,第二次编译的时候,解析到这些模块就直接使用缓存,而不是去编译这些模块。

方案四、使用 Happypack 加速你的代码构建

dllPlugin 基本用法:

以上介绍均为针对webpack中的chunk计算和编译内容的优化与改进,对资源的实际体积改进上也较为明显,那么除此之外,我们能否针对资源的编译过程和速度优化上做些尝试呢?

const output = { filename: '[name].js', library: '[name]_library', path: './vendor/'}module.exports = { entry: { vendor: ['react', 'react-dom'] // 我们需要事先编译的模块,用entry表示 }, output: output, plugins: [ new webpack.DllPlugin({ // 使用dllPlugin path: path.join(output.path, `${output.filename}.json`), name: output.library // 全局变量名, 也就是 window 下 的 [output.library] }) ]}

众所周知,webpack中为了方便各种资源和类型的加载,设计了以loader加载器的形式读取资源,但是受限于node的编程模型影响,所有的loader虽然以async的形式来并发调用,但是还是运行在单个 node的进程以及在同一个事件循环中,这就直接导致了当我们需要同时读取多个loader文件资源时,比如babel-loader需要transform各种jsx,es6的资源文件。在这种同步计算同时需要大量耗费cpu运算的过程中,node的单进程模型就无优势了,那么happypack就针对解决此类问题而生。

DllReferencePlugin 基本用法:

开启happypack的线程池

const manifest = path.resolve(process.cwd(), 'vendor', 'vendor.js.json')module.exports = { plugins: [ new webpack.DllReferencePlugin({ manifest: require(manifest), // 引进dllPlugin编译的json文件 name: 'vendor_library' // 全局变量名,与dllPlugin声明的一致 } ]}

happypack的处理思路是将原有的webpack对loader的执行过程从单一进程的形式扩展多进程模式,原本的流程保持不变,这样可以在不修改原有配置的基础上来完成对编译过程的优化,具体配置如下:

autodll-webpack-plugin 基本用法:

const HappyPack = require; const os = require const HappyThreadPool = HappyPack.ThreadPool.length}); // 启动线程池});module:{ rules: [ { test: /.$/, // use: ['babel-loader?cacheDirectory'], use: 'happypack/loader?id=jsx', exclude: /^node_modules$/ } ] }, plugins:[ new HappyPack({ id: 'jsx', cache: true, threadPool: HappyThreadPool, loaders: ['babel-loader'] }) ]
module.exports = { plugins: [ new AutoDllPlugin({ inject: true, // 与 html-webpack-plugin 结合使用,注入html中 filename: '[name].js', entry: { vendor: [ 'react', 'react-dom' ] } }) ]}

我们可以看到通过在loader中配置直接指向happypack提供的loader,对于文件实际匹配的处理 loader,则是通过配置在plugin属性来传递说明,这里happypack提供的loader与plugin的衔接匹配,则是通过id=happybabel来完成。配置完成后,laoder的工作模式就转变成了如下所示:

happypack thread-loader

happypack在编译过程中除了利用多进程的模式加速编译,还同时开启了cache计算,能充分利用缓存读取构建文件,对构建的速度提升也是非常明显的,经过测试,最终的构建速度提升如下:

多线程编译,加快编译速度,thread-loader不可以和 mini-css-extract-plugin 结合使用。

关于happyoack的更多介绍可以查看:

happypack 基本用法:

方案五、增强 uglifyPlugin

const HappyPack = require('happypack');const os = require('os');const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });const happyLoaderId = 'happypack-for-react-babel-loader';module.exports = { module: { rules: [{ test: /.jsx?$/, loader: 'happypack/loader', query: { id: happyLoaderId }, include: [path.resolve(process.cwd(), 'src')] }] }, plugins: [new HappyPack({ id: happyLoaderId, threadPool: happyThreadPool, loaders: ['babel-loader'] })]}

uglifyJS凭借基于node开发,压缩比例高,使用方便等诸多优点已经成为了js压缩工具中的首选,但是我们在webpack的构建中观察发现,当webpack build进度走到80%前后时,会发生很长一段时间的停滞,经测试对比发现这一过程正是uglfiyJS在对我们的output中的bunlde部分进行压缩耗时过长导致,针对这块我们可以使用webpack-uglify-parallel来提升压缩速度。

thread-loader 基本用法:

从插件源码中可以看到,webpack-uglify-parallel的是实现原理是采用了多核并行压缩的方式来提升我们的压缩速度。

module.exports = { module: { rules: [ { test: /.js$/, include: path.resolve("src"), use: [ "thread-loader", // your expensive loader (e.g babel-loader) "babel-loader" ] } ] }}
plugin.nextWorker().send({ input: input, inputSourceMap: inputSourceMap, file: file, options: options});plugin._queue_len  ; if  { callback();} if (this.workers.length < this.maxWorkers) { var worker = fork(__dirname   '/lib/worker'); worker.on('message', this.onWorkerMessage.bind; worker.on('error', this.onWorkerError.bind; this.workers.push;}this._next_worker  ;return this.workers[this._next_worker % this.maxWorkers];

hard-source-webpack-plugin cache-loader

使用配置也非常简单,只需要将我们原来webpack中自带的uglifyPlugin配置:

使用模块编译缓存,加快编译速度。

new webpack.optimize.UglifyJsPlugin({ exclude:/.min.js$/ mangle:true, compress: { warnings: false }, output: { comments: false }})

const os = require; const UglifyJsParallelPlugin = require('webpack-uglify-parallel'); new UglifyJsParallelPlugin.length, mangle: true, compressor: { warnings: false, drop_console: true, drop_debugger: true } })

hard-source-webpack-plugin 基本用法:

目前webpack官方也维护了一个支持多核压缩的UglifyJs插件:uglifyjs-webpack-plugin,使用方式类似,优势在于完全兼容webpack.optimize.UglifyJsPlugin中的配置,可以通过uglifyOptions写入,因此也做为推荐使用,参考配置如下:

module.exports = { plugins: [ new HardSourceWebpackPlugin() ]}cache-loader 基本用法:module.exports = { module: { rules: [ { test: /.ext$/, use: [ 'cache-loader', ...loaders ], include: path.resolve('src') } ] }}
const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); new UglifyJsPlugin({ uglifyOptions: { ie8: false, ecma: 8, mangle: true, output: { comments: false }, compress: { warnings: false } }, sourceMap: false, cache: true, parallel: os.cpus

编译分析类webpack-bundle-analyzer

方案六、Tree-shaking & Scope Hoisting

编译模块分析插件

wepback在2.X和3.X中从rolluo中借鉴了tree-shaking和Scope Hoisting,利用es6的module特性,利用AST对所有引用的模块和方法做了静态分析,从而能有效地剔除项目中的没有引用到的方法,并将相关方法调用归纳到了独立的webpack_module中,对打包构建的体积优化也较为明显,但是前提是所有的模块写法必须使用ES6 Module进行实现,具体配置参考如下:

基本用法:

 // .babelrc: 通过配置减少没有引用到的方法 { "presets": [ ["env", { "targets": { "browsers": ["last 2 versions", "safari >= 7"] } }], // https://www.zhihu.com/question/41922432 ["es2015", {"modules": false}] // tree-shaking ] } // webpack.config: Scope Hoisting { plugins:[ // https://zhuanlan.zhihu.com/p/27980441 new webpack.optimize.ModuleConcatenationPlugin() ] }
new BundleAnalyzerPlugin({ analyzerMode: 'server', analyzerHost: '127.0.0.1', analyzerPort: 8889, reportFilename: 'report.html', defaultSizes: 'parsed', generateStatsFile: false, statsFilename: 'stats.json', statsOptions: null, logLevel: 'info'}),

适用场景

stats-webpack-plugin PrefetchPlugin

在实际的开发过程中,可灵活地选择适合自身业务场景的优化手段。

stats-webpack-plugin 将构建的统计信息写入文件,该文件可在,并根据分析结果,可使用 PrefetchPlugin 对部分模块进行预解析编译(本人也不理解这个plugin,据说优化效果不明显,有兴趣的同学请见 how-to-optimize-webpacks-build-time-using-prefetchplugin-analyse-tool)。

优化手段

stats-webpack-plugin 基本用法:

开发环境

module.exports = { plugins: [ new StatsPlugin('stats.json', { chunkModules: true, exclude: [/node_modules[\/]react/] }) ]};

生产环境

PrefetchPlugin 基本用法:

CommonsChunk √ √ externals √ DllPlugin √ √ Happypack √ uglify-parallel √

module.exports = { plugins: [ new webpack.PrefetchPlugin('/web/', 'app/modules/HeaderNav.jsx'), new webpack.PrefetchPlugin('/web/', 'app/pages/FrontPage.jsx') ];}

温馨提醒

speed-measure-webpack-plugin

本文中的所有例子已经重新优化,支持最新的webpack3特性,并附带有分享ppt地址,可以在线点击查看

统计编译过程中,各loader和plugin使用的时间。

小结

const SpeedMeasurePlugin = require("speed-measure-webpack-plugin"); const smp = new SpeedMeasurePlugin(); const webpackConfig = { plugins: [ new MyPlugin(), new MyOtherPlugin() ]}module.exports = smp.wrap(webpackConfig);

性能优化无小事,追求快没有止境,在前端工程日益庞大复杂的今天,针对实际项目,持续改进构建工具的性能,对项目开发效率的提升和工具深度理解都是极其有益的。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

TAG标签:
版权声明:本文由澳门新葡8455手机版发布于Web前端,转载请注明出处:构建性能优化策略小结_javascript技巧_脚本之家,