最近,在維護一個 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 對象。