npm 發(fā)布一個(gè)react-native組件react-native-app-info

bg

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

組件發(fā)布流程:

image.png

下面以組件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

  1. 命令$ npm whoami確認(rèn)本地是否成功登陸認(rèn)證成功
  2. 在線打開 https://npmjs.com/~username 查看是否創(chuàng)建賬戶成功

注意:初次注冊、登錄npm賬號(hào),需要填寫郵箱,并進(jìn)行激活

3.2.3 發(fā)布前準(zhǔn)備(編寫git、npm等配置文件)

編寫.gitignore 和 .npmignore文件
  1. .gitignore中定義哪些文件不上傳到github中
  2. .npmignore中定義哪些文件發(fā)布時(shí)不打包
  3. 如果有.gitignore但是沒有.npmignore文件,那么.gitignore可以充當(dāng).npmignore的作用
  4. 具體規(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

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

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