QRN v1.4.0
base的官方RN版本从 0.20.0
同步到了 0.33.0
React API
现在需要从 react
中引入v1.4.0
QRN
中 react
版本为 15.3.0
从 0.32.0
开始, React Native
官方开始强制 React API
从 react
中引入
之前的写法:
import React, { Component, View } from 'react-native';
现在的写法:
import React, { Component } from 'react';
import { View } from 'react-native';
React API
中包含了下面的组件:
import { Component,
PropTypes,
createClass,
PureComponent,
Children,
createElement,
cloneElement,
isValidElement,
createFactory
} from 'react';
为了兼容现在线上的业务,在 qunar-react-native
中,react
被同时注入到了 react-native
和 qunar-react-native
中。不过还是建议使用官方推荐的方式从 react
中引入 React API
。
** 需要注意的是 v1.3.0
不会依赖 react
, 但是 v1.4.0
中会依赖至少 15.3.0
版本的 react
**
那么业务RN工程升级 node_modules
时需要检查 package.json
中依赖的其他的模块是否依赖低版本的 react
,导致qnpm install
安装的是低版本的 react
可以通过查看 node_moduels/react/package.json
来确定安装的 react
版本
findNodeHandle
之前的写法:
require('React').findNodeHandle(this.refs.input);
现在的写法
var findNodeHandle = require('react/lib/findNodeHandle');
findNodeHandle(this.refs.input)
有一些其他的组件也和 findNodeHandle
类似,不再挂在 React
中, 可能需要从 require('react/lib/*')
中引入
babel
插件修改为 babel-preset-qrn
qunar-react-native-ext
所依赖的 babel
插件从babel-preset-qreact
修改为 babel-preset-qrn
,在业务更新 node_Modules
的时候会自动替换。
css
布局的改动如果出现图中送钱啦这种下面多出了大块空白的情况,只需要去掉组件布局中的 flex:1
即可解决:
Image
组件0.20.0
的官方 Image
支持 contain
、 cover
和 stretch
这3种 resizeMode
。
qunar-react-native
在官方 0.20.0
的基础上支持了更多的 resizeMode
:
fitCenter (contain)
centerCrop (cover)
fitXy (stretch)
fitStart
fitEnd
focusCrop
center
centerInside
官方 0.32.0
在 0.20.0
基础上又添加了 center
和 repeat(iOS only)
2种 resizeMode
,而官方新增的 center
其实和 QRN
中添加的 centerInside
是一致的。因此 v1.4.0
在 v1.3.0
的基础上只增加了 repeat(iOS only)
。
业务使用中需要注意官方文档对 center
的说明其实对应 qunar-react-native
中的 centerInside
。
关于 resizeMode
的具体说明可以查看 ImageResizeMode.js
, 在QRN 的 demo 中有相关的示例说明。
react-redux
因为 v1.4.0
版本依赖的 react
版本为 15.3.0
,所以如果使用旧的 react-redux
写法则会提示:
With React 0.14 and later versions, you no longer need to wrap <Provider> child into a function.
解决方法,修改:
class OrderListView extends Component {
render() {
return (
<Provider store={store}>
{() => <OrderCenter {...this.props}/>}
</Provider>
)
}
}
为
class OrderListView extends Component {
render() {
return (
<Provider store={store}>
<OrderCenter {...this.props}/>
</Provider>
)
}
}
NetInfo
v1.4.0
中因为官方使用消息的方式重新写了 NetInfo
,导致调用 NetInfo.isConnected
返回的网络状态永远都是 false
NetInfo.isConnected.fetch().then(isConnected => {
alert(isConnected);// isConnected always be false
});
这个为官方的Bug,不好修复,可以使用下面的方法临时fix
var netChange = (isConnected)=>{alert(isConnected)};
NetInfo.isConnected.fetch().then().done(() => {
NetInfo.isConnected.addEventListener('change', netChange);
});
Keyboard event
监听键盘事件需要通过 Keyboard
注册
之前的写法:
const { DeviceEventEmitter } = require('react-native');
DeviceEventEmitter.addListener('keyboardWillShow', func);
现在的写法:
const { Keyboard } = require('react-native');
Keyboard.addListener('keyboardWillShow', func);
出现上面的问题把JSX里面的immutable List转成数组即可解决
官方0.32.0
版本中,iOS 重构了CSS布局引擎,修改了一些组件的实现。
去掉了 RCTImageComponent.h
去掉了 RCTShadowVirtualImage.h
去掉了 RCTVirtualImageManager.h
RCTXCAssetImageLoader.h
修改为RCTLocalAssetImageLoader.h
Layout.h
修改为 CSSLayout.h
和 CSSNodeList.h
新增加了 JS 字体属性转换相关的类 RCTFont.h
RCTConvert
中单独剥离到 RCTFont
中之前的写法:
RCT_CUSTOM_VIEW_PROPERTY(fontSize, CGFloat, RCTTextField)
{
view.font = [RCTConvert UIFont:view.font withSize:json ?: @(defaultView.font.pointSize)];
}
RCT_CUSTOM_VIEW_PROPERTY(fontWeight, NSString, __unused RCTTextField)
{
view.font = [RCTConvert UIFont:view.font withWeight:json]; // defaults to normal
}
RCT_CUSTOM_VIEW_PROPERTY(fontStyle, NSString, __unused RCTTextField)
{
view.font = [RCTConvert UIFont:view.font withStyle:json]; // defaults to normal
}
RCT_CUSTOM_VIEW_PROPERTY(fontFamily, NSString, RCTTextField)
{
view.font = [RCTConvert UIFont:view.font withFamily:json ?: defaultView.font.familyName];
}
现在的写法:
#import "RCTFont.h"
RCT_CUSTOM_VIEW_PROPERTY(fontSize, NSNumber, RCTTextField)
{
view.font = [RCTFont updateFont:view.font withSize:json ?: @(defaultView.font.pointSize)];
}
RCT_CUSTOM_VIEW_PROPERTY(fontWeight, NSString, __unused RCTTextField)
{
view.font = [RCTFont updateFont:view.font withWeight:json]; // defaults to normal
}
RCT_CUSTOM_VIEW_PROPERTY(fontStyle, NSString, __unused RCTTextField)
{
view.font = [RCTFont updateFont:view.font withStyle:json]; // defaults to normal
}
RCT_CUSTOM_VIEW_PROPERTY(fontFamily, NSString, RCTTextField)
{
view.font = [RCTFont updateFont:view.font withFamily:json ?: defaultView.font.familyName];
}
CSSNode
与 RCTShadowView
CSSNode
相关的头文件由 Layout.h
修改为 CSSLayout.h
和 CSSNodeList.h
CSSLayout.h
设置 node 中 css 属性的方法CSSNodeList.h
与 nodeList
相关的函数,包括新建、释放、获取长度、移除指定的node等等设置cssNode
代码中之前的写法:
- (void)setFrame:(CGRect)frame
{
_cssNode->style.position[CSS_LEFT] = CGRectGetMinX(frame);
_cssNode->style.position[CSS_TOP] = CGRectGetMinY(frame);
_cssNode->style.dimensions[CSS_WIDTH] = CGRectGetWidth(frame);
_cssNode->style.dimensions[CSS_HEIGHT] = CGRectGetHeight(frame);
[self dirtyLayout];
}
现在的写法
- (void)setFrame:(CGRect)frame
{
CSSNodeStyleSetPositionLeft(_cssNode, CGRectGetMinX(frame));
CSSNodeStyleSetPositionTop(_cssNode, CGRectGetMinY(frame));
CSSNodeStyleSetWidth(_cssNode, CGRectGetWidth(frame));
CSSNodeStyleSetHeight(_cssNode, CGRectGetHeight(frame));
}
ShadowView
中 node 的 measure
函数修改为在 init
方法中使用 CSSNodeSetMeasureFunc(self.cssNode, RCTMeasure);
来实现,同时新增了 RCTMeasure
函数的参数。去掉了RCTShadowView
中的fillCSSNode
方法。点击查看官方说明原来的写法
static css_dim_t RCTMeasure(void *context, float width, float height)
{
CGSize computedSize;
// cal computedSize
css_dim_t result;
result.dimensions[CSS_WIDTH] = RCTCeilPixelValue(computedSize.width);
result.dimensions[CSS_HEIGHT] = RCTCeilPixelValue(computedSize.height);
return result;
}
- (void)fillCSSNode:(css_node_t *)node
{
[super fillCSSNode:node];
node->measure = RCTMeasure;
}
现在的写法
- (instancetype)init{
self = [super init];
if (self) {
CSSNodeSetMeasureFunc(self.cssNode, RCTMeasure);
}
return self;
}
static CSSSize RCTMeasure(void *context, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode)
{
CGSize computedSize;
// cal computedSize
CSSSize result;
result.width = RCTCeilPixelValue(computedSize.width);
result.height = RCTCeilPixelValue(computedSize.height);
}
去掉了 RCTShadowView
中 [RCTShadowView dirtyLayout]
方法,使用 CSSNodeMarkDirty(self.cssNode)
。点击查看官方说明
RCTShadowView
不再使用 self.cssNode->children_count = 0
而是通过 [RCTShadowView isCSSLeaf]
来跳过子元素的 css 设置。点击查看官方说明
原来的写法
- (void)insertReactSubview:(RCTShadowView *)subview atIndex:(NSInteger)atIndex
{
[super insertReactSubview:subview atIndex:atIndex];
self.cssNode->children_count = 0;
}
- (void)removeReactSubview:(RCTShadowView *)subview
{
[super removeReactSubview:subview];
self.cssNode->children_count = 0;
}
现在的写法
- (BOOL)isCSSLeaf{
return YES;
}
在 v1.3.0
以及之前的版本中,因为RCTUIManager
中methodQueue
使用的是主线程,所有继承自 RCTViewManager
的 UI Components
中 RCT_EXPORT_METHOD
的方法默认是在主线程调用。
v1.4.0
中 RCTUIManager
的 methodQueue
使用的是 QOS_CLASS_USER_INTERACTIVE
属性的的高优先级串行队列,所以需要检查继承自 RCTViewManager
组件的 RCT_EXPORT_METHOD
的方式是否必须在主线程调用,如果需要则给组件添加下面的方法:
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
RCTScrollView
和 RCTScrollableProtocol
去掉了RCTScrollableProtocol
中的 nativeScrollDelegate
属性,改用 addScrollListener
和 removeScrollListener
来添加删除 RCTScrollView
的 UIScrollViewDelegate
。点击查看官方说明
在 v1.3.0
中,RCTBridgeModule
的子类只要实现了 init
或者 constantsToExport
方法则会在bridge
初始化时在主线程创建该对象,否则new
对象的时机是在JS调用该对象的暴露的方法。
@synthesize bridge;
- (instancetype)init{
self = [super init];
if (self) {
}
return self;
}
在 v1.4.0
中,实现了 init
或者 constantsToExport
方法的RCTBridgeModule
的子类会在bridge
初始化时在主线程创建该对象。
- (instancetype)init{
self = [super init];
if (self) {
}
return self;
}
- (NSDictionary<NSString *, id> *)constantsToExport
{
return @{@"forceTouchAvailable": @(RCTForceTouchAvailable())};
}
@synthesize bridge;
不会导致插件在一开始就初始化,因此对插件初始化时机敏感的话注意检查。
ViewManager
的继承体系,与官方保持同步QRN 之前的 ViewManger
类继承了 ReactContextBaseJavaModule
类,现在已经去除和官方保持一致
** 需要注意的是这个改动会导致 在NativeModuls
中也获取不到 这个继承自 ViewManager
的对象, ViewManager
对象也不能使用 @ReactMethod
给 JS 暴露方法 **
如果需要调用和 某UI 组件相关的方法推荐是新建一个相关联的原生API模块,具体可以询问 hongbo.chen
QReactBaseActivity
类需要在 onDestroy()
生命周期中调用释放内存资源的方法。
调用释放方法示例如下:if (mReactRootView != null) {
mReactRootView.unmountReactApplication();
mReactRootView = null;
}
ReactRootView
在 Activity
被销毁的时候需要调用 QRNActivityHelper
类中的 onDestroy()
方法释放内存资源。QReactNative
中的 createRootViewUseBaseActivityWithListener()
这种情况。
调用释放方法示例如下:qRnactivityHelper.onDestory();
TextInput
和 Toolbar
模块官方实现改动较大,有用到的业务线注意下这块ViewProps
的属性 Spacing.LEFT
和 Spacing.RIGHT
已经变成了 Spacing.START
和 Spacing.END
decomposedMatrix
方法官方最新 0.32.0
已经被移除, QRN
目前保留该方法,如果使用到的业务线请考虑升级成官方的最新方法 transform
ActivityEventListener
接口已经更改,如果有继承这个接口的需要重新修改,否则会引发崩溃。在gradle的依赖配置文件(dependencies.gradle
或者build.gradle
)中,更新qrn的版本依赖。
atomCompile 'com.qunar.spider:react:1.4.0@aar'
compile ('com.qunar.react:qunar-react-native-dependence:1.4.0-SNAPSHOT@aar'){
exclude group: 'com.facebook.fresco', module: 'imagepipeline'
exclude group: 'com.facebook.fresco', module: 'fbcore'
}