bg
開發(fā)React Native項(xiàng)目離不開import官方或第三方的組件,這些插件無非都是用吊炸天的(你懂的)npm管理的。為了為React Native開發(fā)社區(qū)這個(gè)大家庭貢獻(xiàn)一點(diǎn)力量,我們也可以自己開發(fā)一個(gè)組件并發(fā)布到npm。
組件發(fā)布流程:

下面以組件react-native-app-info為例進(jìn)行說明組件目標(biāo):
提供獲取app版本號(hào)、名稱等信息(兼容iOS和Android)
一、npm 創(chuàng)建組件庫
1.安裝react-native-create-library
Requirements: Node 6.0+
npm install -g react-native-create-library
2 創(chuàng)建模板項(xiàng)目
創(chuàng)建項(xiàng)目命令:
react-native-create-library [options] <name>
Options:
-h, --help output usage information
-V, --version output the version number
-p, --prefix <prefix> The prefix for the library (Default:RN)
--module-prefix <modulePrefix> The module prefix for the library (Default:react-native)
--package-identifier <packageIdentifier> (Android only!) The package name for the Android module (Default:com.reactlibrary)
--namespace <namespace> (Windows only!) The **namespace for the Windows module
(Default: The name as PascalCase)
--platforms <platforms> Platforms the library will be created for. (comma separated; default:ios,android,windows)
--github-account <github_account> The github account where the library is hosted (Default:github_account)
--author-name <name> The author's name (Default:Your Name)
--author-email <email> The author's email (Default:yourname@email.com)
--license <license> The license type of this library (Default:Apache-2.0)
--generate-example <shouldGenerate> Will generate a RN example project and link the new library to it (Default:false)
注意: android平臺(tái)必須使用 package-identifier指定包名
2.1創(chuàng)建名為app-info的項(xiàng)目,同時(shí)指定android中的package,同時(shí)創(chuàng)建example用例項(xiàng)目
$ react-native-create-library --package-identifier com.carrot.appInfo --platforms android,ios --generate-example true app-info
2.2重命名一下項(xiàng)目名
$ mv app-info react-native-app-info
2.3 安裝dependencies
終端react-native-app-info下執(zhí)行:
$ npm install
有人可能會(huì)說,樓主為什么不直接生成
react-native-cardview的項(xiàng)目,而要先生成cardview再重命名。其實(shí)這是一個(gè)小技巧,因?yàn)槔?code>react-native-create-library生產(chǎn)的項(xiàng)目,一些跟組件相關(guān)的名稱或者類會(huì)默認(rèn)加上react-native或者RN前綴。
例如,如果你的初始項(xiàng)目名是react-native-card-view,那么package.json中定義的組件名將是react-native-react-native-card-view,android模塊中定義的相關(guān)類會(huì)是RNReactNativeCardviewModule.java,這顯然比較丑啊。
3.tree 命令查看文件目錄結(jié)構(gòu)
3.1 Mac OS或者Linux系統(tǒng)不像Windows自帶tree工具,需要自己安裝 tree:brew install tree
tree命令行 效果
tree -a 顯示當(dāng)前目錄樹
tree -d 只顯示文件夾
tree -D 顯示文件的最后修改時(shí)間
tree -L n n表示顯示項(xiàng)目的層級,n=3即只顯示項(xiàng)目的三層結(jié)構(gòu)
tree -I pattern pattern表示想要過濾的目錄,例如 tree -I “node_modules”可以過濾掉node_modules這個(gè)文件夾
進(jìn)入到app-info項(xiàng)目根目錄下,在命令行輸入:
tree -a
目錄結(jié)構(gòu)如下:
.
├── .gitattributes
├── .gitignore
├── README.md
├── android
│ ├── build.gradle
│ └── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── carrot
│ └── appInfo
│ ├── RNAppInfoModule.java
│ └── RNAppInfoPackage.java
├── index.js
├── ios
│ ├── RNAppInfo.h
│ ├── RNAppInfo.m
│ ├── RNAppInfo.podspec
│ ├── RNAppInfo.xcodeproj
│ │ └── project.pbxproj
│ └── RNAppInfo.xcworkspace
│ └── contents.xcworkspacedata
└── package.json
二、功能實(shí)現(xiàn)
注意:
如果你的插件不需要調(diào)用原生api(iOS、Android原生功能)進(jìn)行開發(fā),直接刪除ios和android文件夾即可。
如果需要原生配合,也就是需要自定義module的話,就要分別對iOS和Android進(jìn)行開發(fā)了,你可以戳這里React Native自定義原生(iOS和Android)模塊Module
1.iOS端實(shí)現(xiàn)
iOS端的實(shí)現(xiàn)還是很簡單的,只需要RNAppInfo.m實(shí)現(xiàn)并導(dǎo)出我們的方法就行了。
我們先看一下默認(rèn)的RNAppInfo.h和RNAppInfo.m文件
#import "RCTBridgeModule.h"
#else
#import <React/RCTBridgeModule.h>
#endif
@interface RNAppInfo : NSObject <RCTBridgeModule>
@end
RNAppInfo.h實(shí)現(xiàn)了RCTBridgeModule協(xié)議,這就是我們自定義原生module和RN的交互核心所在,這里不詳細(xì)介紹了,可以戳這里Native Modules
,這樣RNAppInfo.m里面導(dǎo)出的方法就可以被RN的js代碼調(diào)用到了。
這個(gè)類主要作用:
1.定義原生模塊名,可以直接在javascript中通過NativeModules.xxx來訪問,其中xxx是在RNxxxModule類中定義的RCT_EXPORT_MODULE方法返回值
2.實(shí)現(xiàn)并導(dǎo)出我們需要的方法,RCT_EXPORT_METHOD(getAppVersion:(RCTResponseSenderBlock)callback)
3.js就可以通過NativeModules.NativeModules.RNAppInfo. xxx調(diào)用了,xxx是我們導(dǎo)出的方法。
#import "RNAppInfo.h"
@implementation RNAppInfo
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
RCT_EXPORT_MODULE()
@end
其中,導(dǎo)出模塊,不添加參數(shù)即默認(rèn)為這個(gè)類名
RCT_EXPORT_MODULE();
1.1導(dǎo)出我們的方法
** 導(dǎo)出方法,橋接到j(luò)s的方法返回值類型必須是void**
/**
獲取app版本號(hào)
@param callback:結(jié)果回調(diào)
*/
RCT_EXPORT_METHOD(getAppVersion:(RCTResponseSenderBlock)callback){
BOOL isSucc = YES;
NSDictionary *info = [self getMainBundle];
NSString* appVersion = info[@"CFBundleShortVersionString"];
//準(zhǔn)備回調(diào)回去的數(shù)據(jù)
callback(@[appVersion]);
}
這樣我們就實(shí)現(xiàn)了iOS端的開發(fā)了,沒錯(cuò)就是這么簡單!!!
2.Android端實(shí)現(xiàn)
實(shí)現(xiàn)一個(gè)android自定義module,你需要關(guān)系兩個(gè)類:RNAppInfoModule.java 、 RNAppInfoPackage.java 、
2.1.1 RNAppInfoModule.java
RNAppInfoModule 集成自 ReactContextBaseJavaModule,原理跟iOS相同。
package com.carrot.appInfo;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
public class RNAppInfoModule extends ReactContextBaseJavaModule {
private final ReactApplicationContext reactContext;
public RNAppInfoModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
}
@Override
public String getName() {
return "RNAppInfo";
}
@ReactMethod
/**
* 獲取appVersionCode
* @param callback 回調(diào)
*/
public static int getVersionCode(Context mContext) {
if (mContext != null) {
try {
return mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionCode;
} catch (PackageManager.NameNotFoundException ignored) {
}
}
return 0;
}
/**
* 獲取app版本號(hào)
* @param callback 回調(diào)
*/
public static String getVersionName(Context mContext) {
if (mContext != null) {
try {
return mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionName;
} catch (PackageManager.NameNotFoundException ignored) {
}
}
return "";
}
}
這個(gè)類主要作用:
1.定義原生模塊名,可以直接在javascript中通過NativeModules.xxx來訪問,其中xxx是在RNxxxModule類中定義的getName方法返回值
2.實(shí)現(xiàn)并導(dǎo)出我們需要的方法:
@ReactMethod
public static String getVersionName(Context mContext) {}
3.js就可以通過NativeModules.NativeModules.RNAppInfo. xxx調(diào)用了,xxx是我們導(dǎo)出的方法,注意:是RNAppInfo不是RNAppInfoModule
三、組件發(fā)布
3.1代碼上傳到github
組件發(fā)布到npm的時(shí)候需要用到代碼的github地址。
方式一、可以先在github上手動(dòng)創(chuàng)建倉庫自己的項(xiàng)目倉庫,比如這里是react-native-app-info,
1.git clone "github對應(yīng)的項(xiàng)目git地址"
2、 cd react-native-app-info
3.git add .
4.git commit -a -m 'init repository'
5. git push -u origin master
方式二、先在github上手動(dòng)創(chuàng)建倉庫react-native-app-info,本地進(jìn)行關(guān)聯(lián)。
在本地執(zhí)行以下命令把代碼同步到你github對應(yīng)的repository中:
1、 cd react-native-app-info
2、 git init //初始化本地倉庫
3、 git add . //添加要push到遠(yuǎn)程倉庫的文件或文件夾
4、 git commit -m 'init repository'
5、 git remote add origin "github對應(yīng)的項(xiàng)目git地址" //建立鏈接遠(yuǎn)程倉庫
6、 git push -u origin master #將本地倉庫push到遠(yuǎn)程倉庫
注意:git push 之前還需要編輯.gitignore文件。
在.gitignore中定義哪些文件不進(jìn)行g(shù)it管理,也就是不會(huì)上傳到遠(yuǎn)程庫。
3.2 組件發(fā)布到npm registry
開發(fā)好組件之后,想在其他的項(xiàng)目(或者提供給其他人安裝使用)中通過npm install的方式安裝你的組件,那么你的組件必須發(fā)布到npm registry中。
3.2.1 npm registry 設(shè)置
npm registry 是什么
簡單來說,npm registry就相當(dāng)于一個(gè)包注冊管理中心。它管理著全世界的開發(fā)者們發(fā)布上來的各種插件,同時(shí)開發(fā)者們可以通過npm install的方式安裝所需要的插件。
npm官方registry為:http://registry.npmjs.org/
國內(nèi)速度較快的為:https://registry.npm.taobao.org/
查看registry
可以查看當(dāng)前使用的registry:
$ npm config get registry
切換registry
當(dāng)然也可以通過命令切換當(dāng)前使用的npm registry
全局切換
$ npm config set registry http://registry.npmjs.org/
臨時(shí)指定
有時(shí)候你可能只想在執(zhí)行某些npm命令時(shí)臨時(shí)切換,這個(gè)時(shí)候,可以使用--registry來指定臨時(shí)切換的registry,比如在npm發(fā)布
$ npm publish --registry http://registry.npmjs.org/
注意:國內(nèi)目前發(fā)布組件時(shí),必須切換為npmjs,否則$ npm publish也不會(huì)成功
3.2.2 創(chuàng)建/登陸npm registry賬戶
要發(fā)布組件到npm registry,你必須要是npm registry的注冊用戶,通過:
$ npm adduser
來新增一個(gè)用戶,或者你已經(jīng)在官網(wǎng)注冊了一個(gè)用戶,可以通過:
$ npm login
來登陸npm registry賬戶。
利用以下兩種方式來確認(rèn)你是否創(chuàng)建/登陸成功npm registry
- 命令
$ npm whoami確認(rèn)本地是否成功登陸認(rèn)證成功 - 在線打開 https://npmjs.com/~username 查看是否創(chuàng)建賬戶成功
注意:初次注冊、登錄npm賬號(hào),需要填寫郵箱,并進(jìn)行激活
3.2.3 發(fā)布前準(zhǔn)備(編寫git、npm等配置文件)
編寫.gitignore 和 .npmignore文件
- 在.gitignore中定義哪些文件不上傳到github中
- 在.npmignore中定義哪些文件發(fā)布時(shí)不打包
- 如果有.gitignore但是沒有.npmignore文件,那么.gitignore可以充當(dāng).npmignore的作用
- 具體規(guī)則可以參照:npm-developers, .gitignore or .npmignore pattern rules
編寫package.json
package.json文件定義了發(fā)布的所有信息,包括:組件名、版本、作者、描述、依賴等等關(guān)鍵信息。具體可以參照 Working with package.json
下面是react-native-app-info的package.json文件內(nèi)容:
{
"name": "react-native-app-info",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"eslint-fix": "npx eslint --fix .",
"lint": "eslint ."
},
"keywords": [
"react-native",
"react-component",
"react-native-component",
"react",
"mobile",
"ios",
"android",
"util",
"app-info",
"app"
],
"repository": {
"type": "git",
"url": "git@https://github.com/rocket-developer/react-native-app-info.git"
},
"author": {
"name": "wanglh",
"email": "zh-app-developer@aerozhonghuan.com"
},
"bugs": {
"url": "https://github.com/rocket-developer/react-native-app-info/issues"
},
"homepage": "https://github.com/rocket-developer/react-native-app-info",
"license": "MIT",
"peerDependencies": {
"prop-types": ">=15.6.2",
"react": ">=16.5.0",
"react-native": ">=0.57.1"
},
"devDependencies": {
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "24.1.0",
"babel-eslint": "^10.0.1",
"eslint": "^5.15.1",
"eslint-config-airbnb": "^17.1.0",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-react": "^7.12.4",
"metro-react-native-babel-preset": "0.46.0"
},
"jest": {
"preset": "react-native",
"testPathIgnorePatterns": [
"/node_modules/"
]
}
}
編寫readme.md
可以在readme.md文件中詳細(xì)說明組件的使用方法、注意事項(xiàng)等。一般使用Markdown語法來編寫
編寫CHANGELOG.md
記錄版本更新及修改記錄
以下配置文件不是必須,看自己需求:
編寫.eslintrc.js
配置eslint規(guī)則
編寫.eslintignore
eslint檢測忽略文件配置
編寫.babelrc
babe配置文件
3.2.3發(fā)布組件到npm
做好以上準(zhǔn)備之后,就可以發(fā)布了。這里需要注意,首次發(fā)布跟后面更新發(fā)布是不一樣的。
首次發(fā)布
第一次發(fā)布的話,直接執(zhí)行命令:
$ npm publish
就搞定了,可以在線查看確認(rèn)是否發(fā)布成功。訪問鏈接(<package>是你發(fā)布的npm package名):
https://www.npmjs.com/package/<package>
看看是否已經(jīng)有內(nèi)容了,有內(nèi)容說明發(fā)布成功了。
更新發(fā)布
如果不是首次發(fā)布,需要執(zhí)行兩個(gè)命令
$ npm version <update_type>
$ npm publish
$ npm version命令是用來自動(dòng)更新版本號(hào),update_type取值有patch minor major。那么在什么場景應(yīng)該選擇什么update_type呢?看下表
| update_type | 場景 | 版本號(hào)規(guī)則 | 舉例 |
|---|---|---|---|
| - | 首次發(fā)布 | 版本號(hào)1.0.0 | 1.0.0 |
| patch | 修復(fù)bug、微小改動(dòng)時(shí) | 從版本號(hào)第3位開始增量變動(dòng) | 1.0.0 -> 1.0.1 |
| minor | 上線新功能,并且對當(dāng)前版本已有功能模塊不影響時(shí) | 從版本號(hào)第2位開始增量變動(dòng) | 1.0.3 -> 1.1.3 |
| major | 上線多個(gè)新功能模塊,并且對當(dāng)前版本已有功能會(huì)有影響時(shí) | 從版本號(hào)第1位開始增量變動(dòng) | 1.0.3 -> 2.0.0 |
注意
如果首次發(fā)布版本號(hào)不是1.0.0的話,那么用$ npm version <update_type>
來更新會(huì)報(bào)錯(cuò),因?yàn)槟銢]有按照它約定的版本規(guī)則來,這個(gè)時(shí)候,你可以手動(dòng)修改package.json中的version字段為符合約定規(guī)則的版本號(hào),然后直接執(zhí)行$ npm publish就可以,然后下次再增量更新的時(shí)候,就可以直接使用$ npm version <update_type>的方式來自動(dòng)更新版本號(hào)了
到此,組件的發(fā)布流程就走完了~~
本文參考+推薦:
https://github.com/frostney/react-native-create-library
http://m.itdecent.cn/p/091a68ea1ca7