簡介
為了提高代碼的復(fù)用,減少重復(fù)工作,我們經(jīng)常會把比較獨立的功能模塊封裝成組件或者library
在iOS我們通常封裝為靜態(tài)庫(.framework和.a),
在Android中我們通常封裝為Android Library(gradle模塊),
在JavaScript中我們通常封裝成node package,
而在ReactNative中我們要封裝與原始有關(guān)的庫我們需要,結(jié)合iOS靜態(tài)庫(.a)、Android Library和node package一起封裝成ReactNative用的node package。
目錄結(jié)構(gòu)
testmodule-wyh
.
├── android // 存放Android library
│ ├── build
│ ├── build.gradle
│ ├── libs
│ ├── proguard-rules.pro
│ ├── src
│ └── testmodule.iml
├── component // 存放js橋接過來的組件以及api
│ └── TestView.js
├── index.js
├── ios // 存放iOS library
│ ├── TestModule
│ └── TestModule.xcodeproj
└── package.json // node包的配置文件
源碼地址
制作
裝備工作
創(chuàng)建ReactNative工程
我們需要先創(chuàng)建一個ReactNative工程,使用如下命令創(chuàng)建。
$ react native init A7011CreatNativeModule
創(chuàng)建存放封裝庫的目錄
$ cd A7011CreatNativeModule/node_modules
$ mkdir testmodule-wyh
$ cd testmodule-wyh
$ mkdir ios
$ mkdir android
$ mkdir component
封裝iOS庫
步驟
1. 靜態(tài)庫創(chuàng)建
由于ReactNative的組件都是一個個靜態(tài)庫,我們發(fā)布到npm給別人使用的話,也需要建立靜態(tài)庫。我們使用xcode建立靜態(tài)庫,取名為Testmodule。建立之后,我們將創(chuàng)建的靜態(tài)庫中的文件全部copy到node_modules/Testmodule/ios目錄下。 ios文件目錄如下:
testmodule-wyh
.
├── android // 存放Android library
├── component // 存放js橋接過來的組件以及api
└── ios // 存放iOS library
├── Testmodule
│ ├── Testmodule.h
│ └── Testmodule.m
└── TestModule.xcodeproj
2. 引入到目標(biāo)工程
使用xcode打開A7011CreatNativeModule/ios/下的iOS工程,將BGNativeModuleExample靜態(tài)庫工程拖動到工程中的Library中。

為什么要拖到RN工程中呢?
因為,不拖到RN工程中去的話,用的RN庫中的API,就會報錯,所以需要RN工程的基礎(chǔ)環(huán)境。
為什么這里要先把靜態(tài)庫放到
node_modules/Testmodule/ios,再拖到RN工程中去呢?那是因為,如果直接把創(chuàng)建的靜態(tài)庫的TestModule.xcodeproj文件拖到工程中去,無法編輯這個靜態(tài)庫,只有在
node_modules/Testmodule/ios才能編輯。目前還不知道原因
3. 編寫原生代碼
咱們這里主要是演示做封裝RN的node package,所以就封裝一個最簡單的組件,一個背景色為紅色的View。
-
創(chuàng)建TestView Class
// TestView.h #import <UIKit/UIKit.h> @interface TestView : UIView @end // TestView.m #import "TestView.h" @implementation TestView - (instancetype)init { self = [super init]; if (self) { self.backgroundColor = [UIColor redColor]; } return self; } @end -
創(chuàng)建橋接類
// TestViewManager.h #import <React/RCTBridgeModule.h> @interface TestViewManager : RCTViewManager <RCTBridgeModule> @end // TestViewManager.m #import "TestViewManager.h" #import <React/RCTViewManager.h> #import "TestView.h" @interface TestViewManager : RCTViewManager @end @implementation TestViewManager RCT_EXPORT_MODULE() - (UIView *)view { return [[TestView alloc] init]; } @end 需要更復(fù)雜的交互,可以參考React Native官網(wǎng)的iOS原生模塊和原生UI組件
4. React組件創(chuàng)建
-
接下來你需要一些Javascript代碼來讓這個視圖變成一個可用的React組件:
// A7011CreatNativeModule/node_modules/testmodule-wyh/component/TestView.js // TestView.js import {requireNativeComponent} from 'react-native'; const TestView = requireNativeComponent('TestView', null); export default TestView; -
在路徑
A7011CreatNativeModule/node_modules/testmodule-wyh創(chuàng)建index.js文件,導(dǎo)出TestView組件// index.js import TestView from './component/TestView' export default TestView;
5. 組件使用
在RN工程里面的App組件中,使用這個組件
// App.js
import React, {Component} from 'react';
// ......
export default class App extends Component<Props> {
render() {
return (
<View style={styles.container}>
// ......
<TestView style={{width: 100, height: 100}} /> // TestView組件
</View>
);
}
}
// ......
});
6. 運行效果

注意
引入第三方庫
iOS封裝靜態(tài)庫的時候,經(jīng)常會用到需要引入的系統(tǒng)庫和第三方庫;系統(tǒng)庫引入很簡單,只要在TestModule.xcodeproj->Build Phases->Link Bnary with Libraries添加系統(tǒng)庫就行;而第三方庫有可能是.a也有可能是.framework,.a拷貝到靜態(tài)庫根目錄里面,接下就和系統(tǒng)庫添加一樣了,而.framework就復(fù)雜了,他不能直接引用到封裝的靜態(tài)庫中,需要引用到目標(biāo)iOS工程中去才可以。
橋接ViewController
封裝Android庫
步驟
1. 創(chuàng)建Android library
用Android studio打開RN工程中的Android工程,新建file->new->new module..->Android library,并命名為testmodule

2. RN的Android工程中配置模塊
- 在
app工程中的build.gradle文件中的dependencies添加一行compile project(':testmodule-wyh'),讓主工程app依賴我們新創(chuàng)建的Library。 - 我們還需要讓新創(chuàng)建的Library依賴
react native,和上面差不多,只需要在我們新創(chuàng)建的testmodule-wyh下的build.gradle中的dependencies添加一行compile "com.facebook.react:react-native:+"就行了。
3. 編寫原生代碼
Android原生代碼的編寫,前兩步和iOS步驟類似,多了一個創(chuàng)建ReactPackage實現(xiàn),并在MainApplication的getPackages方法里面注冊。
-
創(chuàng)建TestView Class
package com.example.testmodule; import android.content.Context; import android.graphics.Color; import android.view.View; public class TestView extends View { public TestView(Context context) { super(context); this.setBackgroundColor(Color.rgb(255,0,0)); } } -
創(chuàng)建橋接類
package com.example.testmodule; import com.facebook.react.uimanager.SimpleViewManager; import com.facebook.react.uimanager.ThemedReactContext; public class TestViewManager extends SimpleViewManager<TestView> { public static final String REACT_CLASS = "TestView"; @Override public String getName() { return REACT_CLASS; } @Override protected TestView createViewInstance(ThemedReactContext reactContext) { return new TestView(reactContext); } } -
需要創(chuàng)建一個Package注冊類
package com.example.testmodule; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class TestPackage implements ReactPackage { @Override public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { return Collections.emptyList(); } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { List<ViewManager> modules = new ArrayList<>(); modules.add(new TestViewManager()); return modules; } } -
在MainApplication.java中注冊這個package
package com.a7011creatnativemodule; // ...... public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { //....... @Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(), new TestPackage(), // ....... ); } //....... }; //....... } 需要更復(fù)雜的交互,可以參考React Native官網(wǎng)的android原生模塊和原生UI組件
4. 組件創(chuàng)建使用
之后的步驟在封裝iOS庫的4、5、6步已經(jīng)做了
5. 運行效果
直接在點擊run,看結(jié)果

注意
Gradle問題
- com.android.tools.build:gradle工具也有2升級為3以后,dependencie依賴由compile變?yōu)閕mplementation,而用React Native init ProjectName創(chuàng)建的rn項目還是用的compile,所以創(chuàng)建的時候要調(diào)整Gradle.build的dependencie和minSdkVersion以適用目標(biāo)項目。
- 一定要記得創(chuàng)建ReactPackage的接口實現(xiàn)類,這樣當(dāng)執(zhí)行
react-native link testmodule-wyh才會自動添加到依賴。
整理成node package
-
將封裝的Android library里面的所以文件移到
node_modules/Testmodule/android里面,形成的目錄結(jié)構(gòu)如下:. ├── android │ ├── build.gradle │ ├── libs │ ├── proguard-rules.pro │ ├── src │ ├── androidTest │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ └── example │ │ │ └── testmodule │ │ │ ├── TestPackage.java │ │ │ ├── TestView.java │ │ │ └── TestViewManager.java │ │ └── res │ │ ├── drawable │ │ └── values │ │ └── strings.xml │ └── test │ └── testmodule.iml ├── component │ └── TestView.js ├── index.js ├── ios │ ├── TestModule │ │ ├── TestView.h │ │ ├── TestView.m │ │ ├── TestViewManager.h │ │ └── TestViewManager.m │ └── TestModule.xcodeproj -
在路徑
node_modules/Testmodule-wyh/使用npm init創(chuàng)建一個package.json文件,全部使用默認(rèn)項就行,這樣Testmodule-wyh node package就封裝完成了。// package.json { "name": "testmodule-wyh", "version": "1.0.0", "description": "test", "main": "index.js", "directories": { "lib": "lib" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" }
使用
testmodule-wyh node package拷貝到桌面,然后將iOS和Android項目中與Testmodule-wyh有關(guān)的代碼全部刪除
-
在RN項目中package.json中使用本地路徑的方式引用制作的testmodule-wyh node package
// package.json { "name": "A7011CreatNativeModule", "version": "0.0.1", "private": true, "scripts": { "start": "node node_modules/react-native/local-cli/cli.js start", "test": "jest" }, "dependencies": { "react": "16.4.1", "react-native": "0.56.0", "testmodule": "file:/Users/wangyinghui/Desktop/testmodule-wyh", }, "devDependencies": { "babel-jest": "23.4.0", "babel-preset-react-native": "^5", "jest": "23.4.0", "react-test-renderer": "16.4.1" }, "jest": { "preset": "react-native" } } -
在RN項目根目錄執(zhí)行如下指令
$ yarn install $ react-native link testmodule-wyh -
看到如下提示表示成功
image -
運行iOS和Android查看效果
$ react-native run-android $ react-native run-ios
注意事項
iOS和Android的命名不一定要和封裝的node package名一直。
-
默認(rèn)iOS和Android的library都要在根目錄,如果不在根目錄需要在packag.json指明如下
{ "name": "react-native-maps", // ... "rnpm": { "android": { "sourceDir": "./lib/android" } } }
上傳代碼庫
在GitHub,創(chuàng)建一個代碼倉庫,將node package 代碼上傳,然后在package.json添加repository如下:
"repository" :
{
"type" : "git",
"url" : "https://github.com/<yourusername>/testmodule-wyh.git"
}
repository 屬性不寫也可以,但是最好建一個 github 項目然后把地址寫進來,方便以后維護。
發(fā)布
注冊npm賬號
發(fā)布node package到npm服務(wù)器上,需要npm的賬號,注冊哪怕npm賬號有兩種形式
去npm官網(wǎng)注冊
-
使用命令行注冊
$ npm adduser Username: XXX # 賬戶名 Password: *** # 密碼 Email: (this IS public) XXX@XXX.com # 郵箱
如果用命令行注冊的話,最好是在npm官網(wǎng)驗證一下
登錄npm賬戶
首次需要登錄,npm login(需要輸入用戶名,密碼,還有郵箱) 存儲證書到本地,后面就不需要每次都登錄的
$ npm login
Username: wangyinghui
Password:
Email: (this IS public) iyinghui@163.com
Logged in as wangyinghui on http://registry.npmjs.org/.
開始發(fā)布
$ npm publish
+ testmodule-wyh@1.0.2
發(fā)布時報錯
1. cnpm造成的報錯
$ npm publish
npm ERR! registry error parsing json
npm ERR! publish Failed PUT 413
npm ERR! Unexpected token < in JSON at position 0
npm ERR! <html>
npm ERR! <head><title>413 Request Entity Too Large</title></head>
npm ERR! <body bgcolor="white">
npm ERR! <center><h1>413 Request Entity Too Large</h1></center>
npm ERR! <hr><center>nginx/1.4.6 (Ubuntu)</center>
npm ERR! </body>
npm ERR! </html>
npm ERR!
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/wangyinghui/.npm/_logs/2018-07-13T02_27_29_082Z-debug.log
出現(xiàn)原因:使用的是淘寶源cnpm,登陸到的是cnpm
解決方法:切換到npmjs的網(wǎng)址,代碼如下:
$ npm config set registry http://registry.npmjs.org/
發(fā)布完成之后,如果還想回到之前的cnpm,使用下面的命令
$ npm config set registry https://registry.npm.taobao.org
npm 安裝git項目的幾種方式
1. 直接通過用戶名安裝
# 直接利用用戶名與倉庫名進行安裝
$ npm install yiifaa/yii-es6-amd
# 或者為了提醒自己,加上github前綴進行區(qū)分
$ npm install github:yiifaa/yii-es6-amd1234
2. 通過地址安裝
# 這樣適合安裝公司內(nèi)部的git服務(wù)器上的項目
$ npm install git+https://git@github.com/yiifaa/yii-es6-amd.git#v1.0.0
# 或者以ssh的方式
$ npm install git+ssh://git@github.com/yiifaa/yii-es6-amd.git#v1.0.0
