如何评价 React Native?(转自知乎)



首先一点,React Native真的是完完整整地将React.js的特性搬到了iOS平台上。所以我最开始说的关于数据流的控制以及Diff Update这些作用在React Native也能得到很好的体现,所以我前面说的竟然没有跑题。

不过,尽管都是用JavaScript,但是用React做iOS开发跟做网页开发是完全两种体验。在React Native里你见不到<div>,但是会经常用到<View>,也就是说我们已经完全脱离了HTML的框框,而是用标记语言的格式写类似XML风格的JS代码,调用的都是iOS平台下的原生组件。App在运行时也不会去跑一个WebView,而是只运行一个JavaScript Core VM,然后用Objective-C搭建一个Bridge把iOS的各种组件Expose给JS代码。React Native实现了一些常用的组件的Bridge可以直接在JS中使用,如果开发者需要使用其他的组件可以自己写一个Bridge将API Expose给JS VM,所以理论上Native App能做的事情React Native都能做到。

React Native将Flexbox排版移植到了iOS上,也就是说开发者可以不再忍受Constraint-based的Layout Engine,而转而使用更直观、更像CSS的Boxed Layout。

JS VM跟iOS的渲染是处于两个线程的,也就是说JS代码控制逻辑和Viewer,iOS Framework对Viewer进行渲染。从效率上来看由于JS跟O-C处在两个线程,不会出现因使用VM而带来的卡顿,相反App的使用体验十分流畅。对此不太确信的可以尝试一下Facebook Groups,你会感觉跟用Native App的体验没有什么区别。在开发的过程中JS可以脱离VM来跑,并通过JSON等的序列化来进行通讯,在React Con上的演示就是这样,他们可以直接用Chrome Developer Tools对App的JS代码进行调试。




可以看出我先前对React Native的预测基本都说中了,它实现了去WebView化,用iOS原生组件作为Viewer的Render Engine,JS代码虽然没有编译过但是执行效率还不错。从开发体验上来看,React Native提供了一套非常完整的开发Workflow,基本上你只要一直跑着iOS Simulator,更新代码后在Simulator中Cmd-R一下就能刷新App,基本不需要进行编译。我在想,这样一来其实一定程度上还能做到App内部的自主更新不是吗?只要把最新的JS代码拉下来就能放到VM里面跑了。就是不知道App Store会不会允许这样的更新机制。

App的Test也可以完全用JS来写,而且理论上来说Test的Context与Render环境无关,可以脱离iOS直接在本地的Node.js上跑,这对于程序的调试来说也是一种便利。开发者可以在非Mac系统下进行Test-driven的开发而不需要去考虑iOS的具体环境。

当然,从文档的各种留白我们可以看出React Native现在还处于非常初期的摸索阶段,还有很多iOS Framework下的东西没有很好地整合进去。但是FB已经向我们Proved the Concept,并且消除了人们对效率和兼容性的担忧。我觉得在现今的基础上做更深一步的融合是相当迅速的,我相信在接下来的几个月中我们将看到新一波的Paradigm Shift出现在移动App开发领域。




看到 @rank 把react-canvas都扔出来了我不得不来唠两句了。我也是这个星期才在HN上看到的这个项目,实际上这个项目一周前才发布出来。不得不说这的确是一个很赞的实现,我也见过一些用canvas写的网页,对移动端的适应做得非常好,比如这个Legend就设计得很酷炫。

但是,从我目前了解到的信息来看,react-canvas在制作规模化移动端应用方面并不能赶超React Native的势头,原因如下:

  • React Native的目的在于让原本用WebView做的App去掉DOM渲染层,用更加原生的界面元素和渲染引擎提升性能。但是如果用react-native的话则仍旧没有脱离WebView,两者的运行时体积可见一斑。而且这加多一层东西必然对性能有所影响,比如你用Native的方式做Transition Animation,在动画的实现方面不需要由你的代码控制,可以直接使用渲染引擎的自带动画,就算有些自定义动画也不必再JavaScript上面运行,因为React Native已经将JS编译成了Native的二进制来执行了,所以又减去一层JS虚拟机的效率损耗。再看react-native,你要跑canvas就不得不用WebView,不得不使用原本的JS引擎去驱动整个界面,你不得不将所有的控件、Layout、Animation全部在JS层面实现一遍,并全权交由JS来控制,这个计算开销不是一般的大。你如果仅仅为了保留WebView、JavaScript的情况下使用canvas硬件加速而去增加这么多中间层,实在不是一种理想的趋势。
  • 由于内容需要在canvas上渲染,那么有可能会遇到一些写游戏的人喜闻乐见的问题,比如CJK字体渲染。还有很多Accessibility方面的硬伤,比如canvas里面的文字没法选中、复制或者使用系统的Define功能查词,也无法兼容读屏程序。在Flipboard的例子中他们的解决这些问题的方法实在是太Dirty了,他们用回了DOM元素,用DOM渲染文字,然后悬浮在canvas上面,这样用户就能对文字进行Native的操作。但是这样的解决方法根本就是在倒退回DOM的开发模式啊好吗,为了达到上面的效果,他们还得在后台代码构造一个Virtual DOM Tree,实时跟canvas的内容同步,然后更新DOM的内容和style。这样的做法简直无法理喻,如果说canvas能靠硬件加速提升性能,你把DOM跟canvas混用那这个效率还能提升多少?再说如果用DOM画的部分要与canvas上的内容作更复杂的互动,用DOM是根本没法渲染的。引用react-canvas作者的话来说就是:“pushing the browser beyond its limits that we can make progress in this area.”所以这个项目到底有多Dirty作者本人也是知道的。

相比之下,React Native致力于去掉更多的中间层,只保留一个Flux+React的中心概念,这才是一个合理的发展趋势。我见到有人说React Native跟react-canvas两者不冲突,可以用canvas做React Native的渲染引擎。说这话的人还是没有认识到React Native的目的究竟在哪。我说了React Native在于去掉中间层,你又要用回canvas再加上一个WebView和一堆JavaScript,那React Native的意义何在?就好比乔布斯千方百计把iPhone的厚度减少了一毫米,你带个套又给人加回去了一样。




看了所有的答案,都没有讲清楚React的实质作用在哪。

我上周用纯React完成了一个CMS发布系统的界面框架,数据层仅仅用了Parse的JS库,但是功能还算完备。中间读了不少这方面的资料,对Flux + React有了一个较为系统的了解。这里大致总结一下给还没开始动手写React的人一点启发。

要讲React的实质作用那一定不能脱离Flux模型。一说到Flux,很多人的第一反应就是那个经常见到的、看起来很复杂的Loop流程图。我不想一开始就贴那张图,因为我默认你们都没真正用过React,那张图只能让你们更加不明觉厉。其实了解Flux + React最好的方法就是看看官网给的这个视频,我就把重点摘出来讲讲吧。

首先,Flux是为了解决MVC模型中数据流向不一致的问题的。视频中给出了一个非常典型的案例,可以说我以前也被类似的问题折腾了好久。比方说你从服务端拉下来的数据要改变两个View的内容,比如给未读消息的数字提醒+1,然后把消息放到Chat View中,如果用户正在看当前的Chat View就把未读消息数字-1。这一连串的操作在没有用React的时候要怎么写?她给出的代码也是我以前处理类似状况的方法:




我以前也不是没想过写一个MessageManager之类的,类似Flux的Dispatcher一样的东西去控制这些逻辑然后分别对不同的View进行改动,但是好好想想这不过是一个简单的信息接收而已啊,为啥要复杂到专门给一个数字写一个Dispatcher去做这种事情?所以最后想想还是算了,代码丑点就丑点,能用就行,最后我也写成上面那样了。但是你要知道,这样的代码写在异步程序里是非常危险的!如果用户同时在操作,那你的代码很可能就会被中断报错的。还有,如果后期需要加功能,比如在屏幕侧边再搞个信息框,那你怎么知道在哪去加代码?你程序的数据交换会像这样混乱:




怎么样?写异步MVC的童鞋有没有感同身受?脸书以前的Chat就是经常因为异步数据的问题被人黑。比如经常是右上角一个红色的1,点进去一看啥都没有,强迫症的怒火你可晓得?

那么问题来了:如何让异步数据像同步数据一样正确地显示出来?脸书的攻城狮想到一个绝妙的招式:咱别一个DOM一个DOM的去更新内容了,来一次数据俺们就“刷新”页面!是的,就是回到了90年代的那种模式:你想看新邮件?请刷新页面!然后服务端把你当前的邮件全部读一遍,排好序,生成好HTML,然后给你寄过来,你就能看到新邮件啦。Flux就是这么干的,只不过现在已经是21世纪了,渲染HTML这种工作不需要服务端也能做,我们也不用真的去刷新整个网页,虽说Chat的那个问题的确可以靠刷新页面解决掉lol。Flux的(大致)做法是:来新的数据了,好,我把它Merge到客户端的旧数据中,然后把客户端存着的所有数据交给一个Render,然后把整个HTML从头到尾渲染一边,最后把新的HTML替换掉现有的HTML就好啦!那么之前那个Chat的代码咱就可以这样写(我YY的,视频没有,也不是React代码)


function newMassageHandler(newMessage) {
  this.messages.push(newMessage);
  this.renderTheWholePage(); // In practice it should be an Action Creator
}

看到没有?管你有多少个View,你们自个儿去算怎么渲染吧,handler只管存数据跟提醒渲染。如果哪个View计算完了说,不对,那个Unread Message Number要-1,好,你跟Action Creator说一声,他会在下一轮渲染前把这个项目加到数据中的。如此一来就不会出现把改动View的代码写到某个Controller中,然后一个Controller又要同时关联好几个View的情况,这样一来整个程序的结构就变得非常清晰了。这时候再看这张图就容易理解多了:
 
 
 
数据的流向变得非常统一,如果View要对数据做些改动也得等整个View渲染完了在下一轮渲染的时候再改,从而形成一个非常完整的Loop,这对异步数据的处理是非常有帮助的。 Flux是一个取代MVC的设计模式,而React就是脸书写的给Flux用的专门做Viewer的框架。由于Flux要将原有的HTML从头到尾渲染一边,那么就不得不考虑一个平滑过渡的问题,因为毕竟我们要讲究用户体验,不能每次收到数据就真的把原网页给删了然后重新给用户画一个,那用户还不崩溃啊?React解决这个问题的办法就是用Virtual DOM,这才是React真正的核心价值。其原理是写看起来像是DOM的JS代码,然后生成ReactDOMElement的对象,而不是真正的DOM,然后把新的Virtual DOM Tree跟用户正在看的DOM Tree做个Diff,然后用最小的改动量Update整个网页。对比一下jQuery大概就是这样:

// jQuery
$('<button>').text('Submit').on('click', submitHandler);
// <button>Submit</button>
// React without JSX
React.creatElement('button', {onClick: submitHandler}, 'Submit');
// <ReactDOMButton>Submit</ReactDOMButton>
// React with JSX
<button onClick={submitHandler}>Submit</button>
 
可以看到,其实JSX只是React的一个语法糖,这只不过是一门DSL。有些人一看到JSX就说把HTML写JS里了,我真是不知道该怎么评价这种人。ReactDOMButton并不是真的DOM,在运行时只是一个普通的JS Object,我写成XML形式只是易读。不用真的DOM当然是因为效率问题,这也是一种优化手段,但是Virtual DOM的好处当然不止这样,后面会讲。 你要是不喜欢JavaScript也没事,我就不喜欢。知道GitHub的Atom吗?就是用CoffeeScript + React写的。它并不是用改过语法的那个看起来就很恶心的coffee-react项目,而是自己写了一个很简单的helper来做简单的Transform。大致的原理是这样的:

{div, button} = require('react').DOM
div
  className: 'message'
  onClick: @messageHandler
  'some messages'
  button()
我觉得这样的写法真的很简洁很优雅,而且一想到这写的根本就不是HTML也没碰一行JavaScript就觉得很开心啊。不过我是LiveScript党,有更加简洁的语法,把Atom的helper改了改现在用在别的项目中用得很方便。

看到这样的写法观众就应该很清楚地明白了React的原理了吧,它实际上就是把DOM元素全部变成了JS的类,可以直接用JS等价的代码调用new出来。(实际上是要用一个factory包着才能用的,0.12后把createFactory整合到createElement里面了所以现在用法上更加简洁)

但是千万不要以为React就只是JS世界中的东西,其实它也是受到别的领域的启发呢。上面那个视频中后半段详细介绍了React,其实React这种idea受启发于游戏的渲染。有很多游戏都是数据来了,跟现在的数据Merge一下,然后update数据结构,diff一下老的结构,然后再部分渲染。所以说React其实最重要的还是一种使用Virtual Element描述界面的方式,而因为这种方式容易进行二次计算,所以能够保证最小幅度的影响用户体验,最大幅度地保证数据跟界面的一致。因此React才能够很轻易地使用在后端,做给Spider渲染HTML的工作。要知道Angular在之前是做不到这一点的,对搜索引擎的优化竟然要用到PhantomJS!而且这种方式竟然还演变成了收费的服务!简直让人咋舌。但是React却能做到,因为它并不受限于JavaScript,脸书最先用React做后端的时候是PHP的,后来整合Instagram的时候才分离出来写了开源的JS库。

所以我觉得React Native的最终形式并不仅仅是把Web做到移动端上,因为那样根本没法跟Native的比。而是要把React这种Viewer的架构在Native上实现,把原有的模板式UI重构成Virtual Elements,然后以更加动态的方式渲染。不过就目前的开发进度来看,这个目标还是遥遥无期。



不深思则不能造于道。不深思而得者,其得易失。

名人名言- 曾国藩
  • By 知乎
  • 2015-10-16
  • 2767
  • 公司新闻,网站开发,网站设计,UI