发表日期:2016-03 文章编辑:小灯 浏览次数:961
译注: 这是今年5月份React Native刚发布的时候,在code.facebook.com发布的一篇博客。时隔5个月,这篇文章依然值得一读,尤其是对于想了解为何Facebook要开发并发布React Native的新手、对于React Native的由来以及和其它框架的区别感兴趣的同学,都可以读一读这篇文章。
来源:code.facebook.com 原文链接 作者:Tom Occhino
如果你对React没有什么了解,你可以先去React官方网站阅读相关的内容。你也可以直接从React Native开始了解,这是我们在2015年F8展会发布的新框架。
一切从React开始
我们在两年前就公开了我们的React框架,并且它取得了令人瞩目的成长,不论是在Facebook内还是外部。今天,即使没有任何人强迫,Facebook内部的很多web项目都已经使用React构建,它也逐步被工业界广泛采用。工程师们选择每天使用React来开发,因为它使得工程师们可以把更多的时间和精力用于关注他们的产品本身,而不是应付框架带来的各种问题。随着我们的不断使用,终于,我们开始了解是什么使React如此强大。
React强迫我们把我们的应用划分成多个互不相关的组件,每个组件作为一个独立的视图。这使得我们更容易迭代我们的产品,因为我们不用在改动一小部分的时候把整个系统都装在我们脑子里。最重要的是,React包装了复杂而易变的DOM API,改为提供一个声明式的结构,使得整个程序模型变得抽象而简单。我们发现当我们使用React来构建网页时,我们的代码变得十分可预测。这种可预测性使得我们在快速的迭代产品时更多的信任已有的代码,最终我们的应用程序也变得更为可靠。更进一步,不仅仅扩大我们的应用规模变得更容易了,我们的团队规模也更容易进行调整。
我们一起对web产品经快速迭代后,我们已经可以用React去创建一些棒极了的产品,包括Facebook.com的很多组件。在此基础上,我们还构建了一些Javascript顶层的框架,例如Relay,这使得我们的数据获取流程也变得更为简单。当然,web并不是唯一的目标,Facebook也有一些被广泛使用的Android和iOS应用,是基于一些完全不相关的技术栈来构建的。不得不为每个平台去构建App使得我们的工程师团队不得不被分隔成几个部分去运作,而这还不是移动应用开发唯一的难点。
为什么原生开发如此困难
有很多关于原生开发环境和web开发比起来难的多的解释。举例来说,在屏幕上去布局一个东西就十分困难。我们往往不得不自己去计算视图的大小和位置。我们也没办法像网站开发那样用React或Relay来简化我们的开发流程或者扩大我们的工程师团队。而且对我们来说迁移到移动平台最痛苦的事情,就是它大幅的降低了我们的开发效率。
在开发Web应用时,我们只需要保存我们的文件,然后在浏览器中点击刷新,就可以看到改变的结果。而在原生开发的过程中,我们每次改动都得重新编译和构建,即使我所做的仅仅是把一个文本框在屏幕上挪动了几个像素。这使得我们的工程师在做很多工作的时候变得更加缓慢,尤其是当一个大工程的编译特别的慢的时候。为原生开发还使得我们为新功能进行测试变得十分困难。在Facebook,我们一个网站可能每天就要发布两次,这样我们几乎立刻就能获得实验反馈。而在移动设备上,我们经常要等上几个星期甚至是几个月来等待Alpha/Beta测试的结果。因为“跑的快”是Facebook的团队基因,然而在移动设备上我们实在没法和web上跑的一样快。那我们为什么一开始要放弃用web呢?
我们现在为不同平台单独开发原生APP,最重要的原因是我们能创造最佳的用户体验,并且能和这个平台的习惯保持一致。
用户体验的区别移植React的原生版本
把React移植到原生也是一个好主意。实际上,我们在iOS平台上已经这么做了,这个项目叫做ComponentKit,这也是我们昨天在F8展会上开源的项目。用ComponentKit,你可以获得React所有的好处,尤其是声明式的、可预测的UI。我们还能获得原生开发环境的各种强大优势,使用平台独有的组件和复杂的手势处理,或者异步的进行图片解码、文本布局、和渲染等等。不仅如此,因为ComponentKit用了flexbox作为布局方案,你不再需要自行去管理应用中各项视图的位置,所以你的代码最终能变得简洁而易于维护。
不过这个方案也有几处不太重要的负面影响。首先,它是iOS独有的,所以如果我们想在Android上获得一样的好处,我们只能去创造一个独立的实现,然后教会工程师怎么去用它。并且,我们也没办法同时使用我们为web顶层构建的各种工具,譬如帮助我们解决了规模性的数据获取的许多痛点的Relay。最重要的是,我们没能改善开发速度中最大的问题——我们还得每次修改之后,重新编译和构建工程。
用脚本封装原生
如果我们用JavaScript去调用原生API,我们应当能获得原生平台的所有强大之处,同时还能享受快速迭代和使用我们现有JavaScript上基础设施的好处。不仅如此,因为基于JavaScript构建,我们应当能使得这样技术栈跨平台。这听起来恰好是我们要的全部,而且毫不意外有成千上万的框架正在这么干,然而实际上问题并没有被直截了当的解决。
用脚本封装原生是一件需要技巧的事情
如果我们只是同步的在原生环境和解释环境之间调用,我们的UI线程很可能会被JavaScript执行阻塞住。要提升界面的响应效率,我们知道我们必须把JavaScript放到主线程之外执行,但这么做其实很困难。最直接的困难就是资源访问竞争。如果我们的JavaScript访问什么正好在被别的线程用的东西(譬如一个渲染的View的尺寸),系统就只能加锁来确保方案安全,而这又会导致UI线程的卡顿。还有一个问题在于每次原生和JavaScript虚拟机之间互相访问,在访问过程中都会带来极大的开销。如果我们要经常跨线程访问,我们不得不一次又一次的经历这种开销。
所以如果我们不找到一个正确的办法,我们的应用可能比我们整个用原生写或者整个用JavaScript写还要糟糕的多。我们不能只是把用户接口API又同步的封装了一遍然后就期待能获得一个流畅的、和原生APP一样的体验。我们必须要改变一些基础层次的设计来确保我们的系统传递消息永远是异步的,这样我们就可以把这些消息以一定的单位来打包发送,来尽可能减少一些跨线程交互的开销。
幸运的是,React恰好给了我们一个适当的模型来达到这个结果
Declarative=>Async=>Responsive
Sample1