美文网首页
ReactNative 知识整理

ReactNative 知识整理

作者: Juvid | 来源:发表于2018-05-14 16:31 被阅读0次

    一、ReactNative 布局

    1、指定宽高#

    最简单的给组件设定尺寸的方式就是在样式中指定固定的width和height。React Native中的尺寸都是无单位的,表示的是与设备像素密度无关的逻辑像素点。

    配合marginLeft,paddingLeft等使用

    2、弹性(Flex)宽高#

    在组件样式中使用flex可以使其在可利用的空间中动态地扩张或收缩。一般而言我们会使用flex:1来指定某个组件扩张以撑满所有剩余的空间。如果有多个并列的子组件使用了flex:1,则这些子组件会平分父容器中剩余的空间。如果这些并列的子组件的flex值不一样,则谁的值更大,谁占据剩余空间的比例就更大(即占据剩余空间的比等于并列组件间flex值的比)。

    组件能够撑满剩余空间的前提是其父容器的尺寸不为零。如果父容器既没有固定的width和height,也没有设定flex,则父容器的尺寸为零。其子组件如果使用了flex,也是无法显示的。

    3Flex Direction

    在组件的style中指定flexDirection可以决定布局的主轴。子元素是应该沿着水平轴(row)方向排列,还是沿着竖直轴(column)方向排列呢?默认值是竖直轴(column)方向。

    4Justify Content

    在组件的style中指定justifyContent可以决定其子元素沿着主轴的排列方式。子元素是应该靠近主轴的起始端还是末尾段分布呢?亦或应该均匀分布?对应的这些可选项有:flex-start、center、flex-end、space-around以及space-between。

    *如果主轴是竖直flex-start代表顶端,如果主轴是水平flex-start代表左边,其他以此类推。

    5Align Items

    在组件的style中指定alignItems可以决定其子元素沿着次轴(与主轴垂直的轴,比如若主轴方向为row,则次轴方向为column)的排列方式。子元素是应该靠近次轴的起始端还是末尾段分布呢?亦或应该均匀分布?对应的这些可选项有:flex-start、center、flex-end以及stretch。

    注意:要使stretch选项生效的话,子元素在次轴方向上不能有固定的尺寸。以下面的代码为例:只有将子元素样式中的width: 50去掉之后,alignItems: ‘stretch'才能生效。

    *次属性用于次轴生效,如果次轴是相对主轴是顶端,lex-start代表左上角;如果次轴相对主轴是左边,则lex-start代表左上角。

    二、类文件引用

    两种方法创建公共类文件

    1、先创建class  Test module.exports = Test;ES5方法)

    2、直接创建export default class Test;(ES6方法)

    其他文件引进该类 import Test from ‘./Test’(ES6)

    var MyComponent = require(‘./MyComponent’);(ES5)

    *./(当前目录)  ../(上级目录)

    三、引用原生模块或者原生UI组件

    1、引用原生模块

    OC:创建CalendarManager类

    1)简单的参数传递 ->.h文件

    #import

    #import

    #import

    @interface CalendarManager : RCTEventEmitter

    @end

    .m文件

    RCT_EXPORT_MODULE();

    RCT_EXPORT_METHOD(juEvent:(NSString *)name location:(NSString *)location)

    {

      RCTLogInfo(@"原生的 %@ at %@", name, location);

      CalendarManager *test=[[CalendarManager alloc]init];

      [test juTest];

    }

    -(void)juTest{

     NSLog(@"原生的1234");

    }

    JS:导入原生类

    import { NativeModules } from 'react-native';

    var CalendarManager = NativeModules.CalendarManager;

    CalendarManager.juEvent('Birthday Party', '4 Privet Drive, Surrey’);

    2)回调函数

    RCTReponseSenderBlock->(.m文件)

    RCT_EXPORT_METHOD(findCall:(RCTResponseSenderBlock)callback)

    {

      NSArray *events = @[@"1235",@"76543"];

      callback(@[[NSNull null], events]);

    }

    JS:文件

    CalendarManager.findCall((error, events) => {

        if (error) {

            console.error(error);

        } else {

            console.log(events);

            // this.setState({events: events});

        }

    })

    Promises->(.m文件)

    RCT_EXPORT_METHOD(executeCommand:(NSDictionary *)cmdDic

                     resolver:(RCTPromiseResolveBlock)resolve

                     rejecter:(RCTPromiseRejectBlock)reject)

    {

      NSArray *events = @[@"1235",@"76543"];

      if (resolve) {

          resolve( events);

      }

    }

    RCT_REMAP_METHOD(alertUserPromise, resolver:(RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)reject){

     reject(@"0",@"cancel",err); 

     resolver(@[@1]);

    }

    js文件

    RCT_EXPORT_METHOD(函数带参数)

    CalendarManager.executeCommand({"jute":"123456"})

        .then(

            (result) => {

                console.log(result);

            })

        .catch(

            (error) => {

            });

    RCT_REMAP_METHOD(函数不带参数)

    CalendarManager.alertUserPromise()

        .then((datas)=> {

            console.warn('data', datas);

        })

        .catch((err)=> {

            console.warn('err', err);

        });或async  _alertPromise() {

            try {

                var datas = await CalendarManager.alertUserPromise();

                console.warn('data', datas);

            } catch (e) {

                console.warn('err', e);

            }

        }

    *理解Resolve和Reject

    Resolve和Reject分量是Promise的两种状态,表示已解决和已拒绝,Resolve是正常的执行结果,而Reject会触发catch操作。

    在Object-C与之相对应的是:RCTPromiseResolveBlock和RCTPromiseRejectBlock,两个都是定义好的Object-C bridge。

    这里的then处理的是Resovle状态的结果,而catch处理的是Reject状态的结果。

    也可以使用async/await实现

    async/await是两个关键词,用来把Promises的思想融入到语言本身。使用他们就不再需要写catch这样的伪同步的代码,直接使用try/catch/return这样的关键词就可以了.

    Promises对于回调的使用很便利,尽量避免了JavaScript的回调地狱。

    *使用时需要注意多线程

    3)给Javascript发送事件

    即使没有被JavaScript调用,原生模块也可以给JavaScript发送事件通知。最好的方法是继承RCTEventEmitter,实现suppportEvents方法并调用self sendEventWithName:。

    .m文件

    - (NSArray *)supportedEvents

    {

      return @[@"EventReminder"];

    }

    - (void)calendarEventReminderReceived:(NSNotification *)notification

    {

      NSString *eventName = notification.userInfo[@"name"];

      [self sendEventWithName:@"EventReminder" body:@{@"name": eventName}];

    }

    js文件

    const subscription = calendarManagerEmitter.addListener(

      'EventReminder',

      (reminder) => console.log(reminder.name)

    );

    ...

    // 别忘了取消订阅,通常在componentWillUnmount生命周期方法中实现。

    subscription.remove();

    优化无监听处理的事件

    / 在添加第一个监听函数时触发

    -(void)startObserving { 

        hasListeners = YES;

        // Set up any upstream listeners or background tasks as necessary

    }

    // Will be called when this module's last listener is removed, or on dealloc.

    -(void)stopObserving { 

        hasListeners = NO;

        // Remove upstream listeners, stop unnecessary background tasks

    }

    2、引用原生UI组件

    OC:创建CalendarManager类

    .h文件

    #import

    #import

    @interface RNTMapManager : RCTViewManager

    @end

    .m文件

    #import "RNTMapManager.h"

    @implementation RNTMapManager

    RCT_EXPORT_MODULE()

    RCT_EXPORT_VIEW_PROPERTY(pitchEnabled, BOOL)

    - (UIView *)view

    {

      return [[MKMapView alloc] init];

    }

    @end

    JS:首先创建MapView.js(以下两种方法皆可,第一种无事件)

    //第一种

    var { requireNativeComponent } = require('react-native');

    module.exports = requireNativeComponent('RNTMap', null);

    //第二种

    import React, { Component, PropTypes } from 'react';

    import { requireNativeComponent } from 'react-native';

    var RNTMap = requireNativeComponent('RNTMap', MapView);

    export default class MapView extends Component {

        static propTypes = {

            /**

             *当这个属性被设置为true,并且地图上绑定了一个有效的可视区域的情况下,

             *可以通过捏放操作来改变摄像头的偏转角度。

             *当这个属性被设置成false时,摄像头的角度会被忽略,地图会一直显示为俯视状态。

             */

            pitchEnabled: PropTypes.bool,

        };

        render() {

            return ;

        }

    }

    导入原生类

    import MapView from ‘./MapView'

    使用:

    四、图片

    1、静态图片资源(不设置宽度会根据图片尺寸自适应)

    相应js文件下

    ├── button.js

    └── img

        ├── check@2x.png

        └── check@3x.png

    2、使用混合App的图片资源(需要知道宽高否则不显示)

    (通过Xcode的asset类目或者Android的drawable文件夹打包)

    3、网络图片(需要知道宽高否则不显示)

    4、网络图片缓存(http://blog.csdn.net/sinat_17775997/article/details/65442050http://blog.csdn.net/sinat_17775997/article/details/65442054

    安装依赖方法:

    1、 npm  install react-native-img-cache --save

    2、npm install --save react-native-fetch-blob

    react-native-img-cache该库使用是需要依赖react-native-fetch-blob

    3、链接blob  react-native link

    四、导航栏

    1iOS导航栏(NavigatorIOS

    render() {

                    initialRoute={{

                        component: FirstPageComponent,

                        title: 'title哈哈',

                    }}

                    style={{flex: 1}}

                />

            )

        }

    2、通用导航栏

    1Old方法( Navigator

    render() {

            let defaultName = 'firstPageName';

            let defaultComponent = FirstPageComponent;

            return (

                    styles = {styles.container}

                    initialRoute = {{name: defaultName,component : defaultComponent}}

                    configureScene = {

                        (route)=>{

                            return Navigator.SceneConfigs.FloatFromRight

                        }

                    }

                    renderScene = {(route,navigator)=>{

                        let Component = route.component;

                        return

                    }}

                />

                 )

        }

    2)新方法 React Navigation

    首先安装 npm install --save react-navigation

    export default class RnWidget extends React.Component {

     static navigationOptions = {

            title: 'Welcome',

        };

        render() {

            return Hello, Navigation!;

        }

    }

    const SimpleApp = StackNavigator({

      Home: { screen: RnWidget },

    });

    AppRegistry.registerComponent('testRN', () => SimpleApp);

    ReactNative发布(打包方式)

    1、离线包

    1)通过代码直接xcode自动生成

    在App Store上发布应用首先需要编译一个“发布版本”(release)的应用。具体的做法是在Xcode中选择Product -> Scheme -> Edit Scheme (cmd + <),然后选择Run选项卡,将Build Configuration设置为release。 Release版本的应用会自动禁用开发者菜单,同时也会将js文件和静态图片打包压缩后内置到包中,这样应用可以在本地读取而无需访问开发服务器(同时这样一来你也无法再调试,需要调试请将Buiid Configuration再改为debug)。

    *次方法入口函数只能在文件(index.ios.js)

    2)通过命名打相应的发布文件

    rm -rf bundle && mkdir bundle && react-native bundle --entry-file index.ios.js --bundle-output ./bundle/index.ios.jsbundle--platform ios --assets-dest ./bundle --dev false && open bundle

    *如有多个入口函数需更改相应的js(index.ios.js)文件和打包后文件名(index.ios.jsbundle)

    2、线上包(需提供IP地址)

    更改客户端IP:[RCTBundleURLProvider sharedSettings].jsLocation=@“192.168.8.18";

    注意:以上方法若有多个独立模块需注册多个入口函数

    AppRegistry.registerComponent('testRN', () => testRN);

    ‘testRN'入口函数名 testRN类名;

    客户端需要改相应代码

    ios方法:

    1)[[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:@“index.ios”]

    *jsBundleURLForBundleRoot:@“index.ios” 动态文件(IP地址)使用不同文件 

    fallbackResource:@“index.ios”离线包模块;

     2)RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation

                                                          moduleName:@“testRN”

                                                   initialProperties:nil

                                                       launchOptions:nil];

     moduleName:@“testRN”(模块名)

    ReactNative默认属性与方法

    1、默认属性defaultPropspropTypes

    class Video extends React.Component {

        static defaultProps = {

            autoPlay: false,

            maxLoops: 10,

        };  // 注意这里有分号

        static propTypes = {

            autoPlay: React.PropTypes.bool.isRequired,

            maxLoops: React.PropTypes.number.isRequired,

            posterFrameSrc: React.PropTypes.string.isRequired,

            videoSrc: React.PropTypes.string.isRequired,

        };  // 注意这里有分号

        render() {

            return (

               

            );

        }// 注意这里既没有分号也没有逗号

    }

    2、初始化STATE

    //ES5 

    var Video = React.createClass({

        getInitialState:function() {

            return {

                loopsRemaining:this.props.maxLoops,

            };

        },

    })

    //ES6

    class Video extends React.Component {

        state = {

            loopsRemaining:this.props.maxLoops,

        }

    }

    3、通过构造函数初始化constructor

    class Video extends React.Component {

        constructor(props){

            super(props);

            this.state = {

                loopsRemaining:this.props.maxLoops,

            };

        }

    }

    state props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据。

    把方法作为回调提供

    很多习惯于ES6的用户反而不理解在ES5下可以这么做:

    //ES5

    var PostInfo = React.createClass({

        handleOptionsButtonClick:function(e) {

            // Here, 'this' refers to the component instance.

            this.setState({showOptionsModal: true});

        },

        render:function(){

            return (

               

                    {this.props.label}

            )

        },

    });

    在ES5下,React.createClass会把所有的方法都bind一遍,这样可以提交到任意的地方作为回调函数,而this不会变化。但官方现在逐步认为这反而是不标准、不易理解的。

    在ES6下,你需要通过bind来绑定this引用,或者使用箭头函数(它会绑定当前scope的this引用)来调用

    //ES6

    class PostInfo extends React.Component

    {

        handleOptionsButtonClick(e){

            this.setState({showOptionsModal: true});

        }

        render(){

            return (

                    onPress={this.handleOptionsButtonClick.bind(this)}

                    onPress={e=>this.handleOptionsButtonClick(e)}

                    >

                    {this.props.label}

            )

        },

    }

    箭头函数

    箭头函数实际上是在这里定义了一个临时的函数,箭头函数的箭头=>之前是一个空括号、单个的参数名、或用括号括起的多个参数名,而箭头之后可以是一个表达式(作为函数的返回值),或者是用花括号括起的函数体(需要自行通过return来返回值,否则返回的是undefined)。

    // 箭头函数的例子

    ()=>1

    v=>v+1

    (a,b)=>a+b

    ()=>{

        alert("foo");

    }

    e=>{

        if (e == 0){

            return 0;

        }

        return 1000/e;

    }

    需要注意的是,不论是bind还是箭头函数,每次被执行都返回的是一个新的函数引用,因此如果你还需要函数的引用去做一些别的事情(譬如卸载监听器),那么你必须自己保存这个引用

    // 错误的做法

    class PauseMenu extends React.Component{

        componentWillMount(){

            AppStateIOS.addEventListener('change', this.onAppPaused.bind(this));

        }

        componentDidUnmount(){

            AppStateIOS.removeEventListener('change', this.onAppPaused.bind(this));

        }

        onAppPaused(event){

        }

    }

    // 正确的做法

    class PauseMenu extends React.Component{

        constructor(props){

            super(props);

            this._onAppPaused = this.onAppPaused.bind(this);

        }

        componentWillMount(){

            AppStateIOS.addEventListener('change', this._onAppPaused);

        }

        componentDidUnmount(){

            AppStateIOS.removeEventListener('change', this._onAppPaused);

        }

        onAppPaused(event){

        }

    }

    这个帖子中我们还学习到一种新的做法:

    // 正确的做法

    class PauseMenu extends React.Component{

        componentWillMount(){

            AppStateIOS.addEventListener('change', this.onAppPaused);

        }

        componentDidUnmount(){

            AppStateIOS.removeEventListener('change', this.onAppPaused);

        }

        onAppPaused = (event) => {

            //把方法直接作为一个arrow function的属性来定义,初始化的时候就绑定好了this指针

        }

    }

    网络请求

     getDataFromFetchs() {

            fetch('http://gank.io/api/search/query/listview/category/福利/count/10/page/1',{

            })//请求地址

            .then((response) => response.json())//取数据

            .then((responseJson) => {//处理数据

                //通过setState()方法重新渲染界面

                console.log("返回",responseJson.results);

            })

            .catch((error) => {

                console.warn('失败',error);

            }).done();

        }

        //三方网络请求

        httpRequest(){

            var request = new XMLHttpRequest();

            request.onreadystatechange = (e) => {

                if (request.readyState !== 4) {

                    return;

                }

                if (request.status === 200) {

                    console.log('成功', request.responseText);

                } else {

                    console.warn('error');

                }

            };

            request.open('GET', 'http://gank.io/api/search/query/listview/category/福利/count/10/page/1');

            request.send();

        }

    额外学习知识

    1CSS相关属性,控制View相关属性。

    backgroundColorcolor。(-全部用首字母大写表示);

    2React JSXStateProps

    3JavaScript:至少从ES5开始学习;

    4OCAndroid相关知识(可在reactnative中嵌套原生、或者原生中嵌套相关reactnative模块)。

    相关文章

      网友评论

          本文标题:ReactNative 知识整理

          本文链接:https://www.haomeiwen.com/subject/todjdftx.html