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/

React-Native国际化多语言

库属性介绍

项目地址:<https://github.com/AlexanderZaytsev/react-native-i18n

属性 解释

支持RN版本 所有版本

支持平台 iOS+Android

是否需要NativeModule 是

是否可移植 是

是否含有jni模块 否

使用

1.install(略,git里都写着了,就是npm那些事)

2.项目中使用

因为是一些静态属性引用,所以你用redux做储存替换也可以,直接做饮用也可以(本文拿en,zh为例)。

首先是建英文版本的配置文件,en/index.js

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
exportdefault{



home:{



greeting:'Greeting in en',



tab_home:'Home',



tab_donate:'Donate',



tab_demo:'Demo',



language:'language',



live_demo:'Live Demo',



buy_me_coffee:'Buy me a coffee',



gitee:'Gitee',



star_me:'Star me',



donate:'donate',



exit:'exit?',



},



donate:{



donate:'donate us~~~',



donate_desc:'© 2017 Pactera Technology International Limited. All rights
reserved.',



},



demo:{



dialog:'dialog',



button:'button',



switch:'switch',



action_sheet:'Action Sheet',



}



};

然后是中文的zh/index.js

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
 exportdefault{



home:{



greeting:'Greeting in zh',



tab_home:'首页',



tab_donate:'捐赠',



tab_demo:'例子',



language:'语言',



live_demo:'例子',



buy_me_coffee:'请我一杯coffee',



gitee:'Gitee',



star_me:'关注我',



donate:'贡献',



exit:'是否退出?',



},



donate:{



donate:'支持我们~~',



donate_desc:'© 2017 Pactera Technology International Limited. All rights
reserved.',



},



demo:{



dialog:'提示框',



button:'按钮',



switch:'开关',



action_sheet:'',



}



};

属性名,结构是一致的只是属性不同,当然这里是静态的2个文件,如果场景需要可以服务端下发json,那就是完全动态的了,这部分看业务需求了。

默认的语言环境

我们在上面写了2种语言配置,那么哪种作为初始化的呢?在业务层调用前,我们可以先进行预设

i18n/index.js

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
import i18nfrom'react-native-i18n';



import enfrom'./en';



import zhfrom'./zh';



i18n.defaultLocale ='en';



i18n.fallbacks = true;



i18n.translations = {



en,



zh,



};



export {i18n};

这边进行了一些预设,默认语境为en,允许fallbacks状态(为true时,顺序向下遍历翻译),预设转换的文件就2个,一个en一个zh,这个你也可以自行后续添加根据需求而定。

业务层调用

先是倒包

import {i18n} from ‘你预设的index的目录’;

调用(拿一个Toast做个例子)

ToastAndroid.show(i18n.t(‘home.exit’),ToastAndroid.SHORT);

源码分析

这个库的实现分为2部分,一部分是Native的版本判断等功能以及js部分的核心实现fnando/i18n-js

i18n-js是一个轻量级的js翻译库,他支持各种格式和内容的换算和语言内容的切换,地址如下:<https://github.com/fnando
/i18n-js

那么翻译转换这块是 I18n.js做的那么Native做了些啥呢?我们来一探究竟(以安卓为例,苹果看不懂,抱歉)

Native代码就两个类,所以我之前说你直接把Native代码copy走然后项目依赖I18n.js也能达到这个效果

RNI18nPackage是一个普通的Package类,它的作用就是把我们的module加到主应用的getPackages()方法中的列表里,然后一起打进包里而已。

具体功能都在RNI18nModule里

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
publicclassRNI18nModuleextendsReactContextBaseJavaModule{



publicRNI18nModule(ReactApplicationContext reactContext) {



super(reactContext);



}



//RN调用的控件名



@Override



publicStringgetName() {



return"RNI18n";



}



//对取出的Locale列表进行格式化的方法



privateStringtoLanguageTag(Locale locale) {



if(Build.VERSION.SDK_INT = Build.VERSION_CODES.LOLLIPOP) {



returnlocale.toLanguageTag();



}



StringBuilder builder =newStringBuilder();



builder.append(locale.getLanguage());



if(locale.getCountry() !=null) {



builder.append("-");



builder.append(locale.getCountry());



}



returnbuilder.toString();



}



privateWritableArraygetLocaleList() {



WritableArray array = Arguments.createArray();



if(Build.VERSION.SDK_INT = Build.VERSION_CODES.N) {



//获取区域设置列表。这是获取区域的首选方法。



LocaleList locales = getReactApplicationContext()



.getResources().getConfiguration().getLocales();



for(inti =0; i < locales.size(); i++) {



array.pushString(this.toLanguageTag(locales.get(i)));



}



}else{



array.pushString(this.toLanguageTag(getReactApplicationContext()



.getResources().getConfiguration().locale));



}



returnarray;



}



//js端可获取属性的列表



@Override



publicMap<String, ObjectgetConstants() {



HashMap<String, Object constants =newHashMap<String,Object();



constants.put("languages",this.getLocaleList());



returnconstants;



}



//提供给js端调用的方法,用来获取默认的语言环境,回调方式用的是promise



@ReactMethod



publicvoidgetLanguages(Promise promise) {



try{



promise.resolve(this.getLocaleList());



}catch(Exception e) {



promise.reject(e);



}



}



}

加一个toast看下locale会出现什么

Toast.makeText(getReactApplicationContext(),”locales.get(i)
“+locales.get(i),Toast.LENGTH_LONG).show();

本想一探究竟内部的实现,结果是个不公开的类

总结

首先Native那里获取本手机的LocaleList然后格式化取第一个元素交由I18n.js处理,然后I18n.js根据key选用一套有效的语言规则,再之后流程就和使用时候的顺序一样了。

整个库集成难度较低,使用起来比较简便,使用下来没碰到大坑,配合redux更美味。

准备阶段

[react-native-i18n](https://github.com/AlexanderZaytsev/react-native-
i18n)第三方多语言库

安装:yarn add react-native-i18n

[react-native-device-
info](https://www.jianshu.com/p/%5Bhttps://github.com/rebeccahughes/react-
native-device-info%5D(https://github.com/rebeccahughes/react-native-device-
info))用户获取系统本地语言环境

安装:yarn add react-native-device-infoandreact-native link react-native-device-
info

实践阶段:

在项目中创建zh.js、en.js、I18n.js三个js文件,DataRepository.js是一个我自定义的数据持久化类,在这个demo中的作用是存取用户改变后的语言环境,直接拷贝过去就可以用(不是必须的)。

代码分别为:

zh.js

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
exportdefault{



english:'英文',



chinese:'中文',



changeToEnglish:'切换到英文',



changeToChinese:'切换到中文',



changeToSystem:'切换到系统语言',



}

en.js

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
exportdefault{



english:'english',



chinese:'chinese',



changeToEnglish:'change to english',



changeToChinese:'change to chinese',



changeToSystem:'change to System',



}

I18n.js

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
importI18n,{ getLanguages }from'react-native-i18n'



importDeviceInfofrom'react-native-device-info'



importDataRepositoryfrom'../dao/DataRepository'



importenfrom'./en'



importzhfrom'./zh'



I18n.defaultLocale ='en';



I18n.fallbacks =true;



I18n.translations = {



en,



zh,



};



I18n.localeLanguage =()={



newDataRepository().fetchLocalRepository('localLanguage')



.then((res)={



I18n.locale = res;



})



.catch((error)={



I18n.locale = DeviceInfo.getDeviceLocale();



});



returnI18n.locale;



};



export{ I18n, getLanguages };

重点方法、属性讲解

I18n.t(): 使用频率是最高的,举个栗子:

<Textstyle={styles.welcome}

{I18n.t(‘english’)}

</Text

以上I18n.t(‘english’)中的english参数就是在zh.js、en.js文件中的语言配置项

具体显示内容会随着语言环境调用相应的语言配置文件,呈现给用户相应的语言内容。

I18n.getLanguages获取用户首选的语言环境

I18n.locale: 设置本地语言环境。

I18n.defaultLocale首选默认语言

I18n.fallbacks: 看文档说明我理解的意思是:如果获取到的系统语言类似en_USen-GB这样的,插件初始化的时候发现没有en_US.jsen-
GB.js,这个时候如果设置了I18n.fallbacks =
true;系统就会按这样的(en_USen.js)顺序去查找文件,就会去找有一个en.js这样的文件, 官方建议使用I18n.fallbacks =
true;

更多关于i18n-js的属性和方法请点击这里查看

ios需要配置语言环境

使用过程中发现一个刷新的问题:

我在使用过程中发现调用了I18n.locale=‘我设置的语言’后,当前的界面语言并没有改变,而其他界面的语言已经改变了,就比如说我上面截图的侧滑菜单,当我在侧滑菜单切换语言后发现侧滑菜单里面的语言并没有发现变化,而首页的语言环境已经改变了,我不知道为什么,摸索最后找到了一种解决方案(可能不是最佳方案,但是解决了刷新当前界面语言的问题,如果有更好的方法欢迎分享),解决方案:调用一下setState(无论设置的这个state属性在render中有没有被使用,都有效)。
具体代码看App.js,我项目中有使用localeLanguage所以我把改变后的语言存到state中

1
2
3
4
5
6
7
8
9
this.setState({



localeLanguage: I18n.locale



});

请注意,js的名字最好都是语言的缩写,下面提供参考:

突然有个想法看看没有支持的语言会变成什么:看来如果没有支持某种语言就会默认使用英语,我曾经试过分别调换这两个的引入顺序发现结果还是英语

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

×