umi工程的打包优化

简介: 在SPA应用中,随着项目的推进,功能不断增加,代码量也随之日益增长,导致打包内容也变得更加臃肿,首屏加载慢,部分功能复杂的页面渲染慢,这个时候需要对打包内容进行优化调整。

在SPA应用中,随着项目的推进,功能不断增加,代码量也随之日益增长,导致打包内容也变得更加臃肿,首屏加载慢,部分功能复杂的页面渲染慢,这个时候需要对打包内容进行优化调整。

1. 分析打包内容

打包内容臃肿,想要对症下药,就要先分析下臃肿的原因是什么,umi 内置包模块分析工具 analyze ,通过该工具可以看到打包后各个模块的大小,然后就可以根据实际情况按需进行优化。

通过在package.jsonscripts配置中配置 ANALYZE=1 umi buildANALYZE=1 umi dev开启包分析:

..."scripts": {
"start": "umi dev",
"build": "ANALYZE=1 umi build",
"analyzeDev": "ANALYZE=1 umi dev", // 开发打包分析"analyzeBuild": "ANALYZE=1 umi build", // 生产打包分析"pack": "umi build && npm run packjs",
"packjs": "node scripts/pack.js",
"eslint": "eslint --fix --ext .js --ext .jsx --ext .ts --ext .tsx ./src",
"lint-staged": "lint-staged",
"test": "umi-test",
"test:coverage": "umi-test --coverage"},

执行npm run build命令,会使用analyze默认配置(其他更多配置)自动打开包分析页面,包含打包中所有模块的内容,鼠标悬浮能查看包详细信息,为后续代码拆包进行一个简单的规划,哪些是比较大,需要单独拆出来的。在默认没有代码切割和按需加载配置的情况下,umi最终只会输出一个.js文件,如下图,是一个高达5.11MB (umi默认会对代码进行压缩)的文件:

2. 产物优化

通过包分析可以看到打包后的JS文件体积为 5.11MBgizped 后也有 1.51MB。而且最终的打包产物只有一个JS文件和CSS文件,静态资源的分配需要在文件体积和文件数量之间均衡,太大的单文件和数量太多的小文件,都会降低用户体验,所以这里需要将构建产物进行拆包处理,先拆成小的模块。

1. 按需加载-配置dynamicImport

umi自带的配置项,默认关闭,该情况下只输出一个JS和CSS文件,即:umi.jsum.css 该配置可以实现按需加载资源:

是否启用按需加载,即是否把构建产物进行拆分,在需要的时候下载额外的 JS 再执行。

当项目越来越庞大时,构建的文件也会越来越大,虽然默认的构建产物简单,部署方便,但是带来的结果就是加载资源慢,导致白屏时间长,用户体验差。下面进行按需加载的拆包配置

...// 只在生产环境下进行按需加载dynamicImport: process.env.NODE_ENV==='production'? {
loading: '@/Loading'} : undefined,
  • 这里的loading配置为一个路径,指向一个loading组件文件,自定义构建在/src目录下即可。

进行按需加载的拆包后,输出的文件如下:

可以发现,这里的umi.js文件小了很多,而且打包的产物多了很多文件,但是大部分文件中都包含一些重复的node_modules/目录下的文件,需要对这些文件再次进行拆包抽离的优化。

2. 代码切割,减少包尺寸-配置webpack-splitChunks

splitChunks为webpack自带的配置项,可以提取一些公共依赖,将复用的antd、echarts以及node_moudles目录下的其他文件进行抽离:

chunks: ['echarts', 'vendors', 'antd', 'umi'],
chainWebpack(memo) {
memo.optimization.splitChunks({
chunks: 'all', //async异步代码分割 initial同步代码分割 all同步异步分割都开启automaticNameDelimiter: '.',
name: true,
minSize: 30000, // 引入的文件大于30kb才进行分割//maxSize: 50000, // 50kb,尝试将大于50kb的文件拆分成n个50kb的文件minChunks: 1, // 模块至少使用次数// maxAsyncRequests: 5,    // 同时加载的模块数量最多是5个,只分割出同时引入的前5个文件// maxInitialRequests: 3,  // 首页加载的时候引入的文件最多3个// name: true,             // 缓存组里面的filename生效,覆盖默认命名cacheGroups: {
echarts: {
name: 'echarts',
test: /[\\/]node_modules[\\/](echarts)[\\/]/,
priority: -9,
enforce: true,
      },
antd: {
name: 'antd',
test: /[\\/]node_modules[\\/](@ant-design|antd|antd-mobile)[\\/]/,
priority: -10,
enforce: true,
      },
vendors: {
name: 'vendors',
test: /[\\/]node_modules[\\/]/,
priority: -11,
enforce: true,
      },
    },
  });
},
  • 需要注意,这里的chunks配置表示文件加载顺序,注意文件间的依赖关系加载文件。

公共依赖抽取之后,打包后的文件资源如下,打包后的文件从7.64MB缩小到了5.6MB,同时抽取了公共文件,可以进行缓存处理

3. 大的三方包从 cdn 引入-配置externals

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

该配置可以将一些公共的三方包以CDN的方式引入,不再打包到静态资源文件中,进一步减小构建产物的体积,也在一定程度上缓解服务器压力

...// 配置 externalexternals: {
'react': 'window.React',
'react-dom': 'window.ReactDOM',
},
// 引入被 external 库的 scripts// 区分 development 和 production,使用不同的产物scripts: process.env.NODE_ENV==='development'? [
'https://gw.alipayobjects.com/os/lib/react/16.14.0/umd/react.development.js',
'https://gw.alipayobjects.com/os/lib/react-dom/16.14.0/umd/react-dom.development.js',
] : [
'https://gw.alipayobjects.com/os/lib/react/16.14.0/umd/react.production.min.js',
'https://gw.alipayobjects.com/os/lib/react-dom/16.14.0/umd/react-dom.production.min.js',
],

配置完成后, .html 文件中引入资源的方式就会编程cdn外链的方式来引入:

  • 该方案不适合内网项目,无法访问外部资源的部署环境

至此,大部分的优化都已经结束了,vendor.js中的文件可以进行进行抽离优化,因为浏览器同域名下请求资源的数量是有上限的,所以在减小静态资源文件的大小的同时,控制好静态资源的数量。

3. 实现预加载

前文说到了按需加载的拆包方案,使静态资源需要的时候再进行加载,但是如果某个页面过于复杂,打包后该页面的JS文件也会比较大,需要点击该页面的时候才能加载,这同样会带来一个问题,虽然有loading,但是体验也并不太好,是如果在进入系统后在浏览器空闲时间能够预加载一些比较庞大的页面,那就达到需求了。

1. 按需加载组件dynamic

dynamic是umi自带的API,用于实现一个异步加载的组件:

importReactfrom'react';
import { Spin } from'antd';
import { dynamic } from'umi';
exportdefaultdynamic({
loader: asyncfunction () {
const { default: AddEdit } =awaitimport(/* webpackChunkName: "examineAddEdit" */'./AddEdit');
returnAddEdit;
  },
loading: () => (
<divstyle={{ width: '100vh', height: '100vh', paddingTop: '20vh', display: 'flex', justifyContent: 'center', alignItems: 'center' }}><Spintip="页面加载中..."/></div>  )
});

这里的关键是动态的import(),借用网上的一段译文来了解下import()函数:

ES2015 Loader 规范 定义了 import() 方法,可以异步的、动态的加载模块,与所加载的模块没有静态连接关系,这点也是与import语句不相同之一。import函数的返回值是promise对象,可以使用.then.catch方法进行接收数据处理,import()加载模块成功以后,这个模块会作为一个对象,当作then方法的参数。因此,可以使用对象解构赋值的语法,获取输出接口,其允许模块路径动态生成。import函数可以放在任何地方,因为它是运行时执行的,什么时候执行到它,就什么时候进行指定模块的加载,所以它可以在条件语句和函数中进行动态的加载。

该异步组件可以像其他普通组件一样被调用即可,它会在需要的时候进行异步加载。

2. 魔法注释-Magic Comments

Magic Comments是一种内联注释,通过在 import 中添加注释,可以进行诸如给chunk命名或选择不同模式的操作。

上例中的异步组件即使用了魔法注释的一个webpackChunkName

/* webpackChunkName: "examineAddEdit" */

新 chunk 的名称。 添加此注释后,将单独的给 chunk 命名为 [my-chunk-name].js 而不是 [id].js

除了该属性外,还有其他四个属性分别为:

  • webpackPrefetch:告诉浏览器将来可能需要该资源来进行某些导航跳转。查看指南,了解有关更多信息 how webpackPrefetch works
  • webpackPreload:告诉浏览器在当前导航期间可能需要该资源。 查阅指南,了解有关的更多信息 how webpackPreload works
  • webpackMode:从 webpack 2.6.0 开始,可以指定以不同的模式解析动态导入.
  • webpackInclude:在导入解析(import resolution)过程中,用于匹配的正则表达式。只有匹配到的模块才会被打包
  • webpackExclude:在导入解析(import resolution)过程中,用于匹配的正则表达式。所有匹配到的模块都不会被打包

webpackPrefetch是这里要使用的关键属性,前面说到按需加载的资源,会在进入该路由后再进行静态资源的加载,所以在index.html中看到的大部分都是这样的:

进入页面后再进行静态资源的请求

使用预加载的魔法注释,来实现预加载:

importReactfrom'react';
import { Spin } from'antd';
import { dynamic } from'umi';
exportdefaultdynamic({
loader: asyncfunction () {
const { default: AddEdit } =awaitimport(/* webpackChunkName: "examineAddEdit" *//* webpackPrefetch: true */'./AddEdit');
returnAddEdit;
  },
loading: () => (
<divstyle={{ width: '100vh', height: '100vh', paddingTop: '20vh', display: 'flex', justifyContent: 'center', alignItems: 'center' }}><Spintip="页面加载中..."/></div>  )
});

这种预加载的组件在进入系统的时候就会以<link rel="prefetch" as="script">的形式预拉取代码:

可以看到在进入该页面后请求资源是没有消耗时间的,这尤其适用于非首页的某个路由下的比较复杂的页面,进行资源的预加载,跳转至该页面下会快速呈现出页面。

目录
相关文章
|
5月前
|
JavaScript 中间件
|
5月前
|
前端开发 测试技术 API
vite项目怎么build打包成不同环境的代码?从而适配不同环境api接口
vite项目怎么build打包成不同环境的代码?从而适配不同环境api接口
323 0
|
6月前
|
移动开发 前端开发 JavaScript
uniapp如何打包h5项目
uniapp如何打包h5项目
497 0
|
6月前
|
缓存 前端开发 JavaScript
Vite 打包优化:全面解析与实践
Vite 作为新一代前端构建工具,以其快速开发体验和高效打包能力著称。然而,在实际项目开发中,为了进一步提升性能和用户体验,我们仍需对 Vite 打包进行优化。本文将深入探讨 Vite 打包优化策略,涵盖代码拆分、资源压缩、缓存利用、构建配置等多个方面,并提供实践案例和最佳实践建议,帮助开发者充分释放 Vite 的潜力。
1637 1
|
6月前
|
Java Maven
Maven项目模块打包引入
Maven项目模块打包引入
60 0
|
Linux Android开发 iOS开发
flutter 打包流程
归纳官网各个平台的打包流程,其实都大同小异,根据流程修改下名称、启动图标等配置,执行打包命令即可
|
缓存 JavaScript CDN
关于vite打包优化,你了解多少
关于vite打包优化,你了解多少
|
Web App开发 缓存 前端开发
|
JavaScript 前端开发 Go
esbuild 配置开发环境
esbuild 相信在使用过vite的同学都知道,vite是开发环境使用的是esbuild来进行编译代码的,生成环境打包使用的是rollup,想看rollup的同学,可以查看我的往期文章。(实战 rollup 第一节入门) (rollup 实战第二节 搭建开发环境)(rollup 实战第二节 搭建开发环境)
esbuild 配置开发环境