ReactNative 知识整理

2018-05-14  本文已影响0人  Juvid

一、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模块)。

上一篇下一篇

猜你喜欢

热点阅读