npm 发布一个react-native组件react-native-app-info

bg

开发React Native项目离不开import官方或第三方的组件,这些插件无非都是用吊炸天的(你懂的)npm管理的。为了为React Native开发社区这个大家庭贡献一点力量,我们也可以自己开发一个组件并发布到npm。

组件发布流程:

image.png

下面以组件react-native-app-info为例进行说明组件目标:

提供获取app版本号、名称等信息(兼容iOS和Android)

一、npm 创建组件库

1.安装react-native-create-library

Requirements: Node 6.0+

npm install -g react-native-create-library

2 创建模板项目

创建项目命令:
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平台必须使用 package-identifier指定包名

2.1创建名为app-info的项目,同时指定android中的package,同时创建example用例项目

$ react-native-create-library --package-identifier com.carrot.appInfo --platforms android,ios  --generate-example true app-info

2.2重命名一下项目名

$ mv app-info react-native-app-info

2.3 安装dependencies

终端react-native-app-info下执行:

$ npm install

有人可能会说,楼主为什么不直接生成react-native-cardview的项目,而要先生成cardview再重命名。其实这是一个小技巧,因为利用react-native-create-library生产的项目,一些跟组件相关的名称或者类会默认加上react-native或者RN前缀。
例如,如果你的初始项目名是react-native-card-view,那么package.json中定义的组件名将是react-native-react-native-card-view,android模块中定义的相关类会是RNReactNativeCardviewModule.java,这显然比较丑啊。

3.tree 命令查看文件目录结构

3.1 Mac OS或者Linux系统不像Windows自带tree工具,需要自己安装 tree:brew install tree

tree命令行 效果
tree -a 显示当前目录树
tree -d 只显示文件夹
tree -D 显示文件的最后修改时间
tree -L n n表示显示项目的层级,n=3即只显示项目的三层结构
tree -I pattern pattern表示想要过滤的目录,例如 tree -I “node_modules”可以过滤掉node_modules这个文件夹

进入到app-info项目根目录下,在命令行输入:
tree -a

目录结构如下:

.
├── .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

二、功能实现

注意:
如果你的插件不需要调用原生api(iOS、Android原生功能)进行开发,直接删除ios和android文件夹即可。
如果需要原生配合,也就是需要自定义module的话,就要分别对iOS和Android进行开发了,你可以戳这里React Native自定义原生(iOS和Android)模块Module

1.iOS端实现

iOS端的实现还是很简单的,只需要RNAppInfo.m实现并导出我们的方法就行了。
我们先看一下默认的RNAppInfo.h和RNAppInfo.m文件

#import "RCTBridgeModule.h"
#else
#import <React/RCTBridgeModule.h>
#endif

@interface RNAppInfo : NSObject <RCTBridgeModule>

@end

RNAppInfo.h实现了RCTBridgeModule协议,这就是我们自定义原生module和RN的交互核心所在,这里不详细介绍了,可以戳这里Native Modules
,这样RNAppInfo.m里面导出的方法就可以被RN的js代码调用到了。

这个类主要作用:
1.定义原生模块名,可以直接在javascript中通过NativeModules.xxx来访问,其中xxx是在RNxxxModule类中定义的RCT_EXPORT_MODULE方法返回值
2.实现并导出我们需要的方法,RCT_EXPORT_METHOD(getAppVersion:(RCTResponseSenderBlock)callback)
3.js就可以通过NativeModules.NativeModules.RNAppInfo. xxx调用了,xxx是我们导出的方法。

#import "RNAppInfo.h"

@implementation RNAppInfo

- (dispatch_queue_t)methodQueue
{
    return dispatch_get_main_queue();
}
RCT_EXPORT_MODULE()

@end

其中,导出模块,不添加参数即默认为这个类名

RCT_EXPORT_MODULE();

1.1导出我们的方法

** 导出方法,桥接到js的方法返回值类型必须是void**

/**
 获取app版本号
 @param callback:结果回调
 */
RCT_EXPORT_METHOD(getAppVersion:(RCTResponseSenderBlock)callback){
  
  BOOL isSucc = YES;
  NSDictionary *info = [self getMainBundle];
  NSString* appVersion = info[@"CFBundleShortVersionString"];
  //准备回调回去的数据
  callback(@[appVersion]);
}

这样我们就实现了iOS端的开发了,没错就是这么简单!!!

2.Android端实现

实现一个android自定义module,你需要关系两个类: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 回调
   */
  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版本号
   * @param callback 回调
   */
  public static String getVersionName(Context mContext) {
    if (mContext != null) {
      try {
        return mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionName;
      } catch (PackageManager.NameNotFoundException ignored) {
      }
    }
    return "";
  }
}

这个类主要作用:
1.定义原生模块名,可以直接在javascript中通过NativeModules.xxx来访问,其中xxx是在RNxxxModule类中定义的getName方法返回值
2.实现并导出我们需要的方法:

@ReactMethod
 public static String getVersionName(Context mContext) {}

3.js就可以通过NativeModules.NativeModules.RNAppInfo. xxx调用了,xxx是我们导出的方法,注意:是RNAppInfo不是RNAppInfoModule

三、组件发布

3.1代码上传到github

组件发布到npm的时候需要用到代码的github地址。
方式一、可以先在github上手动创建仓库自己的项目仓库,比如这里是react-native-app-info,

1.git clone "github对应的项目git地址"
2、 cd react-native-app-info
3.git add .
4.git commit -a -m 'init repository'
5. git push -u origin master

方式二、先在github上手动创建仓库react-native-app-info,本地进行关联。

在本地执行以下命令把代码同步到你github对应的repository中:

1、 cd react-native-app-info

2、 git init //初始化本地仓库

3、 git add . //添加要push到远程仓库的文件或文件夹

4、 git commit -m 'init repository'

5、 git remote add origin  "github对应的项目git地址"  //建立链接远程仓库

6、 git push -u origin master #将本地仓库push到远程仓库

注意:git push 之前还需要编辑.gitignore文件。

.gitignore中定义哪些文件不进行git管理,也就是不会上传到远程库。

3.2 组件发布到npm registry

开发好组件之后,想在其他的项目(或者提供给其他人安装使用)中通过npm install的方式安装你的组件,那么你的组件必须发布到npm registry中。

3.2.1 npm registry 设置

npm registry 是什么

简单来说,npm registry就相当于一个包注册管理中心。它管理着全世界的开发者们发布上来的各种插件,同时开发者们可以通过npm install的方式安装所需要的插件。

npm官方registry为:http://registry.npmjs.org/

国内速度较快的为:https://registry.npm.taobao.org/

查看registry

可以查看当前使用的registry:
$ npm config get registry

切换registry

当然也可以通过命令切换当前使用的npm registry

全局切换

$ npm config set registry http://registry.npmjs.org/

临时指定

有时候你可能只想在执行某些npm命令时临时切换,这个时候,可以使用--registry来指定临时切换的registry,比如在npm发布
$ npm publish --registry http://registry.npmjs.org/

注意:国内目前发布组件时,必须切换为npmjs,否则$ npm publish也不会成功

3.2.2 创建/登陆npm registry账户

要发布组件到npm registry,你必须要是npm registry的注册用户,通过:

$ npm adduser

来新增一个用户,或者你已经在官网注册了一个用户,可以通过:

$ npm login

来登陆npm registry账户。

利用以下两种方式来确认你是否创建/登陆成功npm registry

  1. 命令$ npm whoami确认本地是否成功登陆认证成功
  2. 在线打开 https://npmjs.com/~username 查看是否创建账户成功

注意:初次注册、登录npm账号,需要填写邮箱,并进行激活

3.2.3 发布前准备(编写git、npm等配置文件)

编写.gitignore 和 .npmignore文件
  1. .gitignore中定义哪些文件不上传到github中
  2. .npmignore中定义哪些文件发布时不打包
  3. 如果有.gitignore但是没有.npmignore文件,那么.gitignore可以充当.npmignore的作用
  4. 具体规则可以参照:npm-developers, .gitignore or .npmignore pattern rules
编写package.json

package.json文件定义了发布的所有信息,包括:组件名、版本、作者、描述、依赖等等关键信息。具体可以参照 Working with package.json

下面是react-native-app-info的package.json文件内容:

{
  "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文件中详细说明组件的使用方法、注意事项等。一般使用Markdown语法来编写

编写CHANGELOG.md

记录版本更新及修改记录

以下配置文件不是必须,看自己需求:

编写.eslintrc.js

配置eslint规则

编写.eslintignore

eslint检测忽略文件配置

编写.babelrc

babe配置文件

3.2.3发布组件到npm

做好以上准备之后,就可以发布了。这里需要注意,首次发布跟后面更新发布是不一样的。

首次发布

第一次发布的话,直接执行命令:

$ npm publish

就搞定了,可以在线查看确认是否发布成功。访问链接(<package>是你发布的npm package名):
https://www.npmjs.com/package/<package>
看看是否已经有内容了,有内容说明发布成功了。

更新发布

如果不是首次发布,需要执行两个命令

$ npm version <update_type>
$ npm publish

$ npm version命令是用来自动更新版本号,update_type取值有patch minor major。那么在什么场景应该选择什么update_type呢?看下表

update_type 场景 版本号规则 举例
- 首次发布 版本号1.0.0 1.0.0
patch 修复bug、微小改动时 从版本号第3位开始增量变动 1.0.0 -> 1.0.1
minor 上线新功能,并且对当前版本已有功能模块不影响时 从版本号第2位开始增量变动 1.0.3 -> 1.1.3
major 上线多个新功能模块,并且对当前版本已有功能会有影响时 从版本号第1位开始增量变动 1.0.3 -> 2.0.0

注意

如果首次发布版本号不是1.0.0的话,那么用$ npm version <update_type>
来更新会报错,因为你没有按照它约定的版本规则来,这个时候,你可以手动修改package.json中的version字段为符合约定规则的版本号,然后直接执行$ npm publish就可以,然后下次再增量更新的时候,就可以直接使用$ npm version <update_type>的方式来自动更新版本号了

到此,组件的发布流程就走完了~~

本文参考+推荐:
https://github.com/frostney/react-native-create-library
https://www.jianshu.com/p/091a68ea1ca7

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345