webpack构建速度和构建体积优化策略

webpack构建速度和构建体积优化策略

初级分析:使用webpack内置的stats

stats:构建的统计信息

package.json中使用stats 输出一个stats.json文件

"scripts": {
  "build:stats": "webapck --env production --json > stats.json"
} 

使用speed-measure-webpack-plugin

可以看到每个loader和plugin执行耗时

使用webpack-bundle-analyzer分析体积

构建完成后会在8888端口展示大小

可以分析依赖的第三方模块文件大小,业务里面组件代码大小

速度优化

V8 带来的优化(for of替代foreach,Map和Set替代Object,includes替代indexOf,默认使用更快的md4 hash算法,webpack AST可以直接从loader传递给AST,减少解析时间,使用字符串方法替代正则表达式)

  • 使用高版本的webpack和Node.js

  • 多进程多实例构建:资源并行解析可选方案

    • thread-loader 可选方案:parallel-webpack/HappyPack
  • 多进程/多实例:并行压缩

    • 使用parallel-uglify-plugin插件
    • uglifyjs-webpack-plugin开启parallel参数(不支持es6的语法压缩)
    • terser-webpack-plugin开启parallel参数(支持es6压缩,推荐使用)
  • 分包:设置Externals

    思路:将react,react-dom基础包通过cdn引入,不打入bundle中

    • 方法:使用html-webpack-externals-plugin
  • 进一步分包

    思路:将react,react-dom,redux,react-redux基础包和业务基础包打成一个文件

    • 进一步分包:进阶使用预编译资源模块
    • 使用DLLPlugin进行分包,DllReferencePlugin对manifest.json(描述文件)引用
    • 使用DLLPlugin进行分包 webpack.dll.json 文件
    const path = require("path");
    const webpack = require("webpack");
    
    module.exports = {
      context: process.cwd(),
      resolve: {
        extensions: ['.js', '.jsx', '.json', '.less', '.css'],
        modules: [__dirname, 'node_modules']
      },
      entry: {
        library: [
          'react',
          'react-dom',
          'redux',
          'react-redux'
        ]
      },
      output: {
        filename: '[name]_[chunkhash].dll.js',
        path: path.resolve(__dirname, './build/library'),
        library:'[name]'
      },
      plugins: [
        new webpack.DllPlugin({
          name: '[name]',
          path: './build/library/[name].json'
        })
      ]
    }
    
  • 使用缓存

    目的:提升二次构建速度

    缓存思路:提升模块转换io速度

    • babel-loader开启缓存
    new HappyPack({
      loaders: ['babel-loader?cacheDirectory=true']
    })  // 开启babel缓存
    
    • terser-webpack-plugin 开启压缩缓存
    optimization: {
      minimizer: {
        new TerserPlugin({
          parallel: true,
          cache: true
        })
      }
    }
    
    • 使用cache-loader 或者 hard-source-webpack-plugin
    const HardSourceWebpackPlugin = require('hard-source-webpack-plugin'); 
    
    modules.exports = {
      ...
      plugins: [
        new HardSourceWebpackPlugin()
      ]
    }
    
  • 缩小构建目标

    目的:尽可能的少构建模块,比如babel-loader 不解析 node_modules

    module.exports = {
      rules: {
        test: /\.js$/,
        loader: 'happypack/loader',
        exclude: 'node_modules'
      }
    }
    
  • 减少文件搜索范围

    • 优化resolve.modules 配置(较少模块搜索层级)
    • 优化resolve.mainFields配置
    • 优化resolve.extensions配置
    • 合理使用alias
    module.exports = {
      resolve: {
        alias: {
          react: path.resolve(__dirname, './node_modules/react/dist/react.min.js')
        },
        modules: [path.resolve(__dirname, 'node_modules')],
        extensions:['.js'],  // 默认找.js .json文件
        mainFields:['main']  // 只查找 package.json的 main 文件
      }
    }
    
  • 使用Tree Shaking擦除无用的JavaScript和CSS

    概念:1个模块可能有多个方法,只要其中的某个方法使用到了,则整个文件都会被打到bundle里面去,tree shaking就是只把用到的方法打入bundle,没用到的方法会在uglify阶段被擦除掉。

    使用:webpack默认支持,在.babelrc里面设置modules:false即可,production mode的情况下默认开启

    • PurifyCSS:遍历代码,识别已经用到的CSS class
    • uncss:HTML需要通过jsdom加载,所有的样式通过PostCSS解析,通过document.querySelector来识别在html文件里面不存在的选择器

    使用 purgecss-webpack-plugin和mini-css-extract-plugin配合使用

  • 使用webpack进行图片压缩

    要求Node库的imagemin或者tinypng API,使用:配置image-webpack-loader

    imagemin的压缩原理,pngquant:是一款png压缩器,通过将图像转换为具有alpha通道(通常比24/32位PNG文件小60-80%)的更高效的8位PNG格式,可显著减少文件大小

    pngcrush:其主要目的是通过尝试不同的压缩级别和PNG过滤方法来降低PNG IDAT数据流的大小

    tinypng:将24位png文件转换为更小有索引的8位图片,同时所有非必要的metadata也会被剥离掉

    {
      test: /.(png|jpg|gif|jpeg)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name]_[hash:8].[ext]'
            }
          },
          {
            loader: 'image-webpack-loader',
            options: {
              mozjpeg: {
                progressive: true,
                quality: 65
              },
              // optipng.enabled: false will disable optipng
              optipng: {
                enabled: false,
              },
              pngquant: {
                quality: '65-90',
                speed: 4
              },
              gifsicle: {
                interlaced: false,
              },
              // the webp option will enable WEBP
              webp: {
                quality: 75
              }
            }
          }
    
  • 构建体积优化: 动态Polyfill babel-polyfill占比29.6%很大

    关于什么是polyfill,请点击链接

    方案优点缺点是否采用
    babel-polyfill简单方便1.包体积较大,难以单独抽离Map,Set
    2.每一个单独的项目都需要手动引用
    babel-plugin-transform-runtime能只polyfill用到的类或者方法,相对体积较小不能polyfill原型上的方法,不适用于业务项目的复杂开发环境
    自己写Map,Set的polyfill定制化高,体积小1.浪费时间,重复造轮子,容易在日后年久失修留坑
    2.即使体积小,依然所有用户都要加载
    polyfill-service只给用户返回需要的polyfill,社区维护部分国内的浏览器UA可能无法识别(但是可以降级返回全部polyfill)

    Polyfill Service原理:识别User Agent,下发不同的Polyfill

    polyfill.io 官方提供的服务

    <script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
    
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MaxLoongLvs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值