umi打包编译优化

项目分析

痛点

  1. 项目打包后的体积很大,打包时间也很长,首次打包时长预计8min-20min,导致每次遇到紧急线上问题或者紧急需求时,开发两分钟,发布两小时。整个发布时长拉长,用户体验不好
  2. 项目启动非常慢,热更新非常慢,开发效率大大减少

分析

  1. 原项目总的打包体积为****146.83MB!! ,压缩后也****38.9MB!! ,体积非常大。
  1. 很多公共的模块出现在多个chunk 里面,比如antd相关等
  1. 启动时间非常慢,热更新速度非常慢,影响开发效率,启动速度大约164s/3min~ ,热更新大约16s

体积优化

step1:升级到umi3.5.0

v3.umijs.org/zh-CN/docs/...

umi版本较低,许多新特性无法使用,修改 umi 的版本为 ^3.5.0 或以上

json 复制代码
{
  "devDependencies": {
-    "umi": "^3.2.0-beta.6",
+   "umi": "^3.5.0",
  }
}

step2:提取公共依赖

调整 splitChunks 策略,增加公共依赖的提取,减少整体尺寸

php 复制代码
  // 如果开了 dynamicImport,然后产物特别大,每个出口文件都包含了相同的依赖,比如 antd,可尝试通过 splitChunks 配置调整公共依赖的提取策略。
  dynamicImport: {
    loading: '@/components/Loading',
  },
  chunks: ['vendors', 'umi'],
  chainWebpack: function(config) {
    config.merge({
      optimization: {
        splitChunks: {
          chunks: 'all',
          minSize: 30000,
          minChunks: 3,
          automaticNameDelimiter: '.',
          cacheGroups: {
            vendor: {
              name: 'vendors',
              test({ resource }) {
                return /[\/]node_modules[\/]/.test(resource);
              },
              priority: 10,
            },
          },
        },
      },
    });
  },
  • 首次见效

做完以上两步骤后可以看出,以上修改后包体积大大减少,提升率达到91%!!

step3:删除多余依赖

项目中一些依赖包没有用到,但仍然安装,可以删除这些不必要的依赖包

json 复制代码
{
  "dependencies": {
    -"add": "^2.0.6",
    -"behooks": "0.8.3",
  },
  "devDependencies": {
    -"webpack": "^4.43.0"
  },
}

step4:优化冗余依赖(暂时注释)

对于一些大尺寸依赖,比如图表库、antd 等,可尝试通过 externals 的配置引入相关 umd 文件,减少编译消耗

xlsx/react-dom/react

不再使用npm方式安装xlsx 通过externals 引入cdn链接

xml 复制代码
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.16.9/xlsx.mini.min.js"  integrity="sha512-bF+HLFD/vkMdiECsWsJfRUkaQObWj/BhCE4E9eDfZL2VN6HDh1JKMzRFs0uOPUwic71Zvodr5imoD7eT3eVdGA==" crossorigin="anonymous"></script>
rust 复制代码
  externals: {
    react: 'window.React',
    'react-dom': 'window.ReactDOM',
    xlsx: 'XLSX',
  },
  // 区分 development 和 production,使用不同的产物
  scripts:
    process.env.NODE_ENV === 'development'
      ? [
          'https://gw.alipayobjects.com/os/lib/react/17.0.0/umd/react.development.js',
          'https://gw.alipayobjects.com/os/lib/react-dom/17.0.0/umd/react-dom.development.js',
        ]
      : [
          'https://gw.alipayobjects.com/os/lib/react/17.0.0/umd/react.production.min.js',
          'https://gw.alipayobjects.com/os/lib/react-dom/17.0.0/umd/react-dom.production.min.js',
        ],

通过externals引入后,xlsx/react-dom/react依赖将不再占用包内存! 大约减少了5mb!

由于外部cdn可能挂掉,这项优化项暂时不做~

moment.js

忽略 moment 的 locale 文件,用于减少尺寸

yaml 复制代码
 ignoreMomentLocale: true,

优化前大小:1.05mb、压缩大小:271.68kb

优化前大小:118.41kb、压缩大小:37.73kb

可以看出moment体积大大减小!!

step5:开启gzip压缩

需要Nginx开启Gzip属性

复制代码
npm install compression-webpack-plugin
javascript 复制代码
const CompressionWebpackPlugin = require('compression-webpack-plugin');

// 开启gzip压缩
config.plugin('CompressionPlugin').use(
  new CompressionWebpackPlugin({
    test: /.(js|css)$/,
    threshold: 10240, // 超过10kb的文件就压缩
    deleteOriginalAssets: false, // 不删除源文件
    minRatio: 0.8,
  }),
);

编译提速

webpack4升级webpack5

webpack.docschina.org/migrate/5/

webpack5的加载速度比webpack4加载更快

css 复制代码
  webpack5: {},

esbuild

编译慢中压缩时间占了大部分,所以如果编译时不压缩可节约大量的时间和内存消耗,但尺寸会增加不少。替换terser压缩器为esbuild,来提高编译速度。

css 复制代码
 esbuild: {},

fastRefresh

开发环境下,可以保持组件状态 ,同时编辑提供即时反馈

css 复制代码
  fastRefresh: {}, 

nodeModulesTransform

Umi 默认编译 node_modules 下的文件,带来一些收益的同时,也增加了额外的编译时间。如果不希望 node_modules 下的文件走 babel 编译,可通过以下配置减少 40% 到 60% 的编译时间

css 复制代码
 nodeModulesTransform: {
      type: 'none',
      exclude: [],
    },

mfsu

arduino 复制代码
  // 有缓存时启动 1s+,热更新平均 500ms 内
  mfsu: {},

分析对比

产物分布对比

未压缩体积对比

已压缩体积对比

首次启动速度对比

二次启动速度对比

热更新速度对比

tone首次发布时长对比

花费预计10分钟


花费预计5分钟

tone二次发布时长对比

花费预计4分钟


花费预计1分钟

总结

未压缩体积m 已压缩体积m 首次启动速度s 二次启动速度s 热更新速度s tone首次发布时min tone二次发布时长min
优化前 146.83 38.9 141 98.4 7 10 4
优化后 9.89 2.78 9 3.7 2 5 1
提升率% 93.26431928 92.85347044 93.61702128 96.2398374 71.42857143 50 75

可以看出数据呈现直线下降趋势,效率大大提高!或许一些数据只提升了几分钟甚至几秒钟的效率,但当我们在开发一个版本时,不停修改保存打包代码,次数不断累加时,就会发现效率产生质的飞跃!

过程问题

  • 开启了mfsu报错:Unhandled Rejection (ChunkLoadError): Loading chunk mf-dep_vendors failed.

将splitChunks策略放在生产环境执行

  • tone构建出现告警:(node:45214) [DEP_WEBPACK_CONFIGURATION_OPTIMIZATION_NO_EMIT_ON_ERRORS] DeprecationWarning: optimization.noEmitOnErrors,压缩css失败 optimization,optimize-css-assets-webpack-plugin插件与webpack5不兼容引起的警告,但不影响压缩

webpack5中同等功能的插件是css-minimizer-webpack-plugin,安装并修改配置

由于无法下载css-minimizer-webpack-plugin 暂未使用

csharp 复制代码
yarn add -D css-minimizer-webpack-plugin
yaml 复制代码
optimization: {
    minimize: true,
    minimizer: [
        new CssMinimizerPlugin(),
    ],
}
  • 时间组件全部变成英文格式

由于配置了 ignoreMomentLocale: true 忽略了moment.js所有语言包,需要单独在app.js导入中文语言包

javascript 复制代码
import moment from 'moment'
// 导入中文语言包
import 'moment/locale/zh-cn';
// 设置中文
moment.locale('zh-cn');

umirc.ts配置

javascript 复制代码
import { defineConfig, IConfig } from 'umi';
import routes from './routes';
import path from 'path';
const CompressionWebpackPlugin = require('compression-webpack-plugin')

// 生产环境配置
let configPro = {};
if (process.env.NODE_ENV !== 'development') {
  // 开启了mfsu报错:Unhandled Rejection (ChunkLoadError): Loading chunk mf-dep_vendors failed.
  // splitChunks 策略 只能放在生产环境
  configPro = {
    // Umi 默认编译 node_modules 下的文件,带来一些收益的同时,也增加了额外的编译时间。如果不希望 node_modules 下的文件走 babel 编译,可通过以下配置减少 40% 到 60% 的编译时间
    nodeModulesTransform: {
      type: 'none',
      exclude: [],
    },
    chunks: ['vendors', 'umi'],
    chainWebpack: function(config: any) {
      if (process.env.UMI_ENV !== 'development') {
        config.merge({
          optimization: {
            // 提取公共依赖,调整 splitChunks 策略,减少整体尺寸
            splitChunks: {
              chunks: 'all',
              minSize: 30000,
              minChunks: 3,
              automaticNameDelimiter: '.',
              cacheGroups: {
                vendor: {
                  name: 'vendors',
                  test({ resource }) {
                    return /[\/]node_modules[\/]/.test(resource);
                  },
                  priority: 10,
                },
              },
            },
          },
        });
        // 开启gzip压缩
        config.plugin('CompressionPlugin').use(
          new CompressionWebpackPlugin({
            test: /.(js|css)$/,
            threshold: 10240, // 超过10kb的文件就压缩
            deleteOriginalAssets: false, // 不删除源文件
            minRatio: 0.8
          })
        )
      }
    },
    // 替换压缩器为 esbuild
    esbuild: {},
  };
}

const config: IConfig = {
  title: '陆运通运营后台',
  antd: {},
  dva: {},
  hash: true,
  history: {
    type: 'hash',
  },
  publicPath: '/s/lytAdminSite/',
  theme: {
    'primary-color': '#2B82D8',
  },
  routes,
  alias: {
    '@': path.resolve(__dirname, 'src'),
    // TODO  本地开发使用的
    // 'tf-exportFile/libs': path.join(__dirname, 'dev-npm/lyt-components/tf-exportFile/src/components/index'),
    // 'tf-traceService/libs': path.join(__dirname, 'dev-npm/lyt-components/tf-traceService/src/components/index'),
    // 'tf-lookHd/libs': path.join(__dirname, 'dev-npm/lyt-components/tf-lookHd/src/components/index.js'),
  },
  devServer: {
    host: '0.0.0.0',
    port: 4000,
    proxy: {
      '/landTransAdmin': {
        target: 'https://optest.tf56.com',
        // target: 'http://optest.tf56.lo',  //容器云环境
        // target: 'http://10.50.9.154:8080',
        changeOrigin: true,
      },
    },
    https: {
      key: './public/mytest.tf56.com-key.pem',
      cert: './public/mytest.tf56.com.pem',
    },
  },

  /**-------------------------------------编译提速配置-------------------------------- */
  // 如果开了 dynamicImport,然后产物特别大,每个出口文件都包含了相同的依赖,比如 antd,可尝试通过 splitChunks 配置调整公共依赖的提取策略。
  dynamicImport: {
    loading: '@/components/Loading',
  },

  // 忽略 moment 的 locale 文件,用于减少尺寸。
  ignoreMomentLocale: true,

  // 快速刷新
  fastRefresh: {},
  // 对于一些大尺寸依赖,比如图表库、antd 等,可尝试通过 externals 的配置引入相关 umd 文件,减少编译消耗。
  // externals: {
  //   "react": 'window.React',
  //   'react-dom': 'window.ReactDOM',
  //   'xlsx': 'XLSX'
  // },
  // 区分 development 和 production,使用不同的产物
  // scripts:
  //   process.env.NODE_ENV === 'development'
  //     ? [
  //         'https://gw.alipayobjects.com/os/lib/react/17.0.0/umd/react.development.js',
  //         'https://gw.alipayobjects.com/os/lib/react-dom/17.0.0/umd/react-dom.development.js',
  //       ]
  //     : [
  //         'https://gw.alipayobjects.com/os/lib/react/17.0.0/umd/react.production.min.js',
  //         'https://gw.alipayobjects.com/os/lib/react-dom/17.0.0/umd/react-dom.production.min.js',
  //       ],

  // 有缓存时启动 1s+,热更新平均 500ms 内
  mfsu: {},
  webpack5: {},
  ...configPro,
};

export default defineConfig(config);
相关推荐
跑调却靠谱16 分钟前
elementUI调整滚动条高度后与固定列冲突问题解决
前端·vue.js·elementui
呵呵哒( ̄▽ ̄)"33 分钟前
React - 编写选择礼物组件
前端·javascript·react.js
Coding的叶子38 分钟前
React Flow 简介:构建交互式流程图的最佳工具
前端·react.js·流程图·fgai·react agent
apcipot_rain6 小时前
【应用密码学】实验五 公钥密码2——ECC
前端·数据库·python
ShallowLin6 小时前
vue3学习——组合式 API:生命周期钩子
前端·javascript·vue.js
Nejosi_念旧6 小时前
Vue API 、element-plus自动导入插件
前端·javascript·vue.js
互联网搬砖老肖6 小时前
Web 架构之攻击应急方案
前端·架构
pixle07 小时前
Vue3 Echarts 3D饼图(3D环形图)实现讲解附带源码
前端·3d·echarts
麻芝汤圆7 小时前
MapReduce 入门实战:WordCount 程序
大数据·前端·javascript·ajax·spark·mapreduce
juruiyuan1119 小时前
FFmpeg3.4 libavcodec协议框架增加新的decode协议
前端