React-Native

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 (
      <View style={[{flex:1}, style.red, style.centerItems, style.justifyContent]}>
            <Text style={[{fontSize: 20}, {fontWeight: "bold"}]}>Hello ,🌎 !</Text>
      </View>
    );
  }
}

// 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 的组件生命周期


引用地址:https://race604.com/react-native-component-lifecycle/

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

Mounting

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

Updating

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

Unmounting

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

ES6 常用语法


定义组件

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

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

// ES6
class View extends React.Component{
    render(){
      //  跨组件传值
      return (<React.View title='页面'  state=...this.props.route />);
    }
}

属性初始化与校验

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,相当于把所有命名一致的变量传入
<React.View title='页面'  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,在对应问题中一般都能找到前人的填坑记录。

其中,最常见的问题便是<jschelpers/...> 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 容易遇到的问题一般是找不到 <React/xxx.h> not find!。 遇到这类问题,一些文章会建议把第三方尖括号<React/xxx.h>引用改成引号"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 。

</br>

语言特性踩坑

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)

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 () {})》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容