控制组件显示与否的问题小结

问题说明

在开发React Native过程中,我们常常会根据一个变量的值是否为空来控制组件的显示与否。

一个简单的做法是{ someVariable && some text }

今天就因为写了这样的代码踩到了一个坑,导致App Crash了。

Error内容:RawText “” must be wrapped in an explicitcomponent.

分析了一波,发现是数据问题导致的。当下面的代码中someVariable的值为空字符串’’的时候,就会出现这种Error。

{ someVariable && some text }

测试结果

为了分析这个问题,针对someVariable为空的情况做了一些测试,结果如下:

case 1.{ ‘’ && some text }=>有Error,App会Crash

case 2.{ 0 && some text }=>有Error,App会Crash

case 3.{ null && some text }=> 没有Error,组件不会显示

case 4.{ undefined && some text }=> 没有Error,组件不会显示

case 5.{ NaN && some text }=>有Error,App会Crash

case 6.{ [] && some text }=> 没有Error,组件会显示

case 7.{ {} && some text }=> 没有Error,组件会显示

原因分析

为什么会出现以上结果呢?

为了分析这个问题首先需要明白expr1 && expr2的含义。

这个表达式是说当expr1为false的时候,会返回expr1,否则会返回expr2。

那么’’|0|null|undefined|NaN|[]|{}这些值到底是true还是false呢,我们可以在Chrome
Console上面通过两个非操作!!来测试一下。

从上面结果可以看到,’’|0|null|undefined|NaN的值是false,[]|{}的值为true。

好了,通过以上分析我们可以得到case 1 ~ case 6的结果如下:

case 1.{ ‘’ && some text }=>{ ‘’ }

case 2.{ 0 && some text }=>{ 0 }

case 3.{ null && some text }=>{ null }

case 4.{ undefined && some text }=>{ undefined }

case 5.{ NaN && some text }=>{ NaN }

case 6.{ [] && some text }=>{ some text }

case 7.{ {} && some text }=>{ some text }

在React Native中{ ‘’ }|{ 0 }|{ NaN }会被当做文本字符串,必须要包含在组件中,所以case 1、case
2、case 5会导致App Crash;case 3、case 4不会报错,也不会在页面上显示任何东西;case 5、case 6会在页面上显示some
text文本。

解决方案

为了规避App Crash的风险,有两个简单的方案来实现【根据变量的值是否为空来控制组件的显示与否】:

1.当变量类型为Object或Array时,可以使用lodash的isEmpty方法

{ !_.isEmpty(someVariable) && some text }

2.当变量为其他类型时,使用两个非操作

{ !!someVariable && some text }

Weex&ReactNative对比

weex开源有一段时间了,其实去年刚听说weex这个项目的时候,我就对它很感兴趣,很大程度上是因为我自己对vue的喜爱。我从13年左右开始接触vue,14年开始熟悉这个轻量的框架,并慢慢的推荐给了身边的朋友,当我得知手淘的weex是基于vue的时候,就有了想了解一下的冲动。在weex开源之前,我刚好有几个月的时间一直在致力于ReactNative的优化改造,加上自己之前使用ReactJS的一些经验,对于ReactNative项目也算有了一些自己的见解。趁着weex开源了,赶在前几天,我花了两三天的时间把weex
android的源码完整的看了一遍,前端js代码也粗略看了一下,结合自己对ReactNative源码的一些了解,正好在这里对两者做一个尽量中立的比对。

首先,我们要承认,weex的确是站在ReactNative的肩膀上的,核心思想上两者并没有大的区别,直观的看上去,我认为有三个主要的区别:

JS引擎

weex使用V8, ReactNative使用JSCore

JS开发框架

weex基于vue.js(2W+ star)。小巧轻量的前端开发框架,组件化,数据绑定,2.0引入virtual dom。

ReactNative使用React(4W+ star)。革命性的前端开发框架,组件化,数据绑定,virtual dom。

Android版本要求

ReactNative使用了Choreographer,因此必须在API16以上才可以使用。

weex使用handler来代替Choreographer,可以在API14以上使用。

weex出来的初衷也是为了解决ReactNative使用过程中遇到的一些问题,当然具体决定使用那个框架,我觉得需要从一下几个方面来做对比

学习成本

1. 环境配置:

ReactNative需要按照文档安装配置很多依赖的工具,相对比较麻烦。 weex安装cli之后就可以使用

2. vue vs react

react模板JSX学习使用有一定的成本 vue更接近常用的web开发方式,模板就是普通的html,数据绑定使用mustache风格,样式直接使用css

3. 布局

两者实现了flexbox的相同子集(都使用了FaceBook的代码解析),基本没有区别

易用性

1. sdk使用

ReactNative需要解决mvn依赖的问题,因此必须自己修改源码,打包发布

weex可以直接在mvn项目中使用

2 调试

都可以在chrome中调试JS代码

weex支持在chrome中预览页面dom节点,ReactNative不支持

3 页面开发

weex提供了一个playground,可以方便的预览正在开发的页面

ReactNative开发一个页面,需要建立一个native工程,然后编译运行

4 即时预览

weex和ReactNative都有提供hot reload功能,可以边更改代码,边在手机上看到效果

5 打包

ReactNative官方只能将ReactNative基础js库和业务js一起打成一个js bundle,没有提供分包的功能,需要制作分包打包工具

weex默认打的js bundle只包含业务js代码,体积小很多,基础js库包含在weex sdk中

6 部署

斑马目前同时支持weex和ReactNative页面,但是中心已经转向weex

另外斑马提供了可以拖拽搭建weex活动页面的功能

7 扩展性

组件的扩展上,weex和ReactNative具有一样的能力

三方库的接入上,weex对网络,图片,统计等常见的用户可能想自己定制的功能,提供了相应的适配接口,可以由用户方便的定制,ReactNative需要自己修改源码

8 集团库接入

weex有默认的mtop接入实现,UT接入实现

windvane也提供了对weex页面的支持,可以复用app中的web容器

9 跨平台

ReactNative支持Android iOS两个平台,需要自己扩展去支持web,windows和node-webkit的支持正在开发中

weex可以支持Android iOS web三个平台

10 Moudle方法调用线程

weex 可以通过注解标注是否在UI线程执行

ReactNative在native_modules线程执行

11 异步

weex只支持callback

ReactNative提供了Promise的支持

性能

1 分包加载

ReactNative需要自己实现,从而优化JS加载执行时间

weex默认提供分包实现

2 官方支持

ReactNative官方关注的重心目前并不在性能上

weex持续关注性能优化

3 大块view渲染

ReactNative默认没有优化机制,长view渲染性能会比较差

weex提供了node和tree两种渲染模式,优化长view的渲染

4 ListView Android

ReactNative目前采用scrollView使用,有一些性能问题

weex使用recyclerview实现,性能稍好

社区

ReactNative 3w+ star,issue,pull request, contributor多,社区活跃,围绕react产生了许多开发框架

weex开源较晚,目前只有4k+ start,contributor以阿里人为主,较少,issue和pull request也比较少,社区目前规模比较小

工具链

1 debug tool

都有提供在chrome中调试的支持

2 打包工具

ReactNative需要自己改造

weex默认提供的足够满足使用需求

3 webpack,gulp,脚手架工程

weex有相应的插件,方便开发,部署使用

ReactNative有,但是很久未更新,需要自己维护

通过上面的一些对比,就我个人来说,我还是比较倾向于使用weex,我比较熟悉vue是一方面,另外性能和发布这一块也是我比较关注的点。使用ReactNative确实也可以做到不错,但是最终我发现,自己其实是在做weex团队已经做的事情。与其这样,为什么我不选择weex,去帮助weex解决一些其他问题,给自己留更多时间去做业务开发呢?另外从业务开发的角度,我也觉得weex的门槛相对比较低,更适合业务开发同学上手,简单就是不简单。

http://dev.bingocc.com/buiweex/docs/

http://weex.apache.org/cn/guide/

TypeScript-跟-ReactNative-开发的关系

你用 TypeScript 语法写的 .ts .tsx 等后缀的程序是不能直接运行的,而是会被 tsconfig.json 配置中的 “target”:
“es6”, 这项配置转换为 es6 语法的 .js 文件。

TypeScript 中的 import 只会加载 .ts .tsx 后缀的文件,而 Javascript 中的 import 只能加载 .js
等后缀的文件,

所以,当 ReactNative 启动时,首先加载入口文件,如 index.android.js ,代码如下:

1
2
3
4
5
6
7
8
9
10
 import{ AppRegistry } from'react-native';



importIndexNavigator
from'./application/src/controller/navigator/IndexNavigator';



AppRegistry.registerComponent('mogudan', () = IndexNavigator);

其中 import IndexNavigator from … 这一行加载的不是 IndexNavigator.ts 而是编译后生成的
IndexNavigator.js 文件,下面对比两个文件的差异:

IndexNavigator.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**



* Created by ZHOUZ on 2016-08-26.



*/



import* as React from'react';



import{Navigator}  from'react-native';



importIndexPage from'../page/IndexPage'



exportdefaultclassIndexNavigatorextendsReact.Component<any,any {



    render() {



        let defaultName ='IndexPage3311113';



        let defaultComponent = IndexPage;



       return(



            <Navigator



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



                configureScene={(route) =
Navigator.SceneConfigs.VerticalDownSwipeJump }



                renderScene={(route: any, navigator) = {



let Component =[route.component;](http://route.component%3B/)



                   return<Component {...route.params} navigator={navigator}
/



                }}



            /



        );



    }



}

IndexNavigator.js

为自动编译后生成的es6语法的 javascript 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
"use strict";



var__assign = (this&&this.__assign) ||Object.assign ||function(t){



   for(vars, i =1, n =arguments.length; i < n; i++) {



        s =arguments[i];



       for(varpins)if(Object.prototype.hasOwnProperty.call(s, p))



            t[p] = s[p];



    }



   returnt;



};



/**



* Created by ZHOUZ on 2016-08-26.



*/



constReact =require('react');



constreact_native_1 =require('react-native');



constIndexPage_1 =require('../page/IndexPage');



classIndexNavigator extends[React.Component](http://react.component/){



    render() {



       letdefaultName ='IndexPage3311113';



       letdefaultComponent = IndexPage_1.default;



       return(React.createElement(react_native_1.Navigator, {initialRoute: {
name: defaultName, component: defaultComponent }, configureScene: (route) =
react_native_1.Navigator.SceneConfigs.VerticalDownSwipeJump, renderScene:
(route, navigator) = {



           letComponent =[route.component;](http://route.component%3B/)



           returnReact.createElement(Component, __assign({}, route.params,
{navigator: navigator}));



        }}));



    }



}



Object.defineProperty(exports,"__esModule", { value:true});



exports.default = IndexNavigator;
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×