# 性能优化篇(五)

# 性能优化之webpack4

# 深度treeshaking

  • webpack-deep-scope-plugin
  • cssnano 基于postCss压缩css
  • purifycss-webpack
//压缩css  使用cssnano
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const _modeflag = _mode === 'production' ? true : false;
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module: {
  rules: [
    {
      test: /\.css$/i,
      use: [
        'cache-loader',
        MiniCssExtractPlugin.loader,
        { loader: 'css-loader', options: { importLoaders: 1 } },
        'postcss-loader',
      ],
    },
  ],
},
plugins: [
  new MiniCssExtractPlugin({//提取css
    filename: _modeflag
    ? 'styles/[name].[contenthash:5].css'
    : 'styles/[name].css',
    chunkFilename: _modeflag
    ? 'styles/[name].[contenthash:5].css'
    : 'styles/[name].css',
    ignoreOrder: false,
  }),
  new OptimizeCssAssetsPlugin({
    assetNameRegExp: /\.css$/g,
    cssProcessor: require('cssnano'),
    cssProcessorPluginOptions: {
      preset: ['default', { discardComments: { removeAll: true } }],
    },
    canPrint: true,
  }),
],
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

js部分在webpack5中已经有集成,css目前还是得自己处理

# webpack多线程

开启多核压缩 happypack 多线程编译webpack 不支持的情况下使用thread-loader(慎用)

{
        test: /\.js$/,
        include: resolve('src'),
        //1.电脑配置强悍
        //2.确实是因为loader 分析出来 影响了编译
        use: [
          {
            loader: 'thread-loader',
            options: {
              workers: 3,
              workerParallelJobs: 50,
              workerNodeArgs: ['--max-old-space-size=1024'],
              poolRespawn: false,
              poolTimeout: 2000,
              poolParallelJobs: 50,
              name: 'my-pool',
            },
          },
        ],
      },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

JavaScript的多核压缩可以开启terser-webpack-plugin (多核压缩uglifyjs-webpack- plugin 官方维护 非官方维护webpack-parallel-uglify-plugin(这块儿对Css没啥用))

//js压缩
const TerserJSPlugin = require('terser-webpack-plugin'); module.exports = {
  optimization: {
    minimizer: [new TerserJSPlugin({
			cache: true, // 是否缓存 
      paraller: true, // 是否并行打包 目前默认值true
      sourceMap: true
		})], 
  }
}
1
2
3
4
5
6
7
8
9
10

webpack5新增了这部分功能

# webpack打包速度分析和缓存

speed-measure-webpack-plugin

const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const smp = new SpeedMeasurePlugin();
module.exports = smp.wrap(merge(webpackConfig, _mergeConfig));
1
2
3

cache-loader分析完事哪个慢缓存哪个

'babel-loader?cacheDirectory=true' exclude: /node_modules/, // 排除不处理的目录 include: path.resolve(__dirname, 'src') // 精确指定 要处理的目录

module: {
  rules: [
    {
      test: /\.css$/i,
      use: [
        'cache-loader',
        MiniCssExtractPlugin.loader,
        { loader: 'css-loader', options: { importLoaders: 1 } },
        'postcss-loader',
      ],
    },
  ],
},
1
2
3
4
5
6
7
8
9
10
11
12
13

对整个工程开启缓存 hard-source-webpack-plugin

webpack5已新增缓存功能----借鉴了hard-source-webpack-plugin

# webpack打包进度展示

progress-bar-webpack-plugin

webpackbar

# webpack外部扩展

防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)

例如,从 CDN 引入 jQuery,而不是把它打包:

index.html

<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous">
</script>
1
2
3
4
5

webpack.config.js

module.exports = {
  //...
  externals: {
    jquery: 'jQuery'
  }
};
1
2
3
4
5
6

这样就剥离了那些不需要改动的依赖模块,换句话,下面展示的代码还可以正常运行:

import $ from 'jquery';

$('.my-element').animate(/* ... */);
1
2
3

# webpack集群编译

分模块编译,然后集合。需要掌握免密登录,js shell,webpack区分模块

# 压缩图片

image-webpack-loader

# HtmlWebpackPlugins压缩推荐选项

new HtmlWebpackPlugin({
inlineSource: ".css$",
template: path.join(__dirname, `src/${pageName}/index.html`),
filename: `${pageName}.html`, chunks: ["vendors", pageName], inject: true,
minify: {
    html5: true,
    collapseWhitespace: true,
    preserveLineBreaks: false,
    minifyCSS: true,
    minifyJS: true,
    removeComments: false,
}, });

1
2
3
4
5
6
7
8
9
10
11
12
13

# inline-manifest-webpack-plugin

inline-manifest-webpack-plugin 把runtime放到html里 html-inline-css-webpack- plugin 把一些核心的CSS放到⻚面内部 html-webpack-inline-source-plugin 内部资源 引入

# 代码求值

prepack-webpack-plugin

# 动态引入

@babel/plugin-syntax-dynamic-import

# 输出依赖关系

  • webpack-dashboard 增强了 webpack 的输出,包含依赖的大小、进度和其他细节。

  • webpack-bundle-analyzer 打包结果分析

    const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
      .BundleAnalyzerPlugin;
    new BundleAnalyzerPlugin(),
    
    1
    2
    3
  • webpack --profile --json > stats.json 14-1 ht tp://alexkuz.github.io/webpack-chart/ 14-2 http://webpack.github.io/analyse/

# 工程优化与原理篇

  • 将构建配置设计成一个库,比如:hjs-webpack、Neutrino、webpack-blocks

    Neutrino:快速构建一个项目

  • 抽成一个工具进行管理,比如:create-react-app, kyt, nwb

  • 更友好的提示错误

    • friendly-errors-webpack-plugin 配合webpackdevserver使用

      devServer: {
        historyApiFallback: true,
        contentBase: join(__dirname, '../dist'),
            proxy: {
              '/api': '',
            },
        hot: true,
        quiet: true,
       },
      new FriendlyErrorsWebpackPlugin({
            compilationSuccessInfo: {
              messages: ['You application is running here http://localhost:3000'],
              notes: [
                'Some additionnal notes to be displayed unpon successful compilation',
              ],
            },
            onErrors: function (severity, errors) {
              //安装node-notifier 只想提示错误的话
            },
            quiet: true,
            clearConsole: true,
          }),
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
    • webpack-build-notifier

    const WebpackBuildNotifierPlugin = require('webpack-build-notifier');
    new WebpackBuildNotifierPlugin({
        title: 'cli',
        logo: './favicon.png',
        suppressSuccess: true,
    }),
    
    1
    2
    3
    4
    5
    6
    • set-iterm2-badge && node-bash-title 标题和窗口内容修改
function() {
 this.hooks.done.tap('done', (stats) => {

if (stats.compilation.errors && stats.compilation.errors.length && process.argv.indexOf('--watch') == -1)

{
 console.log('build error'); process.exit(1);

} })

}
1
2
3
4
5
6
7
8
9
10
11
  • splitchunks公用库的代码拆分 去除打包 分离⻚面公用包 html-webpack-externals- plugin

    optimization: {
        runtimeChunk: {
          name: 'runtime',
        },
        splitChunks: {
          chunks: 'async',
          minSize: 30000,
          minChunks: 1,
          maxAsyncRequests: 5,
          maxInitialRequests: 3,
          name: false,
          cacheGroups: {
            commons: {
              chunks: 'initial',
              minChunks: 2,
              maxInitialRequests: 5,
              minSize: 0,
              name: 'commons',
            },
          },
        },
      },
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
  • 使用动态 polyfill 它会根据你的浏览器 UA 头,判断你是否支持某些特性,从而返回给你一个合适的polyfill。https://cdn.polyfill.io/v3/

  • 集成到CI 监控文件的大小 https://github.com/siddharthkp/bundlesize

  • set-iterm2-badge && node-bash-title 标题和窗口内容修改

# webpack提示

@type/webpack npm

# SplitChunks插件配置选项

  • chunks 选项,决定要提取那些模块。

    • 默认是 async :只提取异步加载的模块出来打包到一个文件中。
      • 异步加载的模块:通过 import('xxx') 或 require(['xxx'],() ="{}) 加载的模块。
    • initial :提取同步加载和异步加载模块,如果xxx在项目中异步加载了,也同步 加载了,那么xxx这个模块会被提取两次,分别打包到不同的文件中。
      • 同步加载的模块:通过 import xxx 或 require('xxx') 加载的模块。
    • all :不管异步加载还是同步加载的模块都提取出来,打包到一个文件中。
  • minSize 选项:规定被提取的模块在压缩前的大小最小值,单位为字节,默认为 30000,只有超过了30000字节才会被提取。

  • maxSize 选项:把提取出来的模块打包生成的文件大小不能超过maxSize值,如果超 过了,要对其进行分割并打包生成新的文件。单位为字节,默认为0,表示不限制大 小。

  • minChunks 选项:表示要被提取的模块最小被引用次数,引用次数超过或等于 minChunks值,才能被提取。

  • maxAsyncRequests 选项:最大的按需(异步)加载次数,默认为 6。

  • maxInitialRequests 选项:打包后的入口文件加载时,还能同时加载js文件的数量(包括入口文件),默认为4。

  • 先说一下优先级 maxInitialRequests / maxAsyncRequests< maxSize < minSize 。

  • automaticNameDelimiter 选项:打包生成的js文件名的分割符,默认为 ~ 。

  • name 选项:打包生成js文件的名称。

  • cacheGroups 选项,核心重点,配置提取模块的方案。里面每一项代表一个提取模 块的方案。下面是 cacheGroups 每项中特有的选项,其余选项和外面一致,若cacheGroups 每项中有,就按配置的,没有就使用外面配置的。

  • test 选项:用来匹配要提取的模块的资源路径或名称。值是正则或函数。

    • priority 选项:方案的优先级,值越大表示提取模块时优先采用此方案。默认值 为0。
    • reuseExistingChunk 选项: true / false 。为 true 时,如果当前要提取 的模块,在已经在打包生成的js文件中存在,则将重用该模块,而不是把当前要提 取的模块打包生成新的js文件。
    • enforce 选项: true / false 。为 true 时,忽 略 minSize , minChunks , maxAsyncRequests 和maxInitialRequests 外面选项