react-native icon解決方案(svg)


** 在開發(fā)app的過程中總是少不了各種各樣的icon圖標(biāo)。移動端和pc端的解決方式各有不同,而RN與之前的開發(fā)方式都有所不同,所以我們要對各種引入圖標(biāo)的方式進(jìn)行權(quán)衡。**

一: 三種方式

目前主流的解決圖標(biāo)的方式又三種,如下:
1.圖片
使用png圖片,應(yīng)該是移動端最普適的方案,對RN來說,使用圖片解決圖標(biāo)最簡單,也最復(fù)雜,簡單的是RN自己就能夠解析圖片,因此不用引入任何外部庫,復(fù)雜的就是為了ios和安卓的各種屏幕,我們可能要對每個圖片準(zhǔn)備各種尺寸。
2. IconFont
對于web開發(fā)來說,字體圖標(biāo)絕對是解決icon最熟悉的方案了。也由此,react-native的開源庫react-native-vector-icons開始流行起來。這種方案解決簡單,只用引進(jìn)這個庫和.ttf文件,就能像寫web一樣使用字體圖標(biāo)了。并且現(xiàn)在很多demo都是用字體圖標(biāo)來解決的。
3.svg
之所以要把svg和圖片分開,就是因為RN是默認(rèn)不支持svg的,我們需要引入react-native-svg這個庫才能渲染svg圖標(biāo)。svg對比圖片擁有體積小,而且因其可縮放特性,不用理會用戶屏幕的尺寸。

二:對比

類型 優(yōu)勢 劣勢
圖片(打包) 使用方便,直接用require和Image標(biāo)簽就可以使用 bundle體積增大,特別是熱更新對流量,影響太大。需要根據(jù)屏幕不同準(zhǔn)備多種尺寸。
圖片(URI) 同上,更換方便,遠(yuǎn)程管理 基本同上,緩存管理比較麻煩,需要另外的庫。
IconFont 隨app打包,文件小,使用便利,不用擔(dān)心屏幕屏幕尺寸 不能熱更新,需要引入額外的庫
svg(打包) 文件極小,可隨bundle熱更新,可縮放圖形,不用擔(dān)心屏幕尺寸問題 需要引入額外的庫
svg(URI) 基本同圖片,不用擔(dān)心屏幕尺寸 緩存

三:決定實施方案 (svg)

鑒于使用圖片為了防止模糊,要準(zhǔn)備多倍圖,首先就被pass掉了。而字體圖標(biāo)做為我常用的手段,特別是公司的字體是通過icomoo這種網(wǎng)站統(tǒng)一管理的,本來是很傾向于使用的,奈何.ttf文件必須隨項目打包到app里面,不能熱更新。至少在沒有放棄codepush的情況下,只能放棄了。接下來就只有使用svg了 。

svg的體積極小,幾十個圖標(biāo)文件加起來不到3k,隨bundle打包是最好的選擇,正好現(xiàn)在的字體圖標(biāo)管理網(wǎng)站也能生成svg文件,很方便和設(shè)計師合作。設(shè)計師只用將需要使用的svg圖標(biāo)上傳到icomoo上命名好,然后打包下載就能使用。

使用react-native-svg就能對svg的標(biāo)簽解析成圖片,而使用react-native-svg-uri則能把svg文件的xml解析成響應(yīng)的component。這樣就能把svg文件轉(zhuǎn)化成圖形。但是后來發(fā)現(xiàn)這在安卓中行不通,因為安卓的RN項目在release打包后(非debug模式),只能允許require pngxml格式的文件。不過這并不是什么大問題,本來對icomoo生成svg文件中,我們僅僅需要path標(biāo)簽,其余的都是浪費空間的,而且頻繁require靜態(tài)文件也會減慢速度。我們可以用腳本來將svg文件批量生成js使用的字符串,然后通過react-native-svg-uri來解析xml。這個庫作者也考慮到android的問題預(yù)留了接受字符串的api。


于是我們的使用方式變成了:svg文件->js的xml數(shù)據(jù)集合->Svg Component。
另外在react-native-svg-uri更新太慢,其npm包依賴了低版本的react-native-svg。如果你使用的5.0版本以上的svg,會由于原生和react-native-svg-uri所使用的react-native-svg版本不同而報錯。其實這個庫原理很簡單,而且只有兩百行代碼,很好維護(hù)。建議不通過npm直接在項目中使用,可以解決版本問題。

四:具體步驟

步驟1: 下載svg文件


icomoo倉庫

以我使用的icomoo為例,打包下載下來的SVG文件夾如下


QQ20170324-190940@2x.png

步驟2:腳本處理
每次進(jìn)app請求多個svg很浪費資源,并且安卓本身就不支持svg靜態(tài)文件的require,所以我們需要用簡單的腳本處理一下,把多個svg的字符合并到一個js對象中,代碼如下,運(yùn)行下面的腳本 node getSvg。這里我時用node寫的,當(dāng)然你也可以用自己習(xí)慣的腳本語言來處理。

//  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文件。其結(jié)構(gòu)是

// 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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,319評論 25 708
  • React Native優(yōu)秀博客,以及優(yōu)秀的Github庫列表(很多英文資料源自于[awesome-react-n...
    董董董董董董董董董大笨蛋閱讀 11,019評論 4 162
  • 持續(xù)更新中...... 一套企業(yè)級的 UI 設(shè)計語言和 React 實現(xiàn)。 https://mobile.ant....
    日不落000閱讀 6,074評論 0 35
  • 也想過孑然一身勇闖這江湖 也想過寧靜安然遠(yuǎn)離這長安 一聲呦呦 切切的呼喚 是那初醒的小孩子唱 還是門口那座石磨轉(zhuǎn) ...
    催舒閱讀 433評論 0 2
  • 今天不研究自己了,今天研究一下兒掃地僧。傳說中少林寺之功夫,最高的不是方丈或主持,而是掃地僧,這是為什么...
    w_dahai閱讀 264評論 0 0

友情鏈接更多精彩內(nèi)容