ReactNative: 将自定义的ReactNative组件制作成第三方库的详细流程(制作-->发布)

一、简介

在讲本篇博文之前,需要你熟知怎么自定义ReactNative组件,然后才好学习将自定义的ReactNative组件制作成第三方库。本文中的自定义的ReactNative组件LoginManager API 源自上篇文章,所以需要先看一下上篇博文。言归正传,ReactNative的确提供了一个非常便捷的方式来扩展Native模块。如果要把模块做成第三方组件的话,还有一些工作要做:首先以一个静态库工程来编译模块代码,提供JavaScript的封装,最后创建Package.json来支持node的引用。在步骤开始之前,我先把上篇文章中创建Native原生模块的LoginManager API组件的类贴出来,如下所示:

LoginManager.h

//
//  LoginManager.h
//  RNDemo
//
//  Created by 夏远全 on 2020/1/16.
//  Copyright © 2020 Facebook. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <React/RCTUtils.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTBridgeModule.h>

NS_ASSUME_NONNULL_BEGIN

//OC中定义一个枚举并导出
typedef NS_ENUM(NSInteger, MoveDiretion){
    MoveDiretionNone,
    MoveDiretionLeft,
    MoveDiretionRight,
    MoveDiretionBottom,
    MoveDiretionTop
};

@interface LoginManager : NSObject<RCTBridgeModule>

@end

NS_ASSUME_NONNULL_END

LoginManager.m

//
//  LoginManager.m
//  RNDemo
//
//  Created by 夏远全 on 2020/1/16.
//  Copyright © 2020 Facebook. All rights reserved.
//

#import "LoginManager.h"

#ifdef DEBUG
#define NSLog(format, ...) printf("[%s] %s [第%d行] %s\n", __TIME__, __FUNCTION__, __LINE__, [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]);
#else
#define NSLog(format, ...)
#endif

@implementation LoginManager

//初始化, 添加屏幕旋转监听者
-(instancetype)init {
    self = [super init];
    if (self) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
    }
    return self;
}

//导出模块类
RCT_EXPORT_MODULE();

//重映射,auth为code方法的新名称
RCT_REMAP_METHOD(auth,code:(NSString *)account{
                      NSLog(@"%s---获取验证----",__func__);
                      NSLog(@"account----%@",account);
                  });

//login为方法名
RCT_EXPORT_METHOD(login:(NSString *)account password:(NSString *)password{
                      NSLog(@"%s---登录账号----",__func__);
                      NSLog(@"account----%@",account);
                      NSLog(@"password----%@",password);
                  });

//logout为方法名
RCT_EXPORT_METHOD(logout:(NSString *)account{
                      NSLog(@"%s---退出账号----",__func__);
                      NSLog(@"account----%@",account);
                  });

//设置普通的回调函数
//fetchUserInfoWithToken为方法名,successCallback为成功回调,failureCallback为失败回调
RCT_EXPORT_METHOD(fetchUserInfoWithToken:(NSString *)token success:(RCTResponseSenderBlock)successCallback failure:(RCTResponseErrorBlock)failureCallback
    {
            if(token.length>0 && successCallback){
                  successCallback(@[
                                    @"account = xiayuanquan",
                                    @"password = 123456",
                                    [NSString stringWithFormat:@"token = %@",token]
                                  ]);
            }
            else{
                  if(failureCallback){
                        failureCallback(
                                          [[NSError alloc] initWithDomain:NSOSStatusErrorDomain
                                                                     code:404
                                                                 userInfo: @{NSLocalizedDescriptionKey: @"token exception"}]
                                        );
                  }
            }
    });

//设置异步处理的回调函数
//sendMessage为方法名,successCallback为成功回调,failureCallback为失败回调
RCT_EXPORT_METHOD(sendMessage:(NSString *)message success:(RCTPromiseResolveBlock)successCallback failure:(RCTPromiseRejectBlock)failureCallback
    {
            if(message.length>0 && successCallback){
                  successCallback(@"发送成功!");
            }
            else{
                  if(failureCallback){
                      failureCallback(@"300",@"发送失败",nil);
                  }
            }
    });

//重写constantsToExport, 枚举常量导出
- (NSDictionary<NSString *, id> *)constantsToExport {
   return @{
        @"MoveDiretionNone": @(MoveDiretionNone),
        @"MoveDiretionLeft": @(MoveDiretionLeft),
        @"MoveDiretionRight": @(MoveDiretionRight),
        @"MoveDiretionBottom": @(MoveDiretionBottom),
        @"MoveDiretionTop": @(MoveDiretionTop)
   };
}

//定义一个移动方法,根据传入的枚举值移动
//move为方法名
RCT_EXPORT_METHOD(move:(MoveDiretion)moveDiretion{
                    switch(moveDiretion){
                        case MoveDiretionNone:
                             NSLog(@"仍保持原始位置 --- MoveDiretionNome");
                        break;
                        case MoveDiretionLeft:
                             NSLog(@"向左边移动位置 --- MoveDiretionLeft");
                        break;
                        case MoveDiretionRight:
                             NSLog(@"向右边移动位置 --- MoveDiretionRight");
                        break;
                        case MoveDiretionBottom:
                             NSLog(@"向下边移动位置 --- MoveDiretionBottom");
                        break;
                        case MoveDiretionTop:
                             NSLog(@"向上边移动位置 --- MoveDiretionTop");
                        break;
                    }
                 });

//可以重写队列,给当前模块类指定自定义的串行队列。若不指定,则系统默认会给当前模块类随机分配一个串行队列。
//这个方法一旦重写。当前模块的所有方法均会在该自定义的串行队列中异步执行
-(dispatch_queue_t)methodQueue{
    return dispatch_queue_create("com.facebook.ReactNative.LoginManagerQueue", DISPATCH_QUEUE_SERIAL);
}

//定义一个方法,获取线程和队列信息
//thread为方法名
RCT_EXPORT_METHOD(thread:(BOOL)newQueue{

              const char *queueName = dispatch_queue_get_label([self methodQueue]);
              NSLog(@"当前线程1 ------- %@-----%s", [NSThread currentThread], queueName);

              if(newQueue){

                  dispatch_async(dispatch_get_main_queue(), ^{
                      const char *queueName2 = dispatch_queue_get_label(dispatch_get_main_queue());
                      NSLog(@"当前线程2 ------- %@ -------%s", [NSThread currentThread], queueName2);
                  });

                  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                      const char *queueName3 = dispatch_queue_get_label(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
                      NSLog(@"当前线程3 ------- %@-------%s", [NSThread currentThread], queueName3);
                  });
              }
          });

//获取当前屏幕的尺寸
static NSDictionary *Dimensions(){
    CGFloat width = MIN(RCTScreenSize().width, RCTScreenSize().height);
    CGFloat height = MAX(RCTScreenSize().width, RCTScreenSize().height);
    CGFloat scale = RCTScreenScale();
    if(UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)){
        width = MAX(RCTScreenSize().width, RCTScreenSize().height);
        height = MIN(RCTScreenSize().width, RCTScreenSize().height);
    }
    return @{
        @"width": @(width),
        @"height": @(height),
        @"scale": @(scale)
    };
}
//定义一个方法,获取屏幕信息
//getDimensions为方法名
RCT_EXPORT_METHOD(getDimensions:(RCTResponseSenderBlock)callback{
    if (callback) {
        callback(@[[NSNull null], Dimensions()]);
    }
});

//监听方法,使用RCTEventDispatcher的eventDispatcher调用sendDeviceEventWithName函数发送事件信息
//可以将事件名称作为常量导出,提供给ReactNative中使用,也即添加到constantsToExport方法中的字典中即可。类似上面的枚举。
@synthesize bridge = _bridge;
-(void)orientationDidChange:(NSNotification *)notification {
  [_bridge.eventDispatcher sendDeviceEventWithName:@"orientationDidChange" body:@{
      @"orientation": UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation) ? @"Landscape": @"Portrait",
      @"Dimensions": Dimensions()}
   ];
}

//移除监听者
-(void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

@end

RCTConvert+MoveDiretion.h

//
//  RCTConvert+MoveDiretion.h
//  RNDemo
//
//  Created by 夏远全 on 2020/1/17.
//  Copyright © 2020 Facebook. All rights reserved.
//

#import <React/RCTConvert.h>

NS_ASSUME_NONNULL_BEGIN

@interface RCTConvert (MoveDiretion)

@end

NS_ASSUME_NONNULL_END

RCTConvert+MoveDiretion.m

//
//  RCTConvert+MoveDiretion.m
//  RNDemo
//
//  Created by 夏远全 on 2020/1/17.
//  Copyright © 2020 Facebook. All rights reserved.
//

#import "RCTConvert+MoveDiretion.h"
#import "LoginManager.h"

@implementation RCTConvert (MoveDiretion)

//给RCTConvert类添加扩展,这样在模块方法调用中使用常量导出的枚举值,通信到Native中时,会从整型自动转换为定义的枚举类型
RCT_ENUM_CONVERTER(MoveDiretion,(@{
   @"MoveDiretionNone": @(MoveDiretionNone),
   @"MoveDiretionLeft": @(MoveDiretionLeft),
   @"MoveDiretionRight": @(MoveDiretionRight),
   @"MoveDiretionBottom": @(MoveDiretionBottom),
   @"MoveDiretionTop": @(MoveDiretionTop),
}), MoveDiretionNone, integerValue)

@end

二、步骤

1、使用xcode创建一个名为LoginManager的静态库。

2、打开静态库,将上面贴出来的已经实现好了的Native模块LoginManager API组件的类全部拷贝进去或进行替换。

3、选择Build Settings,配置Header Search Paths路径。

6、打开终端,初始化一个新的RN项目,随意设置一个名称为:LoginManagerTestProject。(本人安装的ReactNative版本较低)

//初始化
react-native init LoginManagerTestProject --version 0.44.3

7、进入目录node_modules。

//进入node_modules
cd LoginManagerTestProject/node_modules

8、创建要制作的第三库文件夹,指定名称为:react-native-login-manager。

//创建第三方名称
mkdir react-native-login-manager

9、进入这个第三方库文件夹。

//进入库文件
cd react-native-login-manager

10、创建ios文件夹。

//创建ios文件
mkdir ios

11、将之前创建的静态库中的根目录下的文件全部copy到这个RN工程LoginManagerTestProject/node_modules/react-native-login-manager/ios目录下。目录结构如下:

12、使用xcode打开这个RN工程LoginManagerTestProject,将静态库LoginManager的.xcodeproj文件 拖到 RN工程LoginManagerTestProject的Libraries文件下。

13、手动添加libLoginManager.a静态包,然后编译,如果编译不出错,则success。

14、使用终端或者webStorm等编程工具进入到RN工程LoginManagerTestProject/node_modules/react-native-login-manager目录下,创建index.js文件,它是整个原生模块的入口,我们这里只是将原生LoginManager模块类进行导出。此时也就完成了第三方库制作的第一步了,仅仅可以自己使用。 (还需要发布到npm上,分享给大家安装使用)

三、测试

好了,咱先自己测试一下,看看行不行。结果是行的,??。

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from ‘react‘;
import {
    AppRegistry,
    StyleSheet,
    View
} from ‘react-native‘;

//使用自定义的react-native-login-manager第三方库的模块方法
import LoginManager from ‘react-native-login-manager‘;
LoginManager.auth("xiayuanquan");

//使用LoginManager模块类的监听通知
const RCTDeviceEventEmitter = require(‘RCTDeviceEventEmitter‘);
let subscription = RCTDeviceEventEmitter.addListener(‘orientationDidChange‘, (dimensions) => {
    Object.keys(dimensions).forEach(key => console.log(key, dimensions[key]));
});
//subscription.remove();

export default class LoginManagerTestProject extends Component {
    render() {
        return (
            <View style={styles.container}>

            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: ‘center‘,
        alignItems: ‘center‘,
        backgroundColor: ‘#F5FCFF‘,
    }
});

AppRegistry.registerComponent(‘LoginManagerTestProject‘, () => LoginManagerTestProject); 

运行的结果如下所示:标红部分确实打印出结果了。

2020-01-18 14:42:21.119 [info][tid:main][RCTBatchedBridge.m:77] Initializing <RCTBatchedBridge: 0x6000023cc1c0> (parent: <RCTBridge: 0x6000031c9490>, executor: RCTJSCExecutor)2020-01-18 14:42:21.173 [warn][tid:com.facebook.react.JavaScript][RCTModuleData.mm:220] RCTBridge required dispatch_sync to load RCTDevSettings. This may lead to deadlocks
2020-01-18 14:42:21.173562+0800 LoginManagerTestProject[89258:2044559] RCTBridge required dispatch_sync to load RCTDevSettings. This may lead to deadlocks

[14:30:32] -[LoginManager code:] [第34行] -[LoginManager code:]---获取验证----
[14:30:32] -[LoginManager code:] [第35行] account----xiayuanquan
2020-01-18 14:42:21.391340+0800 LoginManagerTestProject[89258:2044406] Running application LoginManagerTestProject ({
    initialProps =     {
    };
    rootTag = 1;
})
2020-01-18 15:20:51.450 [info][tid:com.facebook.react.JavaScript] ‘Dimensions‘, { width: 375, scale: 2, height: 667 }
2020-01-18 15:20:51.450504+0800 LoginManagerTestProject[89634:2069334] ‘Dimensions‘, { width: 375, scale: 2, height: 667 }
2020-01-18 15:20:51.451 [info][tid:com.facebook.react.JavaScript] ‘orientation‘, ‘Portrait‘
......
2020-01-18 15:21:08.148 [info][tid:com.facebook.react.JavaScript] ‘Dimensions‘, { width: 667, scale: 2, height: 375 }
2020-01-18 15:21:08.149013+0800 LoginManagerTestProject[89634:2069334] ‘Dimensions‘, { width: 667, scale: 2, height: 375 }
2020-01-18 15:21:08.149 [info][tid:com.facebook.react.JavaScript] ‘orientation‘, ‘Landscape‘
.....

注意事项,如果在xcode11运行时出现类型无法转换。可能是由于RN低版本,Xcode11(iOS13)中对未使用的接口选择器的参数unused字符串属性进行了更改成了__unused__,导致ReactNative动态收集接口时不能把声明的接口进行导入,运行时无法查找到该接口导致的错误。错误如下:

2019-09-25 15:16:47.784 [error][tid:main][RCTModuleMethod.m:376] Unknown argument type ‘__attribute__‘ in method -[RCTAppState getCurrentAppState:error:]. Extend RCTConvert to support this type.
2019-09-25 15:16:47.784408+0800 example[68797:2090899] Unknown argument type ‘__attribute__‘ in method -[RCTAppState getCurrentAppState:error:]. Extend RCTConvert to support this type.

解决办法是需要修改RCTModuleMethod类,找到RCTParseUnused静态函数,使用下面这段代码进行替换(第2行就是新添加的),如下所示:

static BOOL RCTParseUnused(const char **input)
{
  return RCTReadString(input, "__unused") ||
         RCTReadString(input, "__attribute__((__unused__))") ||
         RCTReadString(input, "__attribute__((unused))");
}

四、发布

只有将第三方库发布到了npm上,可以提供给大家安装并进行使用,这才算是真正的第三方库。但是,在将第三方库发布到npm上之前,需要先拥有一个npm账号。

1、查询是否拥有npm账号

//查询
npm whoami

//本人没有安装,所以错误如下
npm ERR! code ENEEDAUTH
npm ERR! need auth This command requires you to be logged in.
npm ERR! need auth You need to authorize this machine using `npm adduser`
npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/xiayuanquan/.npm/_logs/2020-01-18T07_37_42_133Z-debug.log

2、若没有则申请注册一个

//添加一个npm账号
npm adduser

//本人添加结果如下。 如果注册成功,npm官方会发来邮件,用户需要点击邮件进行验证。 "Verify your npm email address"。 一定要进行验证,否则发布无法成功。
Username: xiayuanquan
Password:
Email: (this IS public) [email protected]
Logged in as xiayuanquan on https://registry.npmjs.org/.

拥有了npm账号后,我们现在就可以来进行发布的步骤了。

1、进入RN项目,使用git命令在github上创建仓库react-native-login-manager,和我们的第三方库进行关联。

//进入项目
cd /Users/xiayuanquan/Desktop/LoginManagerTestProject/node_modules/react-native-login-manager 

//git初始化
git init .

//关联远程仓库
git remote add origin https://github.com/xiayuanquan/react-native-login-manager.git

2、仍然在LoginManagerTestProject/node_modules/react-native-login-manager目录下,创建一个package.json文件。发布到npm之前,必须有这个文件存在,它和index.js目录结构是平级的。package.json文件包含了module的所有信息,比如名称、版本、描述、依赖、作者、license等。 使用npm init -y命令来创建package.json,系统帮助完成各个信息的配置(也可以使用npm init命令手动一步步输入配置信息)。

//创建pack.json配置文件
npm init -y

//本人的配置结果如下
Wrote to /Users/xiayuanquan/Desktop/LoginManagerTestProject/node_modules/react-native-login-manager/package.json:
{
  "name": "react-native-login-manager",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "https://gituhb.com/xiayuanquan/react-native-login-manager.git"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

3、如果采用系统帮助创建的pack.json不够完善,可以自己打开这个文件进行编辑。例如,如果我们的第三方库需要依赖,可以手动添加一个dependencies属性。如下所示:pack.json

{
  "name": "react-native-login-manager",
  "version": "1.0.0",
  "description": "this is a third part login library",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "https://gituhb.com/xiayuanquan/react-native-login-manager.git"
  },
  "keywords": [],
  "author": "xiayuanquan",
  "license": "ISC",

  "dependencies": {
  },
} 

4、好了,pack.json配置完毕后。就可以将我们的第三方库react-native-login-manager发布到npm上了。

//发布第三方库
npm publish

如果发布失败,出现如下错误,说明发布采用的镜像源不匹配 (有可能中途不小心切换过)。错误如下:

//错误日志如下
........
npm notice === Tarball Details ===
npm notice name:          react-native-login-manager
npm notice version:       1.0.0
npm notice package size:  18.8 kB
npm notice unpacked size: 41.6 kB
npm notice shasum:        3e576dbb0d45bcc5dd5796928abba724552ab724
npm notice integrity:     sha512-NNKLeNL5XJk1l[...]RR+oP0IKtgYBw==
npm notice total files:   11
npm notice
npm ERR! code E403
npm ERR! 403 403 Forbidden - PUT https://registry.npm.taobao.org/react-native-login-manager - [no_perms] Private mode enable, only admin can publish this module
npm ERR! 403 In most cases, you or one of your dependencies are requesting
npm ERR! 403 a package version that is forbidden by your security policy.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/xiayuanquan/.npm/_logs/2020-01-18T08_08_46_345Z-debug.log

解决办法就是先切换源,然后登录npm,再发布。本人采用nrm管理的源,所以使用nrm切换。nrm管理npm源请参看我的这篇文章,nrm管理npm源。方法如下:

//查看当前源
nrm current       //显示结果:taobao

//切换源
nrm use npm       //显示结果:Registry has been set to: https://registry.npmjs.org/

//再查看
nrm current       //显示结果:npm

//登录npm
npm login        //显示结果: Username: xiayuanquan
                            Password:
                               Email: (this IS public) [email protected]
                            Logged in as xiayuanquan on https://registry.npmjs.org/.

//重新发布
npm publish

//此时,发布成功,结果如下
npm notice
npm notice ??  [email protected]
npm notice === Tarball Contents ===
npm notice 587B   ios/LoginManager/LoginManager.h
npm notice 264B   ios/LoginManager/RCTConvert+MoveDiretion.h
npm notice 129B   index.js
npm notice 428B   package.json
npm notice 7.6kB  ios/LoginManager/LoginManager.m
npm notice 725B   ios/LoginManager/RCTConvert+MoveDiretion.m
npm notice 10.8kB ios/LoginManager.xcodeproj/project.pbxproj
npm notice 238B   ios/LoginManager.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
npm notice 347B   ios/LoginManager.xcodeproj/xcuserdata/xiayuanquan.xcuserdatad/xcschemes/xcschememanagement.plist
npm notice 20.4kB ios/LoginManager.xcodeproj/project.xcworkspace/xcuserdata/xiayuanquan.xcuserdatad/UserInterfaceState.xcuserstate
npm notice 157B   ios/LoginManager.xcodeproj/project.xcworkspace/contents.xcworkspacedata
npm notice === Tarball Details ===
npm notice name:          react-native-login-manager
npm notice version:       1.0.0
npm notice package size:  18.8 kB
npm notice unpacked size: 41.7 kB
npm notice shasum:        74fbf2900e983bc15ad78ef7c62f56c46b331fb3
npm notice integrity:     sha512-gmkR+gIETJabT[...]nG2SwO1ly+Pvg==
npm notice total files:   11
npm notice
+ [email protected]                

发布成功后,邮箱会收到通知,如“ Successfully published [email protected]”。也可以登录npm网站进行查看:https://www.npmjs.com。如下所示:

5、至此,我们已经成功把module发布到了npmjs.org。当然,我们也需要将我们的代码发布到github。

//来取分支
git pull origin master

//添加文件
git add .

//提交日志
git commit -m ‘add all files of project‘

//提交分支
git push origin master

五、验证

1、创建一个新的RN项目进行测试。如下所示:

//创建RN项目
react-native init TestThirdPart --version 0.44.3

2、安装react-native-login-manager第三方库。如下所示:

//安装库
npm install react-native-login-manager --save

3、将第三方库链接到RN项目中。如下所示:

//链接第三方库
react-native link react-native-login-manager

4、使用xcode打开项目。如下所示:

5、测试结果。如下所示:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from ‘react‘;
import {
    AppRegistry,
    StyleSheet,
    View
} from ‘react-native‘;

import LoginManager from ‘react-native-login-manager‘;

//使用自定义的react-native-login-manager第三方库的模块方法
LoginManager.auth("13710371234");

//使用LoginManager模块类的监听通知
const RCTDeviceEventEmitter = require(‘RCTDeviceEventEmitter‘);
let subscription = RCTDeviceEventEmitter.addListener(‘orientationDidChange‘, (dimensions) => {
    Object.keys(dimensions).forEach(key => console.log(key, dimensions[key]));
});
//subscription.remove();

export default class TestThirdPart extends Component {
    render() {
        return (
            <View style={styles.container}>

            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: ‘center‘,
        alignItems: ‘center‘,
        backgroundColor: ‘#F5FCFF‘,
    }
});

AppRegistry.registerComponent(‘TestThirdPart‘, () => TestThirdPart);
//日志如下
.......

2020-01-18 17:35:58.759817+0800 TestThirdPart[96482:2177939] RCTBridge required dispatch_sync to load RCTDevSettings. This may lead to deadlocks

[17:35:41] -[LoginManager code:] [第34行] -[LoginManager code:]---获取验证----
[17:35:41] -[LoginManager code:] [第35行] account----13710371234

2020-01-18 17:36:15.866355+0800 TestThirdPart[96482:2177905] [] nw_socket_handle_socket_event [C3.1:1] Socket SO_ERROR [61: Connection refused]
2020-01-18 17:36:15.868757+0800 TestThirdPart[96482:2177905] [] nw_socket_handle_socket_event [C3.2:1] Socket SO_ERROR [61: Connection refused]
2020-01-18 17:36:15.870211+0800 TestThirdPart[96482:2177912] [] nw_connection_get_connected_socket [C3] Client called nw_connection_get_connected_socket on unconnected nw_connection
2020-01-18 17:36:15.870855+0800 TestThirdPart[96482:2177912] TCP Conn 0x600000cd8cc0 Failed : error 0:61 [61]
2020-01-18 17:36:15.888 [info][tid:main][RCTRootView.m:295] Running application TestThirdPart ({
    initialProps =     {
    };
    rootTag = 1;
})
2020-01-18 17:36:15.888347+0800 TestThirdPart[96482:2177597] Running application TestThirdPart ({
    initialProps =     {
    };
    rootTag = 1;
})
2020-01-18 17:36:15.891 [info][tid:com.facebook.react.JavaScript] ‘Dimensions‘, { width: 375, scale: 2, height: 667 }
2020-01-18 17:36:15.891136+0800 TestThirdPart[96482:2177939] ‘Dimensions‘, { width: 375, scale: 2, height: 667 }
2020-01-18 17:36:15.891 [info][tid:com.facebook.react.JavaScript] ‘orientation‘, ‘Portrait‘
2020-01-18 17:36:15.891625+0800 TestThirdPart[96482:2177939] ‘orientation‘, ‘Portrait‘
2020-01-18 17:36:15.892 [info][tid:com.facebook.react.JavaScript] ‘Dimensions‘, { width: 375, scale: 2, height: 667 }
2020-01-18 17:36:15.892121+0800 TestThirdPart[96482:2177939] ‘Dimensions‘, { width: 375, scale: 2, height: 667 }
2020-01-18 17:36:15.892 [info][tid:com.facebook.react.JavaScript] ‘orientation‘, ‘Portrait‘
2020-01-18 17:36:15.892490+0800 TestThirdPart[96482:2177939] ‘orientation‘, ‘Portrait‘
........

六、完结

至此,一个真正的ReactNative第三方库开发完成。累死我了。。。。

原文地址:https://www.cnblogs.com/XYQ-208910/p/12208955.html

时间: 2024-10-09 07:34:00

ReactNative: 将自定义的ReactNative组件制作成第三方库的详细流程(制作-->发布)的相关文章

将静态库制作成动态库

一.系统说明 Ubuntu12.04TLS 64位 二.制作静态库 (1)编写需要制作成静态库的程序(根据实际情况进行编写,这里只是一个简单例子)     //bar.h              #ifndef _BAR_H     #define _BAR_H          void bar(int i);          #endif     //bar.c     #include <stdio.h>     #include <stdlib.h>     #incl

iOS开发之自定义表情键盘(组件封装与自动布局)

下面的东西是编写自定义的表情键盘,话不多说,开门见山吧!下面主要用到的知识有MVC, iOS开发中的自动布局,自定义组件的封装与使用,Block回调,CoreData的使用.有的小伙伴可能会问写一个自定义表情键盘肿么这么麻烦?下面将会介绍我们如何用上面提到的东西来定义我们的表情键盘的.下面的内容会比较多,这篇博文还是比较有料的. 还是那句话写技术博客是少不了代码的,下面会结合代码来回顾一下iOS的知识,本篇博文中用到的知识点在前面的博客中都能找到相应的内容,本篇算是一个小小的功能整合.先来张图看

Swing中使用UIManager批量自定义单一JComponent组件默认属性

最近在研究Swing,被它的复杂性气的快吐血了,刚才本打算把JFrame的背景色换成白底,结果发现事情没想象中那么顺利,调用setBackground完全没有效果,猛然醒悟到JPanel本身是带不透明底色的,事情一下子变得复杂起来了,因为即便最简单的窗口布局,也是嵌套了若干层JPanel.JSplitPane等容器的,一层遮盖一层,而要想统一改成白底的话,那得一个个改代码,或者用复杂的遍历算法... 百度上寻觅了一下,发现一个老去已久的帖子有提及到使用UIManager来批量更改默认值的,例如:

自定义vue全局组件use使用、vuex的使用

自定义vue全局组件use使用(解释vue.use()的原理)我们在前面学习到是用别人的组件:Vue.use(VueRouter).Vue.use(Mint)等等.其实使用的这些都是全剧组件,这里我们就来讲解一下怎么样定义一个全局组件,并解释vue.use()的原理而我们再用Axios做交互,则不能使用Vue.use(Axios),因为Axios没有install 自定义一个全局Loading组件,并使用:总结目录: |-components |-loading |-index.js 导出组件,

【笔记】【Informatica】自定义序列生成器组件

/************************************************************************** * * Copyright (c) 2003 Informatica Corporation. This file contains * material proprietary to Informatica Corporation and may not be copied * or distributed in any form withou

C#自定义将各种对象转换成JSON格式的类

这个C#封装类可以用于将各种格式的数据转换成JSON格式,包括List转换成Json,普通集合转换Json ,DataSet转换为Json ,Datatable转换为Json ,DataReader转换为Json等,如果你需要将对象转换成JSON,可以使用这个类. using System; using System.Collections.Generic; using System.Text; using System.Data; using System.Reflection; using

vue自定义事件将组件内的数据传输到实例中去使用

vue自定义事件将组件内的数据传输到实例中去使用 <body> <div id="app"> <h1 style="color:deeppink">{{ outer_title }}</h1> //③给实例绑定一个方法 <hello :reader="new_msg"  v-on:chang_event="chang_tit"></hello> </

陈松松:相近的关键词制作成视频内容,如何优化关键词

这是我写的第71篇原创视频营销文章 每个视频,都是你的替身 你懂的视频营销方法越多, 你解决问题的能力就越强,别人遇到问题只有1种解决方案,而你至少有3种解决方案,效果也会比别人更好! 今天给大家继续分享视频营销108技, 昨天上午有位操作视频营销的小伙子(我猜的), 他跟我说:他把挖掘过来的关键词制作成视频内容,但发现写标题的时候,有很多相同或者类似的关键词,不知道该怎么处理? 其实,这个问题,每一个操作视频营销的朋友都会遇到,只是有的人知道如何解决,有的人直接略过. 如果你的视频营销基本功不

Jsp开发自定义标签,自定义标签将字符串转成指定的时间格式显示

本例以将 字符串格式的时间转成指定的时间格式显示. 第一步.定义一个标签处理程序类,需要集成javax.servlet.jsp.tagext.TagSupport,代码如下: import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Calendar; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.T