最近,在维护一个 RN
项目时,发现存在一些问题。当咱们开始使用 RN
以前,确定会作一些技术调研,好比技术难度、社区活跃度、小伙伴们的了解程度等,其中不可忽视的是有无现成成熟的 组件库
使用,这将使项目开发周期和效率有所提升。html
当咱们作好技术选型,选定好 组件库
后,随着项目的开发和不断迭代,可能会出现这么一些问题:前端
那么这个时候,能够自定义组件,将第三方组件库替换掉,提取本身的组件发布到 npm
上。今天,咱们就来聊聊如何 搭建RN组件库
。主要仍是学习如何开发一个组件库这么一个过程
。过程很重要node
当咱们决定开发一个 RN组件库 的时候,有几个问题须要提早考虑下:react
由于,咱们自定义的组件基本都是纯 js 的文件,不多有涉及到原生的功能(主要是不会0_0)。android
所以,咱们的目录组织结构,只须要有个 components
文件夹、一个入口文件index.js
(将组件统一导出)、一个package.json
文件,就能够了。固然,若是你有 icon
assets
或者一些其余的目录也能添加进来。ios
这里须要多说一点,咱们只用关心最终上传 npm 的目录,而不用关心与预览相关的目录结构。好比 有两种方式,一种是当作一个RN项目开发,经过 npx react-native init xxx
来初始化项目。一种是 expo,不用搭建 RN 环境。git
我用的是第一种,因此 个人目录大体以下:github
. ├── README.md ├── app.json ├── android/ ├── ios/ ├── gulpfile.js ├── index.js ├── package.json ├── dist │ ├── README.MD │ ├── index.js │ ├── package.json │ └── src │ ├── components │ ├── icons │ ├── themes │ └── utils └── src ├── App.js ├── component-path ├── components ├── icons ├── routes ├── themes ├── utils └── views
dist
目录 就是我最终用来发布 npm
的目录。dist/components
下的组件,是从 src/components
拷贝而来,其余相似目录,同理。至于其余文件和目录怎么来的,后面会讲到。npm
在开发组件的过程当中,须要实时预览组件的效果,那么组件在预览页面的引入方式多是这样的:json
// 预览页面可能位于 src/views/ButtonDemo.js import { Button } from '../components'
组件开发完,发布后,想要看线上组件的实际效果,引入可能会变成这样:
import { Button } from 'react-native-unit-zjp'
若是,你开发了有一些组件,有一些预览页面,那么,你在开发过程 和 发布后,为了预览组件,可能须要频繁修改组件路径。并且,还有一个问题,就是在开发完组件,预发布的时候,没法提早预览即将发到线上的组件效果。(我碰到过一个问题,就是本地开发,引入组件看效果的时候,没有任何问题;发布到线上,引入的时候,会报警告)
其实,咱们能够有一个统一修改组件引入路径的地方,解决这些问题。
在 src/component-path
下 添加 index.js
文件:
import * as components from '../components'; // 本地调试 // import \* as components from '../../dist'; // 发布前测试包 // import \* as components from '../../node\_modules/react-native-unit-zjp'; // 正式依赖的包。 module.exports = { Theme: components.Theme || require('../themes/Theme'), ...components };
有了这个开关文件后,预览页面的组件就能够这么引入了:
import { Button } from '../component-path'
在组件开发,预发布,发布后 这几个阶段,只须要切换这一个路径就够了。
上面文件中之因此 要加 '../../node\_modules'
,是由于当 dist
目录下 的 package.json
存在时,直接从 react-native-unit-zjp
引入组件,会指向 dist
目录。
从这点能够得出,若是想要以特定名称引入某文件时,不想写长长的路径的话,能够在该目录下,新建一个 pacakage.json
而后指定它的 name
就能够了
发布 npm 包,其实很简单,就一句命令行的事 npm publish
,固然,得从拥有一个 npm 帐号开始。这里就不细说了,能够看看官网,或者网上其余教程,很详细,一分钟教你发布npm包。这里只讨论,基于这个项目,怎么发布。
基于上面的思考,dist
成为了咱们的组件库发布目录了。这里之因此,单独弄个 dist
目录,只是为了将组件库自己所用到的依赖,与整个项目所用到的依赖区分开。若是,不想这么考虑,彻底能够不用 dist
目录,直接在 package.json
的 files
字段配置须要发布的文件 及 目录。
若是想要 dist
目录,应该有如下这样的流程:
git
,而后拷贝相关目录 到 dist
(可用 gulp
,下面会讲)dist
解决这三个问题后,咱们能够开始建立项目了。
上面提到过,咱们只关心用于发布的 dist
目录,所以,怎么建立项目,就看我的喜爱。若是有现成 RN 开发环境,能够直接初始化一个 RN 项目。若是不想费力搭建 RN 开发环境,能够试试 expo。这里,我就用第一种了。
npx react-native init xxx
在 src/components
目录下开发你的组件。怎么开发组件,这里就不讲了,就当是一个 RN 项目来开发,该建啥目录,缺啥引啥,router 啥的。
组件开发完后,在 src/components
最好有个 导出组件的文件 index.js
:
import Button from './Button/Button.js'; export { Button }
为了目录清晰,以及有个 单独的 package.json 管理 发布包的版本,所以,决定单独弄个 dist
文件夹。将须要发布的文件及目录拷贝至 dist
目录中。
在 dist
目录 添加 package.json
,执行 npm init
获得以下文件:
{ "name": "react-native-unit-zjp", "version": "0.0.4", "description": "## 一套拿来就用的 ReactNative 组件库", "main": "index.js", "scripts": { "test": "echo \\"Error: no test specified\\" && exit 1" }, "keywords": [ "react-native" ], "author": "zhangjinpei", "license": "ISC", "dependencies": { "react-native-linear-gradient": "^2.5.6", "react-native-root-siblings": "^4.0.6" }, "devDependencies": {}, "repository": { "type": "git", "url": "xxx" }, "bugs": { "url": "xxx" }, "homepage": "xxx", "files": [ "index.js", "README.md", "src" ] }
为了保持他们之间的引用路径,保持 dist
目录结构,跟 src
目录类似。
为了方便,这里经过 gulp
拷贝文件,配置以下:
const gulp = require('gulp'); const rimraf = require('rimraf'); const { src, dest, task, series} = gulp; task('clean', (cb) => { rimraf('dist/src', cb); }); task('components', () => { return src('src/components/\*\*/\*.\*') .pipe(dest('dist/src/components/')); }); task('icons', () => { return src('src/icons/\*\*/\*.\*') .pipe(dest('dist/src/icons/')); }); task('themes', () => { return src('src/themes/\*\*/\*.\*') .pipe(dest('dist/src/themes/')); }); task('utils', () => { return src('src/utils/\*\*/\*.\*') .pipe(dest('dist/src/utils/')); }); task('readme', () => { return src('./README.md') .pipe(dest('dist/')); }); exports.default = series('clean', 'components', 'icons', 'themes', 'utils', 'readme');
如今组件有了,还须要有个统一的地方导出组件。在 `dist` 目录添加入口文件,内容以下:
import Theme from './src/themes/Theme'; // 这个是主题配置 如不须要 能够去掉 import * as myUnit from './src/components'; module.exports = { Theme, ...myUnit };
这个时候,组件库的架子基本就差很少了,剩下的就是慢慢完善你的组件库了。
这是个人项目 react-native-unit-zjp,目前还在开发中,欢迎提出问题并star。
当咱们开发好组件,或者是开发中时,须要将包提交到 npm 上,须要有个版本号,记录更改。
版本号,通常使用三位数来描述,以点来分割,例如:1.0.0
经过执行,npm version xxx
来自动更新版本号。(须要将改动提交至 git ,而后再执行此命令。此命令,会自动打上 tag,并提交,须要手动 git push )
版本号更新好以后,就能够 npm publish
了。
注意: 首次 publish
只要有个版本号就能够,再次 publish
以前,必须更新版本号,也就是执行 npm version xxx
命令,否者会报错:
You cannot publish over the previously published versions: x.x.x
;
package.json
俗称 依赖配置文件(我本身取的名),最主要的做用就是,管理项目中所用到的依赖。它自己的做用是为 node.js 模块服务的,模块有不少属性,为了描述模块的特性,package.json
也被称做模块的 描述文件
。
name
和 version
是 package.json
中最重要的两个属性,并且是必填的。这两个属性一块儿就造成了 npm 模块惟一标识符。分别表示 模块的 名称 和 版本。名称通常不会变,版本会随着模块的修改而更新变更。
这两个字段都是用来在 npm
官网上搜索的, 区别是一个是字符串, 一个是字符串数组。
这两个属性,都是用来记录项目中所用到的依赖。区别是,一个是用来记录开发环境所用的依赖,一个是记录生产环境所用到的依赖。
好比,对于大多数前端项目来讲,gulp
等构件工具及插件,可能只在开发环境中使用,而在生产环境只关心最终生成的 dist
文件,因此,gul
p 等插件 就应该放在 devDependencies
下。
经过 npm i xxx -save
、npm i xxx -s
、yarn add xxx -S
安装的依赖会添加到 dependencies
下;
经过 npm i xxx -save-dev
、npm i xxx -d
、yarn add xxx -D
安装的依赖会添加到 devDependencies
下;
可让宿主环境拥有某个特定版本的依赖
前提:项目X
依赖 模块A
, 模块A
依赖 模块B
状况一:若是 模块B
定义在 模块A
的 dependencies
字段中
结果:模块B
只能被 模块A 引用
;
可能的好处:假如 项目X 也依赖 模块B,版本不同,则能够互不干扰。
可能的坏处:假如 项目X 也依赖 模块B,且版本同样,则会有两个如出一辙的 模块B
项目X └──node_modules/ └── 模块A └──node_modules/ └── 模块B
状况二:若是 模块B
定义在 模块A
的 peerDependencies
字段中
结果:项目X
也会下载 或 检查 模块b
的版本。
可能的好处:假如 项目X 也依赖 模块B,版本不同,则会在控制台以警告的形式提示版本问题。
可能的坏处:假如 项目X 也依赖 模块B,且版本同样,只用下载一遍。
项目X └──node_modules/ ├── 模块a └── 模块b
结论:定义在 peerDependencies
中的依赖,在宿主环境中,也会被下载 或 被检查版本。
files
是一个包含项目中的文件的数组。若是命名了一个文件夹,那也会包含文件夹中的文件。(除非被其余条件忽略了 .npmignore .gitignore)
在 npm publish
的时候,会依据这个配置 上传你的文件。
scripts
是一个由脚本命令组成的 kye value 对象。