react-native icon解决方案(svg)


** 在开发app的过程中总是少不了各种各样的icon图标。移动端和pc端的解决方式各有不同,而RN与之前的开发方式都有所不同,所以我们要对各种引入图标的方式进行权衡。**

一: 三种方式

目前主流的解决图标的方式又三种,如下:
1.图片
使用png图片,应该是移动端最普适的方案,对RN来说,使用图片解决图标最简单,也最复杂,简单的是RN自己就能够解析图片,因此不用引入任何外部库,复杂的就是为了ios和安卓的各种屏幕,我们可能要对每个图片准备各种尺寸。
2. IconFont
对于web开发来说,字体图标绝对是解决icon最熟悉的方案了。也由此,react-native的开源库react-native-vector-icons开始流行起来。这种方案解决简单,只用引进这个库和.ttf文件,就能像写web一样使用字体图标了。并且现在很多demo都是用字体图标来解决的。
3.svg
之所以要把svg和图片分开,就是因为RN是默认不支持svg的,我们需要引入react-native-svg这个库才能渲染svg图标。svg对比图片拥有体积小,而且因其可缩放特性,不用理会用户屏幕的尺寸。

二:对比

类型 优势 劣势
图片(打包) 使用方便,直接用require和Image标签就可以使用 bundle体积增大,特别是热更新对流量,影响太大。需要根据屏幕不同准备多种尺寸。
图片(URI) 同上,更换方便,远程管理 基本同上,缓存管理比较麻烦,需要另外的库。
IconFont 随app打包,文件小,使用便利,不用担心屏幕屏幕尺寸 不能热更新,需要引入额外的库
svg(打包) 文件极小,可随bundle热更新,可缩放图形,不用担心屏幕尺寸问题 需要引入额外的库
svg(URI) 基本同图片,不用担心屏幕尺寸 缓存

三:决定实施方案 (svg)

鉴于使用图片为了防止模糊,要准备多倍图,首先就被pass掉了。而字体图标做为我常用的手段,特别是公司的字体是通过icomoo这种网站统一管理的,本来是很倾向于使用的,奈何.ttf文件必须随项目打包到app里面,不能热更新。至少在没有放弃codepush的情况下,只能放弃了。接下来就只有使用svg了 。

svg的体积极小,几十个图标文件加起来不到3k,随bundle打包是最好的选择,正好现在的字体图标管理网站也能生成svg文件,很方便和设计师合作。设计师只用将需要使用的svg图标上传到icomoo上命名好,然后打包下载就能使用。

使用react-native-svg就能对svg的标签解析成图片,而使用react-native-svg-uri则能把svg文件的xml解析成响应的component。这样就能把svg文件转化成图形。但是后来发现这在安卓中行不通,因为安卓的RN项目在release打包后(非debug模式),只能允许require pngxml格式的文件。不过这并不是什么大问题,本来对icomoo生成svg文件中,我们仅仅需要path标签,其余的都是浪费空间的,而且频繁require静态文件也会减慢速度。我们可以用脚本来将svg文件批量生成js使用的字符串,然后通过react-native-svg-uri来解析xml。这个库作者也考虑到android的问题预留了接受字符串的api。


于是我们的使用方式变成了:svg文件->js的xml数据集合->Svg Component。
另外在react-native-svg-uri更新太慢,其npm包依赖了低版本的react-native-svg。如果你使用的5.0版本以上的svg,会由于原生和react-native-svg-uri所使用的react-native-svg版本不同而报错。其实这个库原理很简单,而且只有两百行代码,很好维护。建议不通过npm直接在项目中使用,可以解决版本问题。

四:具体步骤

步骤1: 下载svg文件


icomoo仓库

以我使用的icomoo为例,打包下载下来的SVG文件夹如下


QQ20170324-190940@2x.png

步骤2:脚本处理
每次进app请求多个svg很浪费资源,并且安卓本身就不支持svg静态文件的require,所以我们需要用简单的脚本处理一下,把多个svg的字符合并到一个js对象中,代码如下,运行下面的脚本 node getSvg。这里我时用node写的,当然你也可以用自己习惯的脚本语言来处理。

//  getSvg.js
var fs = require('fs');
var path = require('path');
const svgDir = path.resolve(__dirname, './svgs');

// 读取单个文件
function readfile(filename) {
  return new Promise((resolve, reject) => {
    fs.readFile(path.join(svgDir, filename), 'utf8', function(err, data) {
      console.log(data.replace(/<\?xml.*?\?>|<\!--.*?-->|<!DOCTYPE.*?>/g, ''));
      if (err) reject(err);
      resolve({
        [filename.slice(0, filename.lastIndexOf('.'))]: data,
      });
    });
  });
}

// 读取SVG文件夹下所有svg
function readSvgs() {
  return new Promise((resolve, reject) => {
   fs.readdir(svgDir, function(err, files) {
     if (err) reject(err);
     Promise.all(files.map(filename => readfile(filename)))
      .then(data => resolve(data))
      .catch(err => reject(err));
   });
  });
}

// 生成js文件
readSvgs().then(data => {
  let svgFile = 'export default ' + JSON.stringify(Object.assign.apply(this, data));
  fs.writeFile(path.resolve(__dirname, './svgs.js'), svgFile, function(err) {
    if(err) throw new Error(err);
  })
}).catch(err => {
    throw new Error(err);
  });

这样生成了一个svgs.js文件。其结构是

// svgs.js
export default {
  'svgName1': 'xmlData1...',
  'svgName2': 'xmlData2...',
  ...
}

步骤3:封装Svg Component

// Svg.js
import React, { Component } from 'react';
import {
  ViewStyle,
} from 'react-native'
import SvgUri from '../../lib/react-native-svg-uri';
import svgs from '../../assets/svgs';

export default class Svg extends Component<SvgProperties, void>{
  render() {
    const {
      iocn,
      color,
      size,
      style,
    } = this.props;
    let svgXmlData = svgs[this.props.icon];

    if (!svgXmlData) {
      let err_msg = `没有"${this.props.icon}"这个icon,请下载最新的icomoo并 npm run build-js`;
      throw new Error(err_msg);
    }
    return (
      <SvgUri
        width={size}
        height={size}
        svgXmlData={svgXmlData}
        fill={color}
        style={style}
      />
    )
  }
}

使用

render() {
  return <Svg icon="ac_unit" size="40" fill="#ccc"/>
}
svg使用

新加了一个demo https://github.com/cloudZQY/react-native-svg-demo
$ git clone git@github.com:cloudZQY/react-native-svg-demo.git
$ cd react-native-svg-demo
$ npm i
$ npm run build-svg
$ react-native run-android or $ react-native run-ios

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,398评论 25 707
  • React Native优秀博客,以及优秀的Github库列表(很多英文资料源自于[awesome-react-n...
    董董董董董董董董董大笨蛋阅读 10,586评论 4 162
  • 持续更新中...... 一套企业级的 UI 设计语言和 React 实现。 https://mobile.ant....
    日不落000阅读 5,655评论 0 35
  • 也想过孑然一身勇闯这江湖 也想过宁静安然远离这长安 一声呦呦 切切的呼唤 是那初醒的小孩子唱 还是门口那座石磨转 ...
    催舒阅读 350评论 0 2
  • 今天不研究自己了,今天研究一下儿扫地僧。传说中少林寺之功夫,最高的不是方丈或主持,而是扫地僧,这是为什么...
    w_dahai阅读 236评论 0 0