在使用react-native
进行开发的过程中,也许会因为开发阶段的忙碌而忽略一些小细节,导致最后打包出来的bundle size不尽如人意。
本文将提出在JavaScript
层面,优化react-native
最终打包尺寸的几种方案。
裁剪具体模块的尺寸
首先是进行具体模块的剪裁,因为在开发阶段会忙于实现功能而忽略掉一些package的引用。
在这里我们需要用到对于react-native
的bundle可视化工具,针对可视化的结果来进行具体模块/文件内容的优化。
bundle-analyzer
在react-native
中推荐使用react-native-bundle-visualizer进行bundle的查看。
执行如下命令进行安装:
yarn add --dev react-native-bundle-visualizer
并运行它:
yarn run react-native-bundle-visualizer
如果没有跑出具体内容,则需要手动添加入口文件。例如,--entry-file ./index.android.js。
执行结果如下图的官方介绍所示,会把node_modules
和源文件中打包出来的代码尺寸都包含在内,可以清晰地看出哪些文件占用的空间比较大。
react-native-bundle-visualizer
的原理是使用了source-map-explorer
进行了Metro bundler
的可视化输出。
- Metro:
Metor
是React Native
官方的打包程序,会生成对应的bundle文件。 - Source-map-explorer: 在
react
中或者是使用webpack
等工具打包出来的内容,都可以使用与source-map-explorer
相关的一些打包分析工具进行可视化内容查看。
使用bundle分析工具,可以比较明显地辨识出哪些业务文件大小比较异常、需要进行优化,或者是引用了哪些Javascript库,导致bundle膨胀。
在实际的开发中,我们通常会发现如下两个package
的尺寸很大,而且在代码中的运用也许不是很多,那么我们需要针对lodash和moment等库进行替换。
Momentjs、Lodashjs库的替换
在进行模块的替换之前,我们需要用到一个插件Import Cost
。
Import Cost衡量引入模块代价
VSCode插件 Import Cost 可以衡量当前引入文件大小。其他IDE也有该插件,或者可以直接从npm进行安装使用。Import Cost VSCode插件 - 从VSCode市场进行安装。
第三方Import Cost IDE插件
- Jetbrains IDE Plugin
- Atom Package
- Vim Plugin (coc.nvim extension)
- Vim Plugin
该插件可以计算import
和require
引用的模块大小,目前支持:
- 默认import:
import Func from 'utils';
- 整个文件import:
import * as Utils from 'utils';
- 部分import:
import {Func} from 'utils';
- 别名方式的部分import:
import {orig as alias} from 'utils';
- 子模块import:
import Func from 'utils/Func';
- Require:
const Func = require('utils').Func;
- 支持
Javascript
与Typescript
moment存在的问题与解决
moment是一个常用的JavaScript日期处理类库,它支持多语言的日期格式化。
moment的核心代码只有52kb,但是包含了全世界语言的本地化文件,也就是说当你使用其中的功能时,也包含了很多你用不到的特性。
对应的解决方案是你可以通过npm安装moment-mini,该库非官方维护,但暴露了官方的moment-min.js
作为npm模块开源使用。
或者你可以直接使用一些更为简洁的JavaScript日期格式化类库。
作为momentjs的替代方案,你可以使用以下3个类库,或者直接使用JavaScript的原生API来做日期国际化:
luxon
date-fns
day.js
JavaScript Internationalization API
如下对比来自You-Dont-Need-Momentjs。
Name | Size original/gzipped | Tree-shaking | Popularity (stars) | Methods richness | Pattern | Timezone Support | Locale |
---|---|---|---|---|---|---|---|
Moment.js | 329K/69.6K | No | 43.4k | High | OO | Good (moment-timezone) | 123 |
Luxon | 59.9K/17.2K | No | 9k | High | OO | Good (Intl) | - |
date-fns | 78.4k/13.4k without tree-shaking | Yes | 21.3k | High | Functional | Good (date-fns-tz) | 64 |
dayjs | 6.5k/2.6k without plugins | No | 25.8k | High | OO | Not yet | 130 |
如果不需要引入日期国际化,dayjs核心代码只有7.1k,可以作为momentjs的替代。
lodash存在的问题与解决
lodash是一个实用性非常高的JavaScript工具库,可以对array、object、string等值进行操作和检测等等,还具有一些非常实用的函数。
但lodash类库所占用的空间达到了71K,而且也存在很多你用不上的方法。实际上,我们在使用中或许只会用到非常少的几个函数。
官方虽然也提供了lodash-cli
这样的工具,让使用方可以针对具体的某些函数进行打包,但官方是不推荐这种用法的,并且在新的版本中也取消了这样的部分模块打包方式。
官方推荐的方式是,在引用时指定对应的函数,这样最终打包时只会打包对应的函数。
如下所示,如果直接引用lodash,大小时71K。
import get from 'lodash' // 71K (gzipped: 24.7K)
如果引用对应的函数,那么所需要的空间会大大减少。
import get from 'lodash/get' // 8.2K (gzipped: 2.5K)
但lodash依然有很多存在依赖关系的内部函数需要一起打包进去。
如果你仅仅是使用到这个实用库类的部分工具函数,那么可以用一些体积更小的工具包进行优化,或者直接使用对应的原生实现方式进行替换。
在You don't (may not) need Lodash/Underscore这个项目中,作者罗列了可以直接替代Lodash/Underscore库类中函数的原生代码实现。例如debounce函数。
Create a new function that calls func with thisArg and args.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
timeout = null;
if (!immediate) func.apply(context, args);
}, wait);
if (immediate && !timeout) func.apply(context, args);
};
}
// Avoid costly calculations while the window size is in flux.
jQuery(window).on('resize', debounce(calculateLayout, 150));
把项目中涉及到的工具库类函数直接用原生代码替换,不失为一个很好的解决方案。
替换不必要的模块
这里的替换掉不必要的组件/模块,更多地是从业务逻辑方面来说的。
如果已经引用的库里面存在某些业务逻辑功能,或者有公用的组件已经实现了对应的功能,那么我们应该进行替换,删除掉多余的业务内代码。
同样的,检查下package.json
文件中也许会存在未使用的包,或者是重复功能。
在开发阶段,也许会存在引用了某些库类,随着业务变化,又在具体逻辑中删除了引用,但未清除彻底,导致package中还有残余,却给bundle size带来了一定的负担。
也或者是同上面lodash和moment库,可以通过用一些更简单的库,或者自己实现几个常用功能来进行整个模块的替换。
总结如下:
-
使用现有组件替换不必要的组件/模块;
-
优化package.json,删除不必要的node_modules模块,使用其他模块替代。
优化base64图片大小
在优化代码的过程中发现,文件中存在base64过长的问题,甚至是base64出现在了一些不符合使用base64场景的地方。
base64适用场景
base64更适合出现在一些重复使用的背景图片,或者尺寸极小的ICON的情形,而一些较大的图片则适合使用PNG或者JPEG。
PNG是无损的,JPEG是有损的。如果不需要背景透明,那么把PNG转换为JPEG会更节省空间。
如何优化base64图片
- 剪裁图片大小
设计师给出的图片一般会比较大,而实际应用中不需要这么大的图片,可以适当地进行图片大小的裁剪。
可以使用在线的PS工具进行图片的裁剪,如果是MAC可以直接使用自带的软件进行图片大小的裁剪。
- 压缩图片质量
笔者一般会使用tinypng进行图片压缩,反复上传直到无法压缩为止。
VSCode中也有使用该网站进行快速压缩的插件。
该网站压缩效果比较好,一般来说肉眼看不出压缩前后差别。
经过以上两个步骤以后,base64的图片字节数会明显减少很多。如果字节数还是很大,那么应该考虑是否不适合使用base64进行展示。
总结
进行bundle分析后,可以明显地找出尺寸异常的文件或者模块,进行对应的优化,从大的层面上进行分析与尺寸优化:
- 根据bundle分析裁剪具体模块,结合Import Cost分析模块引用代价;
- 替换不合理的库类引用
粗粒度的优化后,剩下的有关逻辑的代码优化,就跟平时的编写有关。从小的层面上进行优化需要:
- 从逻辑上分析不必要存在的库类/模块引用;
- 编写逻辑代码时,需要更加注重保持代码行数的简洁;
- 提取常用功能为公用组件进行使用