翻译背景:

最近我在做 React Native 项目的时候遇到了一个很奇怪的问题:IOS 的 release 包竟然比 debug 包要慢,不管是启动加载还是 tab 切换都慢,而且慢好几秒,这让我很不能理解。做过 React Native 项目的人都会知道,release 包一定会比 debug 包快。

通过排查我发现,是引入了本地图片的原因,如果把本地图片改成网络图,加载速度就正常了。

我在网上搜索关于类似的问题时,竟然没有人遇到同样的问题,这让我很费解。当然,可能大家都用的是网络图,也可能大部分人懂原生 app 开发所以知道怎么解决。

通过一番资料查阅,我找到了唯一一篇写 React Native 图片性能的文章,这篇文章讲到了为什么从 javascript 包里加载图片会慢。于是我决定翻译这篇文章。(英语水平有限,如有错误欢迎指出。)

过去的几个月里,我在做 React Native 项目过程中学习到了很多关于图片的东西。大多数项目可能都不会遇到与图片性能相关的问题。但不管怎样,我们最好注意图片的这些问题,才能避免以后发生性能问题。

加载静态资源

静态资源方面,我通过对比图片资源从 app 包中(就是图片通过 Xcode 资源目录或者安卓 drawable 文件夹加载)加载和从 javascript 包中加载,发现了一个很明显的性能问题。

我尝试在同一个 view 中加载350个 icon(很奇葩的做法), 我发现如果资源从 javascript 包里加载需要接近15秒的时间,而当我把资源移到 app 包后,渲染时间只有10毫秒。

这是因为如果资源从 javascript 包中加载, React Native 需要先从包中拿到资源,然后通过 一种 bridge (桥)把资源传送到 UI 层去渲染。而如果资源已经存在在 app 包里,那么 React 可以直接告知 UI 层去渲染具体的图片,无需通过这个”桥“引入或者转入图片资源。

IOS app 包资源引入

你可以使用 Xcode 项目中已经存在的目录 (Images.xcassets) 或者创建一个新的目录。创建新目录的方法:在你的 Xcode 项目里,进入 File -> New -> File -> Asset Catalog (source模块那里),命一个名。一旦创建了新目录,请确保它在 Build Phases -> Copy Bundle Resources 中。

要添加静态资源图片,只需要简单的点击你的资源目录然后把资源文件拖到这里来。或者文件目录已经在 Xcode 打开了,那你可以使用左下角的 + 按钮添加文件。

在你的 javaScript 代码中,只需要设置 Image 组件的 srouce 属性的 uri 字段为你的图片名(不需要文件扩展名),另外注意一定要给图片加上宽高否则图片不会渲染。

<Image
  style={{height: 20, width: 20}}
  source={{uri: 'my_cool_icon'}} // 不要引入文件扩展名
/>

安卓 app 包资源引入

把你的图片资源放到你的安卓项目目录下的 drawable 文件夹(android/app/src/main/res/drawable)。保持文件夹单一级别,任何放在子目录里的文件都会被忽略。确保每个文件名都以字母开头,否则编译项目时会出错。

跟 iOS 差不多, 在 javascript 里设置 uri 字段为文件名(无需扩展名)。由于某些原因,安卓需要在 Image 组件中直接指定图片的宽高,不确定这是否是一个 bug,或者仅仅是文档规定。 你可以看看 react native 的文档,Image 组件里并没有提及这个属性。

<Image
  height={20}
  width={20}
  style={{height: 20, width: 20}}
  source={{uri: 'my_cool_icon'}} // Don't include file extension
/>

加载远程资源

除非你的 app 需要从远端加载较多的图片,否则你可能不会想到要做图片缓存。我们知道这并不是一件必需要做的事,但道德上来讲我们应该做。毕竟我们没有理由去浪费用户的流量,本来可以缓存就没有必要重新进行远程加载。另外从缓存加载图片可以让加载更快更流畅。

React Native Fast Image 可以用来缓存图片,是一个很不错的包,而且用起来也很简单。你只需要引入这个包,然后把 Image 组件改成 FastImage。Fast Image 就为您处理好所有客户端缓存

<FastImage
  style={{height: 250, width: 250}}
  source={{uri: 'http://via.placeholder.com/200x200'}}
/>

资源文件的类型和大小

在 React Native 中使用资源,我们应该采取和 web 一样的方式,应该尽量使用较小的图片。当我们从javascript包中加载资源时,这些文件会被跨 brige 进行发送。所以这些文件越大,它们传输和渲染的时间就越长。为了达到最小的文件大小,需要做一些事情。如果可能,尽可能的使用 jpg 格式,因为 jpg 可以压缩。当然有时候需要透明图片,所以只能使用 png。

我把所有的图片都放在了 ImageOptim。这个应用程序会将图像尽可能的压缩。这不一定会获得最大的性能增益,但对于任何项目来说都是一个很好的方法。