资讯专栏INFORMATION COLUMN

React Native图片资源使用的优美方案

fireflow / 2470人阅读

摘要:图片资源作为与用户交互的界面元素,在客户端产品中起到了非常重要的角色作用。在应用开发中,移动端与的图片使用策略也有所不同。端较大的内存容量,快速的渲染能力使各类型图片资源都能得到较好的使用。

图片资源( jpeg、png、svg、webp ... )作为与用户交互的界面元素,在客户端产品中起到了非常重要的角色作用。在应用开发中,移动端与PC的图片使用策略也有所不同。PC端较大的内存容量,快速的渲染能力使各类型图片资源都能得到较好的使用。而移动端由于设备内存,GPU渲染都与PC有较大差别,所以在App应用开发中,需要我们多带带分析处理。今天我们聊聊在React Native开发中,如何优美的使用图片资源。 在 React Native 开发中,目前主流的图标解决方式大概有四种: 基本图片格式(png、jpeg)

基本图片格式是开发者最常用的一种图片资源,RN官方提供了在移动端适配的解决方案,直接使用 Image 标签就可以加载网络、本地图片。  

【缺点】需要引入多倍图(@2x / @3x)进行适配,使得 jsBundle 体积增大,内存占用的消耗比较明显。热更新时对流量(虽然你不在乎),影响较大。资产变更必须伴随二进制版本(apk | ipa)

Url

将图片保存在服务端,客户端以URL的方式进行加载。客户端不需要任何处理,例如在RN中加载网络图,只需要将URL传递给Image 组件的 src props

【缺点】缓存较为麻烦,可以依赖 react-native-fast-image 库。

IconFont (react-native-vector-icons)

熟悉Web开发的同学,对于 字体图标 绝对是不陌生的。基于此,开源库 react-native-vector-icons 实现了在RN平台的字体图标解决方案。这种方案简单,引进库和 .ttf 文件,就能使用字体图标了。 

【缺点】需要随app打包,文件小,使用便利,不用担心屏幕屏幕尺寸不能热更新,需要引入额外的库。

Svg

Svg 拥有体积小(Path),可缩放特性,因此不需要适配屏幕的分辨率尺寸,并且有效降低移动端内存占用问题。同时完美支持bundle热更新

【缺点】RN平台默认并不支持 Svg,幸运的是 react-native-svg 库实现了在RN移动应用中渲染 Svg 图标的能力。

方案分析

多倍图适配,.ttf 文件不能热更新的问题。现有的字体图标管理网站(iconfont、icomoo)也能生成svg文件。使得我们最终直接使用Svg。 react-native-svg 能对svg的标签解析成图片,如何将图片渲染成组件呢?第三方库 react-native-svg-uri 则能把svg文件的xml解析成 Component。迫不及待的集成,引入,运行。iOS完美的显示出来,而将 Android 进行 Release 打包过程中,Logcat抛出如下错误:


原因是在Android平台不支持直接加载 .svg 后缀的文件格式,只能允许加载 png xml 格式的文件。我们打开.svg文件,可以看到如下内容:

<");xml version="1.0" standalone="no"");
<svg t="1554188197278" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4579" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128">
<defs>
    <style type="text/css">style>
defs>
<path d="M790.24975 1.022977H228.123876c-42.453546 0-77.234765 35.292707-77.234765 78.769231v870.553446c39.896104 0 72.11988 33.246753 72.11988 73.654346h35.292707c0-19.948052 15.856144-36.315684 35.804196-36.315684s35.804196 16.367632 35.804196 36.315684h35.804196c0-19.948052 15.856144-36.315684 35.804196-36.315684s35.804196 16.367632 35.804195 36.315684h35.804196c0-19.948052 15.856144-36.315684 35.804196-36.315684s35.804196 16.367632 35.804196 36.315684h35.804196c0-19.948052 15.856144-36.315684 35.804195-36.315684s35.804196 16.367632 35.804196 36.315684h35.292707c0-19.948052 15.856144-36.315684 35.804196-36.315684s35.804196 16.367632 35.804196 36.315684h35.804196c0-40.407592 32.223776-73.142857 71.608391-73.142857V79.280719C867.484515 35.804196 832.703297 1.022977 790.24975 1.022977z m-118.153846 339.628372l-86.953047 88.999001h45.522478c16.879121 0 30.177822 13.298701 30.177822 30.68931s-13.298701 30.689311-30.177822 30.689311h-91.044955v62.401598h91.044955c16.879121 0 30.177822 13.298701 30.177822 30.689311s-13.298701 30.689311-30.177822 30.689311h-91.044955v93.090909c0 17.390609-13.298701 30.689311-30.177823 30.689311s-30.177822-13.298701-30.177822-30.689311v-93.090909H388.21978c-10.22977 0-20.971029-6.649351-26.085914-15.856144s-5.114885-21.482517 0-30.689311c5.114885-10.741259 15.856144-15.856144 26.085914-15.856144h91.044955V489.494505H388.21978c-16.879121 0-30.177822-13.298701-30.177822-30.68931s13.298701-30.689311 30.177822-30.689311h48.07992L345.254745 341.674326c-7.672328-8.183816-10.22977-19.948052-7.672327-30.689311s11.764236-19.948052 21.994006-22.505495c10.22977-4.091908 21.994006 0 30.177822 8.183817l119.688311 122.245754L629.130869 296.663337c7.672328-9.206793 19.436563-11.764236 30.177822-9.206794 10.22977 2.557443 19.436563 11.764236 21.994006 22.505495 2.557443 10.741259-1.022977 22.505495-9.206793 30.689311z" fill="#FF6F5A" p-id="4580">
path>
svg>

可以看到,其实 Svg 文件就是以path路径来绘制图标的。所以我们可以放弃 require svg 文件的方式,直接解析 svg 文件中的path 即可。同时也解决了减少 svg 占用空间,频繁 require 静态文件减慢速度的问题。 我们可以用脚本来将 svg文件 批量生成 Path 字符串,然后通过 react-native-svg-uri 来解析 Path xml。同时该库作者也考虑到 Android 的问题,为开发者提供了通过 svg path 字符串渲染图标的Api: svgXmlData 。 

【注意】react-native-svg-uri 更新太慢,建议不通过 npm 或 yarn 安装,直接复制文件在项目中使用,避免版本问题。

核心实现

通过上面的解析,实现流程分为如下三步:

(1)下载Svg文件

(2)解析所有Svg文件,将svg的path统一存放在 js 文件中

(3)封装 svg 组件,通过 svgXmlData 加载svg path

可以看出第二步的实现起到来承前启后的重要性,我们来看下核心代码。

解析

/**
 * 读取svg文件
 * @param {*} svgFileName svg文件, 例如 home-icon.svg
 * @returns { "home-icon": "... ... ..." }
 */
function readSvgFile(svgFileName) {
    return new Promise((resolve, inject) => {
        readFile(path.join(svgFileDir, svgFileName), "utf8", (error, svgFile) => {
            // eslint-disable-next-line no-useless-escape
            const svgPath = svgFile.replace(/<");, "");
            if (error) {
                inject(error);
            }
            resolve({
                [svgFileName.slice(0, svgFileName.lastIndexOf("."))]: svgPath,
            });
        });
    });
}
 
/**
 * 读取svg文件夹目录所有svg文件
 * @returns { "home-icon": "...", "xxx": "..." ... }
 */
function readSvgDir() {
    return new Promise((resolve, inject) => {
        readdir(svgFileDir, (error, svgFiles) => {
            if (error) {
                inject(error);
            }
            // svgFiles: string[]
            Promise.all(svgFiles.map((svgFileName) => readSvgFile(svgFileName)))
                .then((data) => resolve(data))
                .catch((err) => inject(err));
        });
    });
}
 
/**
 * 生成 .js 文件
 */
readSvgDir().then((data) => {
    const svgFile = `export default {
        ${
    data.map((item, index) => `${Object.keys(item)[0]}: "${Object.values(item)[0]}"
`)
}
    }`;
    writeFile(path.resolve(__dirname, `./${GENERATE_SVG_FILE_NAME}`), svgFile, (err) => {
        if (err) {
            throw new Error(err);
        }
    });
}).catch((error) => {
    throw new Error(error);
});

从上述代码可以看到,我们通过读取遍历svg文件夹下的所有svg文件,拿到... ... ...的字符串,并将其生成js文件统一管理。最终生成的文件内容如下

export default {    iconImage: ",};

封装 SvgIcon 组件

/**
 * svg 图片组件
 * @export
 * @class SvgIcon
 * @extends {PureComponent}
 */
import React, { PureComponent } from "react";
import { View } from "react-native";
import PropTypes from "prop-types";
import SvgUri from "./SvgUri";
import svgXmlData from "./svgXmlData";
 
export default class SvgIcon extends PureComponent {
    static propTypes = {
        style: PropTypes.object,
        /* eslint-disable react/require-default-props */
        color: PropTypes.object,
        size: PropTypes.shape(
            {
                width: PropTypes.number.isRequired,
                height: PropTypes.number.isRequired,
            }
        ).isRequired,
        icon: PropTypes.string.isRequired,
    }
 
    static defaultProps = {
        style: {},
    }
 
    render() {
        const {
            size,
            color,
            style,
            icon,
        } = this.props;
        const svgXmlPath = svgXmlData[icon];
        // eslint-disable-next-line no-nested-ternary
        return svgXmlData
            ");

使用

marginRight: 5 }}    size={{ width: 26, height: 26 }}/>

通过以上实现,我们就优美的解决了图片资源加载渲染问题。详细代码已上传到GitHub:react-native-svg-icon

WebP

默认情况下 React Native 是不支持 GIF 和 WebP 格式的。Android 需要在android/app/build.gradle文件中根据需要手动添加以下模块:

dependencies {
  // 如果你需要支持Android4.0(API level 14)之前的版本
  compile "com.facebook.fresco:animated-base-support:1.10.0"
 
  // 如果你需要支持GIF动图
  compile "com.facebook.fresco:animated-gif:1.10.0"
 
  // 如果你需要支持WebP格式,包括WebP动图
  compile "com.facebook.fresco:animated-webp:1.10.0"
  compile "com.facebook.fresco:webpsupport:1.10.0"
 
  // 如果只需要支持WebP格式而不需要动图
  compile "com.facebook.fresco:webpsupport:1.10.0"
}

iOS需要依赖第三方平台来实现Webp的加载,如:react-native-webp-supprot 。推荐大家看下这篇文章: React Native + WebP: Reducing bundle + binary sizes, increase speed with .webp image format

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/7009.html

相关文章

  • react-native 初体验 - 使用 javascript 来写 iOS app

    摘要:去年年初写了一个扩展十阅后,一直想写个十阅出来,奈何懒癌后期,一直拖到最近才完成原型。这次心血来潮闲的蛋疼,想起去年年初就有所耳闻的,于是就打算用它来耍耍。使用链接库在设备上运行用来断点调试地址学习资源图图图 去年年初写了一个 chrome 扩展「十阅」后,一直想写个十阅 app 出来,奈何懒癌后期,一直拖到最近才完成原型。 其实很早之前就已经写过一个 hybrid app 了,使用了...

    余学文 评论0 收藏0
  • React Native 基础练习指北(一)

    摘要:围观本文需自备,,以及。使用打开,并点击,会看到模拟器以及此项目对应的内容,效果如下如果想要修改显示内容,请打开,里面是一堆模样的东西。接下来,我们按照教程,来展示一张电影海报,为了方便,我们直接修改。 原文链接:http://www.tinghaige.com/ 本着什么都要搀和的原则,一起来看看React Native是如何开发iOS APP。 围观本文需自备Mac OSX ,...

    nidaye 评论0 收藏0
  • 前端每周清单半年盘点之 ReactReactNative

    摘要:前端每周清单半年盘点之与篇前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点分为新闻热点开发教程工程实践深度阅读开源项目巅峰人生等栏目。与求同存异近日,宣布将的构建工具由迁移到,引发了很多开发者的讨论。 前端每周清单半年盘点之 React 与 ReactNative 篇 前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点;分为...

    Barry_Ng 评论0 收藏0
  • 优秀文章收藏(慢慢消化)持续更新~

    摘要:整理收藏一些优秀的文章及大佬博客留着慢慢学习原文协作规范中文技术文档协作规范阮一峰编程风格凹凸实验室前端代码规范风格指南这一次,彻底弄懂执行机制一次弄懂彻底解决此类面试问题浏览器与的事件循环有何区别笔试题事件循环机制异步编程理解的异步 better-learning 整理收藏一些优秀的文章及大佬博客留着慢慢学习 原文:https://www.ahwgs.cn/youxiuwenzhan...

    JeOam 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<