技术文章

React Native 01:学习笔记

2016-11-08  本文已影响529人  Null先森的内存地址

Learn once, write anywhere.

React Native是Facebook弄出来的一款用来开发真正原生、可渲染iOS和 Android移动应用的JavaScrit框架。它其实就是基于Javascript和React的基础上来开发原生应用。并且一份代码可以同时支持iOS和Android,也就是能够做到真正意义上的跨平台。React Native和React的区别在于,React是将浏览器作为渲染平台的,RN则是将移动设备作为渲染平台。代码上的体现就是用web思想去写原生的代码。所以RN具有Native的一个最大的优势,它可以做到热更新,也就是不需要发包就可以改点东西。


IDE

Nuclide:Nuclide 是 Facebook 推出的一套基于 Atom 的开发工具集。提供自动完成和JavaScript类型检查,内建React开发支持,并支持 Facebook 最新的 React Native 库,支持 Facebook 的 Flow JavaScript 类型检查器。
PS:推荐使用Atom配合Nuclide来搞RN开发。反正是好用。


HelloWorld

DraggedImage.png DraggedImage-1.png
这个文件主要是用来配置一些信息的,添加一些依赖库的。然后需要利用npm执行npm install命令来安装模块到node_modules目录。
PS:npm是一个Node的模块管理器。我们只需要一行命令就能将一些开源的模块给搞下来。感觉和cocoapods功能有点像。_
DraggedImage-2.png
上面那串等于从index.ios.js文件中创建了一个名字叫AwesomeProject的view,并且添加到window的rootVC的view上。
DraggedImage-3.png

ES5&ES6

不管是ES5还是ES6都只是JS的一个规范,RN中并没有强制规定你要用哪个。但是RN官方还有很多大神们都建议我们直接入手ES6。但是问题就来了,你从开源网站上clone下来的代码就有的人用ES5,有的人用ES6了。所有知道这两个的区别是有必要的。不然你就会看到如下类似的红色页面:

5478D0A5F1183C3D2EDEB67CCCBB36DE.jpg.png

语法

//1.定义class
class Point { 
  constructor(x, y) {// 构造函数
     this.x = x;
     this.y = y;
   } 
  toString() { 
    return '(' + this.x + ', ' + this.y + ')'; 
   }
}
// 2.创建实例
var a = new Point();
a.toString();

上面定义了一个Point类,有2个属性(x和y),一个构造方法(constructor)还有一个方法(toString)。
一个class对应一个构造函数。当你使用new创建一个该类的实例的时候,它会调用该构造函数。同样一个class可以生成个多个的实例对象,但是所有的实例对象都共有这一个class原型。

class Point3D extends Point { 
   constructor(x, y, z) {    
     super(x,y);
     this.z = z;
    } 
   toString() { 
    return '(' + this.x + ', ' + this.y + ',' + this.z + ')'; 
    }
 }

上面Point3D类继承与Point,并且在构造函数里利用suepr()方法调用了父类的构造函数。
JS中的继承,子类可以具有父类所有属性和方法。但是在子类的构造函数里,必须要调用super(),这样才能在子类中使用this关键字。

1.回调函数
 //定义
 StorageUtil.load = function(key,successCallback,errorCallback){
    storage.load({
      key: key,
    }).then(ret => {
      successCallback(ret);
    }).catch(err => {
      errorCallback(err);
    })  
 }
 //使用
    StorageUtil.load(
      APPID_KEY,
      (data)=> {
        console.log('data'+data);
      },
      (err)=> {
        console.log('err'+err);
      }
    );
 2.Promise
 //定义
 StorageUtil.load2 = function(key){
    return new Promise((resolve, reject) =>{
    storage.load({
      key: key,
    }).then(ret => {
      resolve(ret);
    }).catch(err => {
      reject(err);
    })  
   });
 }
 //使用
    StorageUtil.load2(APPID_KEY).then((data)=>{
        console.log('[load2]---'+data);
    })
    .catch((error)=>{
        console.log('err'+err);
    });

//1.导入
 var React = require("react");
 var {
       Component, 
       PropTypes
 } = React;
 var ReactNative = require("react-native");
 var {
       Image,
       Text,
 } = ReactNative; 
 //2.导出
 var MyComponent = React.createClass({ 
      ...
 });
 module.exports = MyComponent;
 //3.引用
 var MyComponent = require('./MyComponent');

ES6

//1.导入
 import React, { 
      Component, 
      PropTypes,
 } from 'react';
 import { 
      Image, 
      Text,
 } from 'react-native'
 //2.导出
 export default class MyComponent extends Component{ 
      ...
 }
 //3.引用
 import MyComponent from './MyComponent';
//1.组件
 var Photo = React.createClass({ 
        render: function() {
               return ( 
                  <Image source={this.props.source} /> 
              ); 
        },
 });
 //2.组件方法
 test: function(){ 
 },
 //3.Props
 getDefaultProps: function() { //默认属性
      return { 
            imageId: 0, 
      }; 
 },
 propTypes: {  //属性类型
      imageId: React.PropTypes.number.isRequired, 
 },
 //4.state
 getInitialState: function() { 
      return { 
          iconName:'', 
      }; 
 },
 //5.bind()
 在ES5下,React.createClass会把所有的方法都bind一遍,
 例如:给按钮绑定点击方法的时候不需要bind(this)
  render: function(){
        return (
            <TouchableHighlight onPress={this.onClick}>
            </TouchableHighlight>
        )
    },

ES6

//1.定义组件
 class Photo extends React.Component { 
    render() { 
        return ( 
            <Image source={this.props.source} /> 
        ); 
    }
 }
 //2.组件方法
 test(){ 
 },
 //3.Props
 static defaultProps = { //默认属性
    imageId: 0, 
 }; 
 static propTypes = { //属性类型
     imageId: React.PropTypes.number.isRequired,
 }; 
 //4.State
 constructor(props){
      super(props); 
      this.state = { 
              iconName: '', 
      };
  }
 //5.bind()
 在ES6下,你需要通过bind来绑定this引用,或者使用箭头函数(它会绑定当前scope的this引用)来调用
 例如:
 render(){ 
      return (
            <TouchableHighlight  onPress={this.onClick.bind(this)} >
            </TouchableHighlight> 
            <TouchableHighlight  onPress={()=>this.onClick()}>
            </TouchableHighlight> 
        ) 
 }, 

React

felx布局

1.水平居中(alignItems:’center’)
2.垂直居中(justifyContent:’center’)
3.水平垂直居中(alignItems:’center’, justifyContent:’center’)
4.flexDirection(row, column)
栗如:

      render() {
         return (
          <View style = {{flex:1,flexDirection:'row'}}>
             <View style = {{flex:1,backgroundColor:'red'}}>
             </View>
             <View style = {{flex:2,backgroundColor:'blue'}}>
             </View>
             <View style = {{flex:1,backgroundColor:'yellow'}}>
             </View>
           </View>
           );
       }
Simulator Screen Shot 2016年10月27日 上午11.42.51.png
    render() {
         return (
           <View style = {{flex:1,flexDirection:'column'}}>
             <View style = {{flex:1,backgroundColor:'red'}}>
             </View>
             <View style = {{flex:2,backgroundColor:'blue'}}>
             </View>
             <View style = {{flex:1,backgroundColor:'yellow'}}>
             </View>
           </View>
           );
       }
Simulator Screen Shot 2016年10月27日 上午11.35.11.png
PS:利用这个就可以完成一些复杂的网格布局。
    例如这种多层嵌套的布局:
1902568823-56fde33a311f0_articlex-1.png

5.图片(resizeMode)

 <Text style={styles.welcome}> 100px height with resizeMode contain </Text>
   <View style={[{flex: 1, backgroundColor: '#fe0000'}]}>
       <Image style={{flex: 1, height: 100, resizeMode: Image.resizeMode.contain}} source={{uri: 'http://gtms03.alicdn.com/tps/i3/TB1Kcs5GXXXXXbMXVXXutsrNFXX-608-370.png'}} />
    </View>
DraggedImage-4.png
 <Text style={styles.welcome}> 100px height with resizeMode cover </Text>
   <View style={[{flex: 1, backgroundColor: '#fe0000'}]}>
       <Image style={{flex: 1, height: 100, resizeMode: Image.resizeMode.cover}} source={{uri: 'http://gtms03.alicdn.com/tps/i3/TB1Kcs5GXXXXXbMXVXXutsrNFXX-608-370.png'}} />
   </View>
DraggedImage-5-1.png
<Text style={styles.welcome}> 100px height with resizeMode stretch </Text>
   <View style={[{flex: 1, backgroundColor: '#fe0000'}]}>
       <Image style={{flex: 1, height: 100, resizeMode: Image.resizeMode.stretch}} source={{uri: 'http://gtms03.alicdn.com/tps/i3/TB1Kcs5GXXXXXbMXVXXutsrNFXX-608-370.png'}} />
   </View>
DraggedImage-6.png

组件的生命周期

关于RN中组件的生命周期很类似于iOS中VC中的View的生命周期。一开始在调试的时候会发现组件的render方法调用的非常频繁。所以知道组件的生命周期是很有必要。这样我们可以在适当的方法里面完成相应的事情,比如在componentDidMount添加通知,componentWillUnmount中移除通知等等。

DraggedImage-7.png

getDefaultProps:组件实例创建前调用,多个实例间共享引用。注意:如果父组件传递过来的Props和你在该函数中定义的Props的key一样,将会被覆盖。
getInitalState:组件示例创建的时候调用的第一个函数。主要用于初始化state。注意:为了在使用中不出现空值,建议初始化state的时候尽可能给每一个可能用到的值都赋一个初始值。
componentWillMount:在render前,getInitalState之后调用。仅调用一次,可以用于改变state操作。
render:组件渲染函数,会返回一个Virtual DOM,只允许返回一个最外层容器组件。render函数尽量保持纯净,只渲染组件,不修改状态,不执行副操作(比如计时器)。
componentDidMount:在render渲染之后,React会根据Virtual DOM来生成真实DOM,生成完毕后会调用该函数。在浏览器端(React),我们可以通过this.getDOMNode()来拿到相应的DOM节点。然而我们在RN中并用不到,在RN中主要在该函数中执行网络请求,定时器开启等相关操作
** componentWillReceiveProps(nextProps) **:props改变(父容器来更改或是redux),将会调用该函数。新的props将会作为参数传递进来,老的props可以根据this.props来获取。我们可以在该函数中对state作一些处理。注意:在该函数中更新state不会引起二次渲染。
** boolean shouldComponentUpdate(object nextProps, object nextState) **:该函数传递过来两个参数,新的state和新的props。state和props的改变都会调到该函数。该函数主要对传递过来的nextProps和nextState作判断。如果返回true则重新渲染,如果返回false则不重新渲染。在某些特定条件下,我们可以根据传递过来的props和state来选择更新或者不更新,从而提高效率。
** componentWillUpdate(object nextProps, object nextState) **:与componentWillMount方法类似,组件上会接收到新的props或者state渲染之前,调用该方法。但是不可以在该方法中更新state和props。
** componentDidUpdate(object prevProps,object prevState) **:和初始化时期的componentDidMount类似,在render之后,真实DOM生成之后调用该函数。传递过来的是当前的props和state。在该函数中同样可以使用this.getDOMNode()来拿到相应的DOM节点。如果你需要在运行中执行某些副操作,请在该函数中完成。
componentWillUnmount:组件DOM中移除的时候调用。在这里进行一些相关的销毁操作,比如定时器,监听等等。


React Native

存储

RN官方有封装一个AsyncStorage组件,采用key-value的形式用来处理一些数据存储操作。
PS:更推荐使用react-native-storage这个开源组件,它是对AsyncStorage的一层封装,并且他每个方法都是会返回一个Promise对象。使用起来更加方便。

import React, { Component } from 'react';
 import {
   AsyncStorage,
 } from 'react-native';
 import Storage from 'react-native-storage'; 
 global.USER = { 
   admin_id: '',
   user_name: '',
   admin_name: '',
   expiry: 0,
   auth_token: '' 
 };
 global.APPID = 0;
 
 
 
 global.USER_KEY = 'USERKEY';
 global.APPID_KEY = 'APPIDKEY';
 global.SHOW_ERROR = '0'; //0表示显示全部,1显示异常
 
 var StorageUtil = {};
 
 var storage = new Storage({
   // 最大容量,默认值1000条数据循环存储
   size: 1000,
 
   // 存储引擎:对于RN使用AsyncStorage,对于web使用window.localStorage
  // 如果不指定则数据只会保存在内存中,重启后即丢失
   storageBackend: AsyncStorage,
 
   // 数据过期时间,默认一整天(1000 * 3600 * 24 毫秒),设为null则永不过期
   defaultExpires: null,
 
   // 读写时在内存中缓存数据。默认启用。
   enableCache: true,
 
  // 如果storage中没有相应数据,或数据已过期,
   // 则会调用相应的sync同步方法,无缝返回最新数据。
   sync: {
   }
 });
 
 
 
 StorageUtil.init = function(callback){
     storage.getBatchData([
         { key: USER_KEY },
         { key: APPID_KEY }
     ]).then(results => {
      console.log('[results]--'+ results);
      USER = results[0];
       APPID = results[1];
      console.log('[init:USER]--'+ USER);
      console.log('[init:APPID]--'+ APPID);
      callback(true);
    }).catch(err => {
      console.log(err);
      callback(false);
    });  
 },
StorageUtil.save = function(key,data){
  // 使用key来保存数据。这些数据一般是全局独有的,常常需要调用的。
  // 除非你手动移除,这些数据会被永久保存,而且默认不会过期。
  storage.save({
    key: key,  //注意:请不要在key中使用_下划线符号!
    rawData: data,
     // 如果不指定过期时间,则会使用defaultExpires参数
     // 如果设为null,则永不过期
     // expires: 1000 * 36000

   });
   if (key == USER_KEY) {
     USER = data;
   }else if (key == APPID_KEY) {
     APPID = data;
   }
   console.log('[SAVE:USER]--'+ USER.user_name);
   console.log('[SAVE:APPID]--'+ APPID);
 },
 StorageUtil.load = function(key,successCallback,errorCallback){
   // 读取
   storage.load({
     key: key,
   }).then(ret => {
     successCallback(ret);
     //如果找到数据,则在then方法中返回
   }).catch(err => {
     //如果没有找到数据且没有同步方法,
    //或者有其他异常,则在catch中返回
     errorCallback(err);
   })  
 },
 
 module.exports = StorageUtil;

网络

RN的网络组件封装的非常好用。直接上代码吧。

 getMoviesFromApiAsync() {
     return fetch('http://facebook.github.io/react-native/movies.json')
       .then((response) => response.json())
       .then((responseJson) => {
         return responseJson.movies;
       })
       .catch((error) => {
         console.error(error);
       });
   }

桥接

例如:

OC代码
RCT_EXPORT_METHOD(getVersion:(RCTResponseSenderBlock)callback)
{
  NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
  callback(@[version]);
}
JS代码
ASHUtilManager.getVersion((version)=> {
            this.setState({
                versionText : 'V' + version,
            });
        });

PS:在RCT_EXPORT_METHOD宏括起来的方法都是异步执行的,如果方法里涉及到UI的操作,需要放到主线程里执行。RN中的桥接方式比JSBridge好用多了。


React Native踩坑汇总

附录:
学习资料
[http://reactnative.cn/]
[https://github.com/reactnativecn/react-native-guide]
[http://www.jianshu.com/p/7c43af022758]

上一篇下一篇

猜你喜欢

热点阅读