tags:

  • React
  • React Native

categories:

  • React Native
  • 组件库

背景

图标组件是组件库里不可或缺的一个组件,随着前端的不断发展,其实现方式也是在不断的迭代,从雪碧图到字体图标再到 svg 图标,并且再配合 Typescript 和 Tree Shaking 图标组件也能轻松的获得代码提示和按需加载。

在 web 中使用 svg 图标很简单,浏览器原生支持 svg 元素,但是 React Native 内置的组件中没有 SVG 组件,要使用 SVG,需要自己封装。幸好社区里早早有大佬封装好了 - react-native-svg,我们直接拿来使用就好了。

使用 react-native-svg 和我们在 React 中使用 svg 很像,都有 SvgPath组件,不过一个是原生的,一个需要从 react-native-svg 中引入:

React:

1
2
3
4
5
6
7
8
9
10
11
import * as React from "react";
import { SVGProps } from "react";

const SvgComponent = (props: SVGProps<SVGSVGElement>) => (
<svg width={48} height={1} xmlns="http://www.w3.org/2000/svg" {...props}>
<title>{"Rectangle 5"}</title>
<path d="M0 0h48v1H0z" fill="#063855" fillRule="evenodd" />
</svg>
);

export default SvgComponent;

React Native:

1
2
3
4
5
6
7
8
9
10
11
import * as React from "react";
import Svg, { SvgProps, Path } from "react-native-svg";
/* SVGR has dropped some elements not supported by react-native-svg: title */

const SvgComponent = (props: SvgProps) => (
<Svg width={48} height={1} xmlns="http://www.w3.org/2000/svg" {...props}>
<Path d="M0 0h48v1H0z" fill="#063855" fillRule="evenodd" />
</Svg>
);

export default SvgComponent;

在 React Native 使用 svg,基本就相当于把代码从 React 中 copy 过来,将所有的 svg 元素换成驼峰样式的自定义组件,然后从 react-native-svg 中引入。

这里推荐个工具 SVGR Playground,可以将 svg 转换成 React & React Native 组件,省去手动修改的麻烦。

图标管理

上面介绍了在 React Native 中使用 svg 图标的姿势,但是一个图标组件库中,少则几十多则几百个图标,如何管理这些图标就是一个问题了。

最简单粗暴的方式就是直接放在源码了,但是当你需要修改和预览图标时,你就会体验到什么到痛苦。

更好的方式是借助图标管理平台,比如 iconfonticonpark 等,这里推荐 iconfont,原因见下面:

图标组件生成

图标管理的问题解决了,接下来就要解决如何将 iconfont 上的图标转换成可使用的图标组件的问题了,这里需要借助个工具 - react-native-iconfont-cli,这里贴下 react-native-iconfont-cli 需要解决的痛点,体验下它的强大之处:

引用自:https://github.com/iconfont-cli/react-native-iconfont-cli

通常地,当我们想在 RN 里使用 iconfont,我们可能会借助 react-native-vector-icons 导入 ttf 字体文件,或者直接手动下载各个 svg 文件到本地,然后单个使用。

使用 ttf 字体有一个弊端,就是每次更新图标,都要相应的更新 ttf 文件,然后再次打包发布 APP。而且 ttf 不支持多种色彩的图标,导致所有图标都是单色。如果你是借助 react-native-vector-icons,该库内置了 10 多套 ttf 文件,合起来有 2M 左右;你可能用不到它们,但是它们仍然会被打包进你的 APP 里。

下载 svg 到本地也不方便,因为你需要额外维护一份图标字体,有可能造成线上和本地的图标不一致问题。而且如果你想动态地改变 svg 的渲染色彩,基本上是不可能的,只能渲染原图的颜色。

为了解决这些问题,我用纯 Javascript 实现 iconfont 到 React 组件的转换操作,不需要依赖 ttf 字体文件,不需要手动下载图标到本地。

如何使用就不过多介绍了,README 里写的很清楚,这里贴下我的组件库中使用的配置:

1
2
3
4
5
6
{
"symbol_url": "//at.alicdn.com/t/font_2952809_4bb6cudcj1k.js",
"use_typescript": true,
"save_dir": "./src",
"default_icon_size": 18
}

发版

图标的管理和生成都说完了,一般情况下到这里就该结束了,但是我的组件库中图标是单独抽出来的一个包,这里还需要说下组件的打包和发版问题。

打包的话在上一篇文章 - React Native 组件库 - 脚手架 里介绍了 React Native 的一个优秀的打包工具 react-native-builder-bob,这里我们图标组件依然使用它来打包,如何使用可以参考我上一篇文章或者直接看官方文档,这里贴下最后使用的完整配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
{
"name": "@rn-vant/icons",
"version": "0.2.0",
"description": "rn-vant icons for react-native",
"keywords": ["react-native", "rn-vant", "icon"],
"author": "bijinfeng <bijinfeng@bytedance.com>",
"license": "MIT",
"main": "lib/commonjs/index",
"module": "lib/module/index",
"types": "lib/typescript/index.d.ts",
"react-native": "src/index",
"source": "src/index",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": ["lib", "src"],
"publishConfig": {
"access": "public"
},
"scripts": {
"generate": "npx iconfont-rn",
"build": "bob build"
},
"devDependencies": {
"react-native-builder-bob": "^0.18.2",
"react-native-iconfont-cli": "^2.2.3",
"react-native-svg": "^12.4.4",
"typescript": "^4.4.3"
},
"peerDependencies": {
"react": "*",
"react-native": "*",
"react-native-svg": "*"
},
"react-native-builder-bob": {
"source": "src",
"output": "lib",
"targets": [
"commonjs",
"module",
[
"typescript",
{
"project": "tsconfig.build.json"
}
]
]
}
}

发布到 npm 就不过多介绍,老生常谈的问题了,一般 Mono Repo 项目里都有自己的发布命令,比如,lerna 项目就用 lerna publish,一般 Multi Repo 项目使用 npm publish就可以了。如果不想手动发布,可以借助 CI/CD 工具做到自动发布,这些不在本文讨论范围内,就不多赘述了。

总结

得益于前端强大的社区,在 React Native 中使用 SVG 图标并不是一件多么麻烦的事情,借助一些工具,甚至能做到开发体验丝滑到飞起。有时不得不感叹一句前端发展太快了,早已脱离了刀耕火种的时代,各种先进的工具不断涌现,目不暇接,现在最大的问题反而是工具太多,学不过来的问题!