(前言)
例子的Demo在这里由于本人工 (不) 作 (会) 忙 (Android开发),因此下文中的原生组件以iOS为例,Android原生组件也会考虑在后面补上。(本组件支持npm)
由于篇幅的原因,本文不作React Native的环境搭建和如何创建项目的介绍(我没有前端开发经历哦,相信这点起步也同样难不倒你的),尚未了解的朋友可以参考以下介绍。文中的例子参考了React Native 官网的原文例子,原谅我手笨,尽管有公司前端同事的大力相助,这个小Demo依然做的磕磕碰碰,Anyway,在经过了两个晚上的不懈努力之后,我完成了预定目标,学到了不少有用的东西。对于这些我决定将自己的总结和教训分享出来,方便大家的学习,促进技术交流。
React-Native开发的相关知识了解,前端开发的基本概念入门,包括框架介绍,控件布局,样式,网络请求,更多资源参考
为什么要做原生组件的封装呢?React Native为我们提供了很多现成组件,比如ScrollView,Navigator,TextInput;但是在实际开发中,我们许多时候中会遇到React Native没有为我们备好的组件的需求,而原生项目中已经有在用的控件了,后者往往来自于第三方库或者App自带。如果能把这些控件用JS封装好,并应用于React Native项目中,在性能上要优于后者,体验也更流畅;而React Native为我们提供了这样的入口,这是非常好的。
(一些有用的接口)
//RCTGestureViewManager.m
/**
RCT_EXPORT_MODULE(): 将本组件放入模块中方便JS的导入,在原生中它就是class
RCT_EXPORT_METHOD(): 将一些方法导出,方便JS调用。这个宏的参数中包括JS方法名,方法的参数。参数支持 string,
number, bool, function等JS类型
RCT_EXPORT_VIEW_PROPERTY(): 属性导出给JS,参数为属性的名称和类型。代码如下:
*/
//export modules methods and properties
RCT_EXPORT_MODULE() //default name is RNGesutreView
RCT_EXPORT_VIEW_PROPERTY(onComplete, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(nodeScale, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(colCount, int)
RCT_EXPORT_VIEW_PROPERTY(backgroundImgName, NSString)
RCT_EXPORT_METHOD(setupNodeUI:(NSDictionary *)params) {
NSString *nodeNormalImg = params[@"nodeNormal"];
NSString *nodeErrorImg = params[@"nodeError"];
NSString *nodeSelectImg = params[@"nodeSelect"];
self.innerView.nodeErrorImgName = nodeErrorImg;
self.innerView.nodeNormalImgName = nodeNormalImg;
self.innerView.nodeSelectedImgName = nodeSelectImg;
}
为了方便UI组件和JS的交互,我们创建了RCTGestureViewManager
,作为原生组件的代理,负责将组件的属性,方法,组件类导出给JS,向JS发送事件,并接收后者的回调,方法调用。RCTGestureViewManager
需要继承于RCTViewManager
,相应的部分JS代码则是:
//RCTGestureView.ios.js
const GestureManager = NativeModules.RNGestureViewManager
const NativeGestureView = requireNativeComponent('RNGestureView', RNGestureView);
export default class RNGestureView extends Component {
_onGestureComplete(event) {
if (!this.props.onGestureComplete) {
return;
}
this.props.onGestureComplete(event.nativeEvent.completion)
}
componentDidMount() {
if (this.props.nodeThemes) {
GestureManager.setupNodeUI(this.props.nodeThemes);
}
}
static propTypes = {
nodeScale: PropTypes.number,
colCount: PropTypes.number,
onGestureComplete:PropTypes.func,
backgroundImgName:PropTypes.string,
nodeThemes: PropTypes.shape({
nodeNormal: PropTypes.string,
nodeError: PropTypes.string,
nodeSelect: PropTypes.string
})
}
render() {
return (
<NativeGestureView
{...this.props} onComplete = {this._onGestureComplete.bind(this)}/>
//(event)=>this.props.onGestureComplete(event.nativeEvent.completion)
);
}
}
其中,onComplete
是原生组件暴露出来的调用 JS 完成特定事件的方法也就是OC中的回调 block,在手势解锁完成后,调用JS中的同名方法,并把手势连接的密码作为参数传递给 JS。
另外,原生暴露出来的属性和方法在JS代码中需要相应的列出。方便JS这边的封装组件和原生进行参照比对。其中的nodeScale
等四个字段都是RCTGestureView
的同名属性。 propTypes
是RNGestureView
的类成员,负责完成以上的工作。
为了检验封装是否成功,我们进行简单的测试,测试的部分代码如下:
//index.ios.js
class gestureUnlock extends Component {
constructor() {
super()
this.state= {
textContent: 'test for callback from native module'
}
}
componentDidMount() {
GestureViewManager.setupNodeUI({
nodeNormal: 'gesture_node_normal',
nodeSelect: 'gesture_node_highlighted'
});
}
render() {
return (
<View style={styles.container}>
<GestureView style={styles.gesture} nodeScale={74} colCount={3}
backgroundImgName='Home_refresh_bg' onGestureComplete={(result)=>{
console.log('result', result);
this.setState({
textContent:result==null? 'callback failed':result
})}}/>
<Text style={styles.passcode}>
{this.state.textContent}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: 'blue',
},
gesture:{
flex: 1,
justifyContent: 'center',
// backgroundColor: 'yellow',
},
passcode: {
fontSize: 20,
textAlign: 'center',
margin: 10,
color: 'white'
},
});
运行中和手势动作结束后的效果:
componentDidMount
是声明周期方法,对React Native中的对象运行的生命周期感兴趣的朋友可以参考说明
(结束语)
在本例中,原生向JS发送事件是通过RCTBubblingEventBlock
,这是封装好的可以在OC中使用的 block。当调用时,会调用JS中的同名方法。
另外一个发送事件的方法是使用eventDispatcher
,并在JS代码中注册监听同名的事件。至于这两个方法在封装原生控件这一操作上哪个更好,我还没有得到适合的答案,希望对此熟悉的朋友可以告诉我。
在这里推荐一个不错的学习React Native的网站,收集了架构,数据流,应用状态的监听等资料介绍。
网友评论