简介
为了提高代码的复用,减少重复工作,我们经常会把比较独立的功能模块封装成组件或者library
在iOS我们通常封装为静态库(.framework和.a),
在Android中我们通常封装为Android Library(gradle模块),
在JavaScript中我们通常封装成node package,
而在ReactNative中我们要封装与原始有关的库我们需要,结合iOS静态库(.a)、Android Library和node package一起封装成ReactNative用的node package。
目录结构
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包的配置文件
源码地址
制作
装备工作
创建ReactNative工程
我们需要先创建一个ReactNative工程,使用如下命令创建。
$ react native init A7011CreatNativeModule
创建存放封装库的目录
$ cd A7011CreatNativeModule/node_modules
$ mkdir testmodule-wyh
$ cd testmodule-wyh
$ mkdir ios
$ mkdir android
$ mkdir component
封装iOS库
步骤
1. 静态库创建
由于ReactNative的组件都是一个个静态库,我们发布到npm给别人使用的话,也需要建立静态库。我们使用xcode建立静态库,取名为Testmodule
。建立之后,我们将创建的静态库中的文件全部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. 引入到目标工程
使用xcode打开A7011CreatNativeModule/ios/
下的iOS工程,将BGNativeModuleExample
静态库工程拖动到工程中的Library中。
为什么要拖到RN工程中呢?
因为,不拖到RN工程中去的话,用的RN库中的API,就会报错,所以需要RN工程的基础环境。
为什么这里要先把静态库放到
node_modules/Testmodule/ios
,再拖到RN工程中去呢?那是因为,如果直接把创建的静态库的TestModule.xcodeproj文件拖到工程中去,无法编辑这个静态库,只有在
node_modules/Testmodule/ios
才能编辑。目前还不知道原因
3. 编写原生代码
咱们这里主要是演示做封装RN的node package,所以就封装一个最简单的组件,一个背景色为红色的View。
-
创建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
-
创建桥接类
// 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
-
需要更复杂的交互,可以参考React Native官网的iOS原生模块和原生UI组件
4. React组件创建
-
接下来你需要一些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
创建index.js
文件,导出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. 运行效果
image注意
引入第三方库
iOS封装静态库的时候,经常会用到需要引入的系统库和第三方库;系统库引入很简单,只要在TestModule.xcodeproj->Build Phases->Link Bnary with Libraries
添加系统库就行;而第三方库有可能是.a也有可能是.framework,.a拷贝到静态库根目录里面,接下就和系统库添加一样了,而.framework就复杂了,他不能直接引用到封装的静态库中,需要引用到目标iOS工程中去才可以。
桥接ViewController
封装Android库
步骤
1. 创建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
依赖我们新创建的Library。 - 我们还需要让新创建的Library依赖
react native
,和上面差不多,只需要在我们新创建的testmodule-wyh
下的build.gradle
中的dependencies
添加一行compile "com.facebook.react:react-native:+"
就行了。
3. 编写原生代码
Android原生代码的编写,前两步和iOS步骤类似,多了一个创建ReactPackage实现,并在MainApplication的getPackages
方法里面注册。
-
创建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)); } }
-
创建桥接类
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); } }
-
需要创建一个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(), // ....... ); } //....... }; //....... }
-
需要更复杂的交互,可以参考React Native官网的android原生模块和原生UI组件
4. 组件创建使用
之后的步骤在封装iOS库的4、5、6步已经做了
5. 运行效果
直接在点击run,看结果
image
注意
Gradle问题
- com.android.tools.build:gradle工具也有2升级为3以后,dependencie依赖由compile变为implementation,而用React Native init ProjectName创建的rn项目还是用的compile,所以创建的时候要调整Gradle.build的dependencie和minSdkVersion以适用目标项目。
- 一定要记得创建ReactPackage的接口实现类,这样当执行
react-native link testmodule-wyh
才会自动添加到依赖。
整理成node package
-
将封装的Android library里面的所以文件移到
node_modules/Testmodule/android
里面,形成的目录结构如下:. ├── 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创建一个package.json文件,全部使用默认项就行,这样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有关的代码全部删除
-
在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项目根目录执行如下指令
$ yarn install $ react-native link testmodule-wyh
-
看到如下提示表示成功
image -
运行iOS和Android查看效果
$ react-native run-android $ react-native run-ios
注意事项
-
iOS和Android的命名不一定要和封装的node package名一直。
-
默认iOS和Android的library都要在根目录,如果不在根目录需要在packag.json指明如下
{ "name": "react-native-maps", // ... "rnpm": { "android": { "sourceDir": "./lib/android" } } }
上传代码库
在GitHub,创建一个代码仓库,将node package 代码上传,然后在package.json添加repository
如下:
"repository" :
{
"type" : "git",
"url" : "https://github.com/<yourusername>/testmodule-wyh.git"
}
repository 属性不写也可以,但是最好建一个 github 项目然后把地址写进来,方便以后维护。
发布
注册npm账号
发布node package到npm服务器上,需要npm的账号,注册哪怕npm账号有两种形式
-
去npm官网注册
-
使用命令行注册
$ npm adduser Username: XXX # 账户名 Password: *** # 密码 Email: (this IS public) XXX@XXX.com # 邮箱
如果用命令行注册的话,最好是在npm官网验证一下
登录npm账户
首次需要登录,npm login(需要输入用户名,密码,还有邮箱) 存储证书到本地,后面就不需要每次都登录的
$ npm login
Username: wangyinghui
Password:
Email: (this IS public) iyinghui@163.com
Logged in as wangyinghui on http://registry.npmjs.org/.
开始发布
$ npm publish
+ testmodule-wyh@1.0.2
发布时报错
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
出现原因:使用的是淘宝源cnpm,登陆到的是cnpm
解决方法:切换到npmjs的网址,代码如下:
$ npm config set registry http://registry.npmjs.org/
发布完成之后,如果还想回到之前的cnpm,使用下面的命令
$ npm config set registry https://registry.npm.taobao.org
npm 安装git项目的几种方式
1. 直接通过用户名安装
# 直接利用用户名与仓库名进行安装
$ npm install yiifaa/yii-es6-amd
# 或者为了提醒自己,加上github前缀进行区分
$ npm install github:yiifaa/yii-es6-amd1234
2. 通过地址安装
# 这样适合安装公司内部的git服务器上的项目
$ 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
网友评论