React-Native

React-Native_第1张图片

RN作为H5的下一代跨平台方案,风头正劲。用UC震惊部的话来说:没有研究过RN,都不好意思说自己做过客户端。现在还不了解学习React-Native,你就要快老了!
好了,抛开标题的噱头。写下这篇文章的主要目的,还是作为自己的一个学习记录与汇总,同时对RN的踩坑与学习的经验进行分享,总结ES 6的常用语法,页面的生命周期与相关的一些学习资料。让其他客户端开发者能快速的对React-Native进行接入与学习。文章包含以下内容:

  • 为什么使用RN
  • RN的简单用例
  • ES 6语法
  • RN的生命周期
  • 坑点汇总

为什么选择RN


其实在一开始我也曾思考过这个问题。既然现在已经有成熟的H5方案了,为什么还要选择RN?
最根源的想法还是来自于H5的痛点。页面的交互性差,数据缓存,回传回调困难。在使用过程中也难以满足用户所需的多元化功能与快速响应的速度。面对这种情况,以往我们只能委屈求全,摘掉一些耗时长,操作复杂的功能。亦或是舍弃使用H5,最后改由客户端实现,已达到最好的用户体验。其次,也是作为一个技术的自我学习心态的驱使。React-Native作为编写Hybrid APP的新思路,不管项目需要接入与否,我们都有必要去学习与了解其中的原理和使用。即作为一个知识储备,也能更好的学习里面的新思想。作为一个客户端开发者,Redux的设计给我的思考与收获也是很大的。

总结下来,RN具有以下优势:

  1. 运行性能更好,调用原生组件。
  2. 采用了css,flexbox的布局模式,方便开发。
  3. 比起 Hybird 扩展性更强,自由度更高,交互体验更好。
  4. 支持应用热更新与远程调试。
  5. 展示复杂的高阶动画,能通过原生平台编写,更少卡顿。
  6. 大部分代码都可跨平台,易于维护。
  7. 和weex相比,社区环境更优秀,也有更多成熟的应用使用RN方案。

RN的简单用例


以下是RN最简单的样例,分为三部分组成:
1.对象、组件的导入
2.样式的声明
3.组件的使用与样式引入
4.注册入口类。将与Native中代码的设置对应(这里为'rn')。

// 1
import React, { Component } from 'react';
import {
    AppRegistry,
    Text,
    Button,
    View,
    TouchableHighlight,
    Image,
        StyleSheet,
} from 'react-native';

// 2
const style = StyleSheet.create({
    centerSelf:{
        alignSelf: 'center',
    },
    centerJustify:{
        justifyContent: 'center',
    },
    centerItems:{
        alignItems: 'center',
    },
    red:{
        backgroundColor: 'red',
    },
});

// 3
class HelloWorldApp extends Component {
  componentDidMount(){ // 页面加载后
    // do something...
  }
  render() {
    return (
      
            Hello , !
      
    );
  }
}

// 4 注意,这里用引号括起来的'rn'必须和你init创建的项目名一致
AppRegistry.registerComponent('rn', () => HelloWorldApp);

可以看出其实RN的组件与样式的使用与H5的标签的使用非常相似。只要在给style传入样式,就可以实现布局了。上述代码的效果也很简单,只有 ViewText 两个组件。
外层 View 大小随外层大小变化并填充满(flex:1)。同时 View 的背景为红色(backgroundColor: 'red')。他的子组件将会水平居中(alignItems: 'center')与垂直居中(justifyContent: 'center')。严格说来应该是主轴与次轴居中,RN中组件可以设置竖直或水平方向为主轴。
内层为 Text 组件内容显示为 粗体黄色Hello , !

从上不难看出RN的样式其实就是传入配置json。使其使用起来和H5类似。因此,当多个样式同时使用时需要用数组传入。

style = {style.red} // 传入样式对象
style = {{fontSize: 20}} // 编写传入对象
style = {[style.red, style.centerItems]} // 传入多个样式则通过数组配置

RN的页面生命周期也与客户端类似,有各种加载与渲染的过程,如// 3 中的componentDidMount,与iOS的 ViewDidLoad 用法类似。下面就让我们来认识一下React-Native组件的生命周期。

RN 的组件生命周期


React-Native_第2张图片
引用地址:https://race604.com/react-native-component-lifecycle/

主要分为 Mounting(加载), Updating(更新), Unmounting(卸载)过程。Mounting操作在组件实例化的时候将被调用,Updating会在组件 props 或 state 变化的时候调用,而Unmounting则负责页面销毁。

Mounting

组件实例化时以下函数会被调用或插入 Dom 树。加载过程如上述流程图的最上侧框图。

  • constructor()
  • componentWillMount()
  • render()
  • componentDidMount()
Updating

一般当属性或组件状态变化的时候会触发,以下方法会在组件重新渲染的时候调用。更新调用链如上述流程图左下侧框图。

  • componentWillReceiveProps()
  • shouldComponentUpdate()
  • componentWillUpdate()
  • render()
  • componentDidUpdate()
Unmounting

在组件 Dom 树删除时调用。卸载调用链如上述流程图右下侧框图。

  • componentWillUnmount()

ES6 常用语法


定义组件

ES 6与ES 5定义组件的代码有很大变化,ES 6终于有了类的声明,也让我们看顺眼多了。为了代码的维护和使用,已经不再建议使用ES 5的语法了。

// ES5 的用法,不建议
var View  = React.createClass({
    render(){
      // 输出变量
      return (Hello,{this.props.name}!);
    }
});

// ES6
class View extends React.Component{
    render(){
      //  跨组件传值
      return ();
    }
}

属性初始化与校验

ES 6类属性的初始化建议在构造函数中constructor实现。其中也提供了一个属性propTypes,用以校验某个变量是否必要。使用如下:

class MyView extends React.Component{
    constructor(props) {
        super(props);
        this.state = {
          color: props.initialColor
        };
    }

   //  默认值(组件不传递值过来的时候)
   static defaultProps = {
       name: "bilibili",
       nick: "2333",
   }

   // propTypes用于验证转入的props,当向 props 传入无效数据时,JavaScript 控制台会抛出警告
   static propTypes = {
      name: React.PropTypes.string.isRequired,
      nick: React.PropTypes.number.isRequired,
   }

   state = {
      city: this.props.city,
      index:this.props.index,
   }
}

导入与导出

ES 6 另一个让我觉得便利的地方就是,新的导出方式。不再像以前一样,需要用类似module.exports={xxx}这样一点也不美观的方式导出。并且别名与通配符的使用也很好的防止了变量的冲突与导出。

//ES5
//接收对象
var o = require('../xx/xx.js'); // 模块导入
module.exports={                // 模块导出
    add:add,
    sub:sub
}

//ES6
// 按照变量名导入
import {* as all , userInfo,userToken as uToken,userXXX} form 'test.js';
// 默认导入
import xxx form 'test.js';       //  导入用 default 修饰的默认对象
export {
  info as userInfo,              // 别名
  token as userToken,
  xxx as userXXX 
};
export default { abcde };        // 默认导出

块级变量let

在这里提出的主要原因是他和我们客户端常见所理解的let不大一样,并不是代表常量的意思(swift,kotelin)。而是代表块级变量(js里常量是const),生命周期只在代码块里有效。虽然块级变量在很多语言中都已很常见,但原js中并没有提供块级变量的使用。而var是全局可用的。这里提出避免误用。

// var 容易造成的问题
var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10,如果把 i 声明为 let,则输出为6

其他

以下是一些曾令我疑惑的语法,不一定属于ES 6的新特性。但同属js的语法,因此放在ES 6常见特性的最后。

// 重点在于 state=...this.props.route,相当于把所有命名一致的变量传入



// 类似的
const {name,type}  = this.items;
//等价于下面
const name = this.items.name;
const name = this.items.type;

坑点汇总


坑点的汇总主要分为两各部分,框架踩坑与语言特性踩坑。

框架踩坑

1.接入坑点

因为RN的迭代速度有点快,官方文档往往更不上代码的变动速度。Stack Overflow里面相对的解决方案也比较少,且适用性随版本变化。因此,很多坑不能在网络资源中获得答案,而是要在GitHub里的issue,在对应问题中一般都能找到前人的填坑记录。

其中,最常见的问题便是 not find!。但在不同版本中,但原可能不同。
0.45版本 RN 需要引入 BatchedBridge,不然运行会出现类似报错。而虽然 0.40版本下也会出现这个问题,但原因是 CocoaPod 需要升级到最新版,把1.1版本升到1.2.1就可以解决了。(其他版本未测试过)

    #根据实际路径修改下面的`:path`
    pod 'React', :path => '../node_modules/react-native', :subspecs => [
      'Core',
      'DevSupport', # 如果RN版本 >= 0.43,则需要加入此行才能开启开发者菜单
      'RCTText',
      'RCTNetwork',
      'RCTWebSocket', 
      'BatchedBridge',   # 0.45 需要添加这行
      'RCTImage',
    ]
    
    # 如果你的RN版本 >= 0.42.0,请加入下面这行
    pod "Yoga", :path => "../node_modules/react-native/ReactCommon/yoga"

2.第三方库使用

类似的,在使用第三方库的时候,第三方库不一定会随着RN的跟新做出变化。

  • React-Native link 这个方便的命令有时候用起来就会莫名的恼火。可以的话,第三方库还是通过 CocoaPod 手动引入的好。React-Native link 容易遇到的问题一般是找不到 not find!。 遇到这类问题,一些文章会建议把第三方尖括号引用改成引号"React/xxx.h"。但这样改动太大,且不便于操作。

  • 在我研究对比RN0.390.40的配置区别后,发现主要是xcode 项目配置Alaways Search User Paths导致的。因此,在第三方库的Header Search Paths加入CocoaPod的Pod目录Public里的React文件夹作为搜索路径就好了(0.45Development文件夹里)。同时需要把第三方库的Alaways Search User Paths设成 YES 。


语言特性踩坑

1.没有宏定义

全局变量无法当做宏定义使用,没有预编译。全局变量需要运行后才有值,无法在 import 里面当路径宏使用。

2.引入的资源必须用静态路径

Require 图片资源时必须为静态路径,因为require是在编译时期执行,而非运行时期执行。这个动作会发生在运行之前。如果你使用了变量,打包的时候变量并不会有值。RN会给你报错。

3.编译检错

只有语法补全的插件,即使语法错误,运行后才能得知运行结果。
例如:
if(...) A(); else (error code)...; 的代码。测试时,如果不走else的判断,连语法错误都不会检查。如果是夹杂多个的判断,进行语法检错也是需要全部走完。

4.弱类型语言要注意语境

js为弱类型语言且空类型多(如下方第5点所示)。js 判断时会出现强类型语言考虑之外的情况,编写的代码在类型转换的思考上要更为周全。也会出现一些感官上觉得奇怪的代码,如下所示:

return !!b; // 两次强转,保证变量返回是 Boolean 类型`
return a==1?a:0 // 没有写成a==1?1:0,可能业务上结果会返回字符串和数字类型,不能确定

5.类型较多对比时容易弄混

容易弄混的有0、-0、null、""、false、undefined或者NaN。
其中:

  • undefined:未定义或未赋值的变量
  • null :特殊的object类型,为空对象,和swift 的拆包的空盒模型类似。
  • NaN:特殊的number,类似盒子模型拆盒后为空,而不为0。

以下为容易混淆的对比点:


几个相等的类型(null 不等于 false)

React-Native_第3张图片
undefined 与 Nan 与 boolean 比较都是false

6.this的作用域问题

this的指向场景会根据使用情况变化。而对于不熟悉语言的客户端开发者来说,会经常对指向对象错误的操作。this的指向场景分别四类:

  • 有对象就指向调用对象
  • 没调用对象就指向全局对象
  • 用new构造就指向新对象
  • 通过 apply 或 call 或 bind 来改变 this 的所指。

这个问题的解决,最主要还是了解其中的机制。可以参考这篇文章《作用域与闭包:this,var,(function () {})》。

附录

数值的常见用法和结果。

用  法                  结  果

Number(false)                  0

Number(true)                   1

Number(undefined)              NaN

Number(null)                   0

Number( "5.5 ")                5.5

Number( "56 ")                 56

Number( "5.6.7 ")              NaN

Number(new   Object())         NaN

Number(100)                    100

parseInt("AF",  16);  //returns  175

parseInt("10",   2);   //returns   2

parseInt("10",   8);   //returns   8

parseInt("10",   10);   //returns   10

Boolean("");  //false  –  empty  string

Boolean("hi");   //true   –   non-empty   string

Boolean(100);   //true   –   non-zero   number

Boolean(null);   //false   -   null

Boolean(0);   //false   -   zero

Boolean(new   Object());   //true   –   object

参考资料:

[1] React-Native官方文档

[2] React Native 中文网

[3] GitHub: react-native

[4] GitHub: react-redux

[5] React Native 中组件的生命周期

[6]《作用域与闭包:this,var,(function () {})》

你可能感兴趣的:(React-Native)