webpack构建优化—dll

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了webpack构建优化—dll相关的知识,希望对你有一定的参考价值。

参考技术A 首先要声明 dll 的配置文件 webpack.dll.js ,这个配置文件的作用就是将所有依赖包整合成一个文件, entry 配置为 venders: ["vue","vuex","axios"] ,它表示会将 vue , vuex 和 axios 这些依赖包整合成一个 venders.js 。

用的是 DllPlugin 这个插件,它已经集成到 webpack 中,所以不用安装,它会生成两个文件,一个是包含依赖项的 venders.js ,还有表示依赖映射关系的 manifest.json 文件,它包含依赖包名以及对应存储路径,可以通过它知道 venders.js 里有哪些依赖包,而 webpack 可以通过这个文件知道哪些依赖包不参与打包。

在 package.json 里面配置 dll 的命令,执行 npm run dll 命令,会生成 venders.js 。

那 venders.js 是什么呢?它是根据 webpack.dll.js 里的 entry 的 key 值为文件名来生成的,它的值是一个数组,数组里面是打包的 npm 包的列表,就是把需要的包打包到 venders.js 中。

在 webpack.config.js 里配置 DllReferencePlugin ,这个包已经默认集成到 webpack 里,所以不要安装,它的作用就是根据 manifest.json 的依赖关系告诉哪些库不参与打包,从而提升打包的性能。

还有个 AddAssetHtmlWebpackPlugin 插件需要配置,这个需要执行 npm i add-asset-html-webpack-plugin -D 进行安装,它的作用就是将生成的 venders.js 插入到 html 中。

webpack构建速度优化

webpack打包速度优化

前言

Webpack打包优化并没有什么固定的模式,一般我们常见的就是缓存多进程抽离拆分

一、分析打包速度

优化webpack构建速度的第一步就是知道时间花费在哪里,才可以集中的进行针对性的优化。

这边我们用到speed-measure-webpack-plugin插件。

// 分析打包时间
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
// ...
module.exports = smp.wrap(prodWebpackConfig)

二、开销时间分析

如下是未优化前通过speed-measure-webpack-plugin测量插件生成的数据分析:

 SMP  ?  
General output time took 14 mins, 14.33 secs

 SMP  ?  Plugins
ExtractTextPlugin took 6 mins, 50.99 secs
PurifyPlugin took 6 mins, 11.47 secs
UglifyJsPlugin took 6 mins, 10.98 secs
NgcWebpackPlugin took 3 mins, 6.12 secs
LoaderOptionsPlugin took 35.55 secs
HtmlWebpackPlugin took 6.24 secs
ModuleConcatenationPlugin took 0.582 secs
HashedModuleIdsPlugin took 0.108 secs
CommonsChunkPlugin took 0.057 secs
Object took 0.031 secs
ScriptExtHtmlWebpackPlugin took 0.016 secs
HtmlElementsPlugin took 0.015 secs
DefinePlugin took 0.012 secs
InlineManifestPlugin took 0.01 secs
ProvidePlugin took 0.009 secs

 SMP  ?  Loaders
raw-loader took 6 mins, 17.56 secs
  module count = 1688
css-loader took 6 mins, 17.053 secs
  module count = 1291
css-loader, and 
sass-loader took 4 mins, 53.45 secs
  module count = 402
modules with no loaders took 3 mins, 20.57 secs
  module count = 1736
file-loader took 1 min, 12.41 secs
  module count = 13
@ngtools/webpack took 50.12 secs
  module count = 3487
to-string-loader, and 
css-loader took 14.66 secs
  module count = 1285
json-loader took 4.9 secs
  module count = 3
to-string-loader, and 
css-loader, and 
sass-loader took 3.83 secs
  module count = 402
extract-text-webpack-plugin, and 
style-loader, and 
css-loader took 0.54 secs
  module count = 4
html-webpack-plugin took 0.018 secs
  module count = 1

speed-measure-webpack-plugin插件打包的时间分析可知,花销的总时间达到General output time took 14 mins, 14.33 secs14分钟之多,主要开销在loader的一些解析和编译操作上。

三、缓存处理

1. 通过cache-loader进行缓存处理

从开销时间来看,主要对开销较大的loader进行缓存操作。这边使用到cache-loader进行缓存操作。

?? 请注意,保存和读取这些缓存文件会有一些时间开销,所以请只对性能开销较大的 loader 使用此 loader。

module: {
	rules: [
		...ngcWebpackConfig.loaders,
		{
			test: /.css$/,
			use: [‘cache-loader‘, ‘to-string-loader‘, ‘css-loader‘], // use loader是从右至左执行的,							因此必须放在执行的最后一个,也就是配置的第一个loader[‘cache-loader‘,...loaders]
			exclude: [helpers.root(‘src‘, ‘styles‘)]
		},
		{
			test: /.scss$/,
			use: [‘cache-loader‘, ‘to-string-loader‘, ‘css-loader‘, ‘sass-loader‘],
			exclude: [helpers.root(‘src‘, ‘styles‘)] // to-string-loader、raw-loader、file-loader进行缓存存在一些问题,实际中不适用
		},
		{
			test: /.html$/,
			use: [‘cache-loader‘, ‘raw-loader‘],
			exclude: [helpers.root(‘src/index.html‘)]
		},
		{
			test: /.(jpg|png|gif)$/,
			use: [‘cache-loader‘, ‘file-loader‘]
		},
		{
			test: /.(eot|woff2?|svg|ttf)([?]?.*)$/,
			use: [‘cache-loader‘, ‘file-loader‘]
		}
	]
}

2. UglifyJsPlugin开启压缩JavaScript代码

uglifyjs-webpack-plugin插件也是我们常用用来压缩JavaScript代码的,在这个插件下也有一个参数用来配置缓存处理,也可以在压缩代码的时候生成缓存,以便在下次压缩代码的时候直接在缓存中取而不必重新压缩。

new UglifyJsPlugin({
	cache: true // 开启压缩js代码缓存
})

?? 请注意使用cache-loader之后,我们再一次打包看看效果如何。因为第一次是没有生成缓存文件的,因此使用cache-loader要在打包之后缓存过后的第一次打包,也就是加了cache-loader之后的第二次打包才会生成缓存文件。

// 加了cache-loade, cache: true之后的第一次打包(生成缓存文件,但是第一次不走缓存,开销变大,因为加了一些cache-loader的解析和编译) 由原来的14分钟上升到17分钟。
 SMP  ?  
General output time took 17 mins, 15.61 secs

 SMP  ?  Plugins
ExtractTextPlugin took 9 mins, 21.44 secs
PurifyPlugin took 8 mins, 39.02 secs
UglifyJsPlugin took 8 mins, 38.5 secs
NgcWebpackPlugin took 3 mins, 20.037 secs
LoaderOptionsPlugin took 38.75 secs
HtmlWebpackPlugin took 6.41 secs
ModuleConcatenationPlugin took 0.655 secs
HashedModuleIdsPlugin took 0.107 secs
CommonsChunkPlugin took 0.059 secs
Object took 0.032 secs
HtmlElementsPlugin took 0.016 secs
ScriptExtHtmlWebpackPlugin took 0.016 secs
InlineManifestPlugin took 0.011 secs
ProvidePlugin took 0.01 secs
DefinePlugin took 0.008 secs

 SMP  ?  Loaders
cache-loader, and 
raw-loader took 6 mins, 33.38 secs
  module count = 1688
cache-loader, and 
to-string-loader, and 
css-loader took 6 mins, 20.15 secs
  module count = 1285
css-loader took 6 mins, 16.26 secs
  module count = 1291
css-loader, and 
sass-loader took 4 mins, 49.26 secs
  module count = 402
modules with no loaders took 4 mins, 25.23 secs
  module count = 1736
cache-loader, and 
to-string-loader, and 
css-loader, and 
sass-loader took 4 mins, 6.97 secs
  module count = 402
@ngtools/webpack took 48.6 secs
  module count = 3487
cache-loader, and 
file-loader took 14.78 secs
  module count = 13
json-loader took 7.36 secs
  module count = 3
extract-text-webpack-plugin, and 
style-loader, and 
css-loader took 0.528 secs
  module count = 4
html-webpack-plugin took 0.019 secs
  module count = 1
// 第二次构建,走缓存。花销时间为8分钟40s,可以看到是有很大的提升的。
 SMP  ?  
General output time took 8 mins, 40.22 secs

 SMP  ?  Plugins
NgcWebpackPlugin took 3 mins, 20.82 secs
ExtractTextPlugin took 42.63 secs
LoaderOptionsPlugin took 38.24 secs
HtmlWebpackPlugin took 11.55 secs
UglifyJsPlugin took 1.74 secs
PurifyPlugin took 1.012 secs
ModuleConcatenationPlugin took 0.637 secs
HashedModuleIdsPlugin took 0.1 secs
CommonsChunkPlugin took 0.045 secs
Object took 0.034 secs
HtmlElementsPlugin took 0.015 secs
ScriptExtHtmlWebpackPlugin took 0.013 secs
InlineManifestPlugin took 0.01 secs
ProvidePlugin took 0.005 secs
DefinePlugin took 0.005 secs

 SMP  ?  Loaders
cache-loader, and 
raw-loader took 6 mins, 31.28 secs
  module count = 1688
cache-loader, and 
to-string-loader, and 
css-loader took 6 mins, 25.46 secs
  module count = 1285
css-loader took 6 mins, 19.084 secs
  module count = 1291
css-loader, and 
sass-loader took 4 mins, 46.74 secs
  module count = 402
cache-loader, and 
to-string-loader, and 
css-loader, and 
sass-loader took 4 mins, 14.074 secs
  module count = 402
modules with no loaders took 4 mins, 0.806 secs
  module count = 1736
@ngtools/webpack took 49.95 secs
  module count = 3487
cache-loader, and 
file-loader took 18.81 secs
  module count = 13
json-loader took 5.15 secs
  module count = 3
extract-text-webpack-plugin, and 
style-loader, and 
css-loader took 0.411 secs
  module count = 4
html-webpack-plugin took 0.016 secs
  module count = 1

四、开启多进程

1. 使用happypack开启多进程解析和处理。

在使用 Webpack 对项目进行构建时,会对大量文件进行解析和处理。当文件数量变多之后,Webpack 构件速度就会变慢。由于运行在 Node.js 之上的 Webpack 是单线程模型的,所以 Webpack 需要处理的任务要一个一个进行操作。

而 Happypack 的作用就是将文件解析任务分解成多个子进程并发执行。子进程处理完任务后再将结果发送给主进程。所以可以大大提升 Webpack 的项目构件速度

cnpm i happypack --save-dev // 安装happypack

// webpack.config.js
const HappyPack = require(‘happypack‘); // 通过node CommonJs模块导入
const os = require(‘os‘);

plugins: [
  new HappyPack({
    id: ‘css‘,
    threads: os.cpus().length,
    loaders: [‘to-string-loader‘, ‘css-loader‘]
  }),
	new HappyPack({
    id: ‘scss‘, // 定义id为需开启happypack解析的loader进行标记
    threads: os.cpus().length, // 开启操作系统cpu的最大核心数
    loaders: [‘to-string-loader‘, ‘css-loader‘, ‘sass-loader‘]
  })
],
module: {
  rules: [
    {
      test: /.css$/,
      use: ‘happypack/loader?id=css‘ // id=css就是上述happypack定义的id
    },
    {
      test: /.scss$/,
      use: ‘happypack/loader?id=scss‘ 
    }
  ]
}

2. 同时使用cache-loader和happypack进行打包优化。

// 要同时使用cache-loader和happypack,只需要在happypack插件中的loaders配置把cache-loader加载配置最前头就可以,如下。
plugins: [
	new HappyPack({
    id: ‘scss‘, // 定义id为需开启happypack解析的loader进行标记
    threads: os.cpus().length, // 开启操作系统cpu的最大核心数
    loaders: [‘cache-loader‘, ‘style-loader‘, ‘css-loader‘, ‘sass-loader‘] // cache-loader最前
  })
],
module: {
  rules: [
    {
      test: /.scss$/,
      use: ‘happypack/loader?id=scss‘
    }
  ]
}

3. UglifyJsPlugin开启多进程压缩JavaScript代码

new UglifyJsPlugin({
	cache: true,
	parallel: true // 开启多进程压缩js代码
})
// 同时使用缓存和多进程,可以看到进一步的提升
 SMP  ?  
General output time took 7 mins, 35.03 secs

 SMP  ?  Plugins
NgcWebpackPlugin took 3 mins, 17.25 secs
ExtractTextPlugin took 43.79 secs
LoaderOptionsPlugin took 39.64 secs
HtmlWebpackPlugin took 4.98 secs
UglifyJsPlugin took 1.83 secs
PurifyPlugin took 1.018 secs
ModuleConcatenationPlugin took 0.717 secs
HappyPlugin took 0.364 secs
Object took 0.117 secs
HashedModuleIdsPlugin took 0.108 secs
CommonsChunkPlugin took 0.051 secs
HtmlElementsPlugin took 0.016 secs
ScriptExtHtmlWebpackPlugin took 0.015 secs
InlineManifestPlugin took 0.012 secs
ProvidePlugin took 0.008 secs
DefinePlugin took 0.008 secs

 SMP  ?  Loaders
happypack took 5 mins, 30.95 secs
  module count = 1687
cache-loader, and 
raw-loader took 6 mins, 28.28 secs
  module count = 1688
modules with no loaders took 4 mins, 15.065 secs
  module count = 1736
css-loader, and 
sass-loader took 2 mins, 31.25 secs
  module count = 402
@ngtools/webpack took 49.56 secs
  module count = 3487
css-loader took 28.089 secs
  module count = 1291
cache-loader, and 
file-loader took 17.45 secs
  module count = 13
json-loader took 6.14 secs
  module count = 3
extract-text-webpack-plugin, and 
style-loader, and 
css-loader took 0.509 secs
  module count = 4

五、抽离和外部引用Externals

项目中的抽离一般把不常改变的第三方依赖通过动态链接库抽离出来,再去引用,这样可以解决从动态链接库直接引用,减少了第三方包的打包时间,从而提高打包速度。

这边用到的插件是DllPluginDLLReferencePlugin

  1. 使用DllPlugin需要先创建一个webpack.dll.config.js
  2. 文件写入需要抽离的第三方包,如下的vendor: []
// webpack.dll.config.js
module.exports = {
  entry: {
    vendor: ["immutable", "ng-drag-drop", "ng-zorro-antd", "ngx-amap", "ngx-clipboard", "ngx-color-picker", "ngx-echarts", "ngx-infinite-scroll", "ngx-ueditor", "ngx-umeditor"]
  },
  output: {
    path: path.resolve(__dirname, ‘../dll‘),
    filename: ‘[name].dll.js‘,
    library: ‘[name]_[hash]‘
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.resolve(__dirname, ‘../dll‘, ‘manifeset.json‘),
      name: ‘[name]_[hash]‘
    })
  ]
}

// package.json中配置
{
	"scripts": {
  	"build:dll": "npm run webpack -- --config config/webpack.dll.config.js --progress",
  }
}
  
// 执行npm run build:dll生成vendor.dll.js和manifeset.json文件
  1. 在webpack.prod.js生产配置文件中通过DLLReferencePlugin去引用对应的生成的manifeset.json文件
// webpack.prod.js
plugins: [
  new webpack.DllReferencePlugin({
    manifest: require(‘../dll/manifeset.json‘),
  }) 
]
  1. 也可以使用externals外部扩展,来从输出的bundle中排除依赖。然后在index.html通过script标签引用。这个不仅可以提升打包的速度,也可以缩小bundle的体积,从而提升http请求的速度来提高首屏渲染速度。
// webpack.prod.js
module.exports = {
  externals: {
    jquery: ‘jQuery‘,
    ...obj
  }
}
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>externals</title>
  <script src="https://code.jquery.com/jquery-3.1.0.js"></script>
</head>
<body>
  
</body>
</html>
// index.js

import $ from ‘jquery‘;

console.log($); // 输出成功,代表配置成功
  • ??但是要注意的是,在使用Externals的时候,如果我们更新了我们的代码依赖包的话,比如说通过npm undate <package name>更新的话,我们还要把我们的第三方依赖包更新一下版本在通过script去引入,比较麻烦。可以通过一些webpack插件去自动生成CDN Link Script标签。(有兴趣的话可以继续去了解)。
  • ??还有一点是使用DllPlugin生成动态链接库的时候,在我们更新第三方依赖的时候,如果涉及到我们抽离出来的第三方包的话,我们首先要执行npm run build:dll重新生成manifest.json,才不会出错。

六、微前端实践

管理后台项目较大,每次打包的时候都需要把管理后台重新打包,时间话费巨大。

因此可以把每一个子模块都被拆分成了一个单独的repo,通过微前端架构实践通过portal配置不同的模块入口。来把不同模块的分开打包,最后生成一个整体的代码结构。有兴趣的可以去看看。

需要考虑的是:

  • 如何解决路由的问题
  • 公共模块如何维护的问题
  • ......等一些问题

七、总结

综上所述,一般构建优化优化措施的两个大方向:缓存(cache-loader)、多线程(happy-pack)。缓存是为了让二次构建时,不再去做重复的工作;而多核,是利用了硬件本身的优势,让我们充分发挥出cpu的性能。当然随着webpack的发展,会涌现出更多的优化插件以及方向。

以上是关于webpack构建优化—dll的主要内容,如果未能解决你的问题,请参考以下文章

webpack构建速度优化

webpack构建速度优化

webpack构建优化

webpack构建优化

webpack构建优化—dll

Webpack构建性能优化指南