《架构师》2017年1月
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

观点 | Opinion

我们为什么选择Vue.js而不是React

作者Anton Sidashin,译者 薛命灯

最近,Qwintry开发团队把很多项目都迁移至Vue.js,包括所有遗留的项目和新开始的项目:

• 遗留的Drupal系统(qwintry.com)

• 新的qwintry.com分支,该分支是对旧有项目的彻底重写

• 基于Yii 2的B2B系统(logistics.qwintry.com)

• 其他大大小小的内部和外部项目(大部分使用PHP和Node.js作为后端)

Qwintry在全球有差不多五十万用户,我们在美国和德国各有一个仓库,而且我们是美国最大的货物转运公司,业务主要面向东欧和中东地区。

简单地说,我们帮助上述地区的人们购买美国网上商店的商品,并帮他们节省跨国运费。我们使用自己的IT系统和物流系统为他们提供高质量的转运服务,而且费用是非常实惠的。

我们的系统有很多代码,大部分是PHP和JS代码。

我们分别使用React、Vue.js和Angular 2构建了一个用户计算器作为对比实验,经过比较之后,我们最终决定采用Vue.js。

React.js之我见

React的出现震惊了JS世界,几乎成为JS开发的首选框架。

我使用React构建了一些单页应用和动态控件,我也玩过React Native(iOS)和Redux。我认为,对于面向状态的应用来说,React再合适不过了,而且React为我们提供了真正的面向函数编程。React Native在很大程度上改变了原生应用的开发。

对我来说,React的不足之处在于:

纯净、不可变性和解决问题的意识形态

不要误解,我其实很感激React所带给我们的纯净的函数编码方式和简洁的渲染手法,在实际应用中,这些都算得上好东西。我想说的是其它方面的东西。

如果你的公司里有千人开发团队,而刚好你决定要为PHP里的静态类型开发自己的语法,又或者你正从Haskell转向JS,那么一定程度的严格和纯净是非常有用的。不过大部分公司不会有那么大规模的开发团队,也不会有Facebook那样的宏大目标。下面我会更详细地解释这一点。

JSX糟糕透了

我知道,它“不过是具有特殊语法的javascript罢了”。我们的前端设计人员为了做出漂亮的表单,把表单元素放置在div里面,他们根本不关心什么纯净或ES6。设计React组件仍然需要耗费大量的时间,因为JSX的可读性太差。还有一个不好的地方,就是你无法在HTML代码里使用普通的IF语句。React的忠实用户会告诉你说,有了三元运算,就不需要再使用条件判断了。不过我向你们保证,当你再次阅读或编辑这些代码时,你会发现它们仍然是HTML和JS的混合体,尽管它们可以被编译成纯粹的JS。

        <ul>
                {items.map(item =>
                  <li key={item.id}>{item.name}</li>
                )}
        </ul>

很多开发者认为这种严格的限制可以帮助我们写出更加模块化的代码,因为我们必须把代码块放到工具函数里,并在render()方法里调用,就像这个人建议的那样:http://stackoverflow.com/a/38231866/1132016

JSX甚至让我们不得不把15行的HTML代码分成3个组件,每个组件里包含5行代码。

不要认为这种做法会让你成为更好的开发人员,你只是不得不这么做而已。

而事实其实是这样的:如果你正在开发一个相对复杂的组件,而你并不打算明天就把它发布到GitHub上,那么上述的方式只会拖你的后腿,特别是在你要解决真实的业务问题时。不过不要误会,我并不是说拆分成小组件就一定不好。

你当然清楚通过拆分可以提升代码的可管理性和可重用性。但前提是,只有当业务逻辑实体可以被放到一个单独的组件里时才要这么做,而不是每写一个三元操作代码就要进行拆分!每次在创建新组件时都会让你和你的意识流付出一定的代价,因为你需要从业务思维(在你记住当前组件状态时,需要增加一些HTML代码让它运行起来)转换到“管理思维”——你需要为这个组件创建单独的文件,考虑如何给新组件添加属性,并把它们跟组件状态映射起来,还要考虑如何把回调函数传递进去,等等。

你被迫使用这种过度且不成熟的组件模块化方式来编写代码,从而降低了编码速度,而从中得到的模块化可能并非你所需要的。在我看来,不成熟的模块化跟不成熟的优化没有什么两样。

对于我和我的团队来说,代码的可读性是非常重要的,不过是否能够从编码中获得乐趣也很重要。为了实现一个简单的计算器控件而去创建六个组件,这样的事情一点也不有趣。大多数情况下,这样做也不利于维护、修改或控件检修,因为你要在很多文件和函数间跳来跳去,逐个检查每一个HTML小代码块。再次强调,我并不是在建议使用单体,我只是建议在日常开发当中使用组件来替代微组件。这是常识性问题。

在React里使用表单和Redux会让你忙得停不下来

还记得吗,React的设计在于它的纯净以及干净的单向数据流。这就是为什么LinkedStateMixin不受待见,你需要为10个输入创建10个函数,而80%这样的函数只包含了一行this.setState()代码,或者一次redux调用(或许你还需要为每个输入创建一个常量)。如果只要在脑子里想想就能自动生成这些代码的话,或许还是可以接受的,但现在还没有哪个IDE可以提供这样的功能。

为什么要敲这么多的代码呢?因为双向绑定被认为是不安全的,特别是在大型应用里。我可以肯定地说,双向数据流的代码可读性确实不是很好,而Angular 1的双向绑定更是糟糕透顶。不过,这些还算不上大问题。

最近我用Vue.js为Drupal网站开发了一组快速编辑器组件。

由于一些众所周知的原因,我不能把代码分享出来,不过我可以说使用Vue真的很有趣,而且代码可读性很好。而且我可以肯定地说,在React里开发这种控件,为每个输入创建一个单独的函数,我一定会感到很痛苦。

Redux看起来更像是啰嗦的代名词。开发人员抱怨Mobx把React变成了Angular,就因为它使用了双向绑定——可以参见之前讲到的第一点。似乎很多聪明人只是让他们的代码看起来更纯净,但是并没有完成更多的事情(不过如果你没有截止期限或许问题不大)。

过度的工具绑定

React有点乱糟糟的。如果离开了一大堆npm包和ES5编译器,要做出React应用简直是寸步难行。基于React官方基础包开发的一个简单应用在node_modules目录下包含了大约75MB的JS代码。

这不算什么大问题,它更像是JS世界的事情,不过使用React仍然只会增加我们的挫折感。

Angular 1:太多的自由有时候不是好事

相比React所强调的所谓JS纯净性和代码可读性,Angular 1算得上一款优秀的前端框架。Angular 1可以帮助我们快速进入开发,在代码的头一千行,我们会感到很有趣,但在那之后,代码开始变得糟糕起来。你会迷失在指令和作用域里,而且层间的双向数据流会变得像蛋糕上的樱桃一样,那些新来的开发人员甚至都不想去触碰一下,因为它们太难以管理了。

为什么会这样?

Angular.js是在2009年出现的,那个时候前端世界还很单纯,甚至没有人会想到状态问题。不过我们不能抱怨这些人,他们只是想创造出一个框架,并成为Backbone的竞争对手,他们赋予它一些新的概念,并且可以少敲一些代码。

Angular 2

我们只是创建了hello world应用,它就生成了很多相关文件。你需要使用Typescript和编译器才能开始工作。这些对于我来说已经够了……在开始真正的开发工作之前,我还是觉得需要敲的代码太多了。在我看来,Angular 2团队只是试图构建一个可以完美击败React的框架,而不是试图为一般的用户解决业务上的问题。或许是我想错了,或许我会改变想法。不过我还没有太多使用Angular 2的经验,我们只是构建了一个计算器演示应用作为内部的评估。Vue.js网站上有一个很精彩的关于Angular 2和Vue.js之间的比较,这两者之间共享了很多设计上的概念。

Vue.js

简而言之,Vue.js是一个我等待了很久的框架(我以后会讨论Vue. js 2,相对第一个Vue版本,它做了很多改进,目前所说的是第一个稳定版本)。对于我来说,它优雅而简洁,并把注意力集中在解决问题上。Vue.js是继2007年jQuery出现之后JS世界最大的一次改变。

如果你看过Vue.js的热度图,你会发现不止我一个人这么认为:http://www.timqian.com/star-history/#vuejs/vue&facebook/react

Vue.js是2016年发展最快的JS框架之一,而且我认为它的崛起并不是因为粉丝的过度追捧,也不是因为某个大公司的权威推动。

Laravel把Vue.js加入到它的核心组件,这算得上是一件大事。

Vue.js的优势

Vue.js在可读性、可维护性和趣味性之间做到了很好的平衡。Vue. js处在React和Angular 1之间,而且如果你有仔细看Vue的指南,就会发现Vue.js从其它框架借鉴了很多设计理念。

Vue.js从React那里借鉴了组件化、prop、单向数据流、性能、虚拟渲染,并意识到状态管理的重要性。

Vue.js从Angular那里借鉴了模板,并赋予了更好的语法,以及双向数据绑定(在单个组件里)。

从我们团队使用Vue.js的情况来看,Vue.js使用起来很简单。它不强制使用某种编译器,所以你完全可以在遗留代码里使用Vue,并对之前乱糟糟的jQuery代码进行改造。

恰到好处的神奇

Vue.js可以很好地与HTML和JS一起协作。你可以开发出非常复杂的模板,而不会影响你对业务的专注,而且这些模板一般都具有很好的可读性。当模板膨胀到很大的时候,说明你在业务实现方面已经取得进展,这个时候你或许想把模板拆分成更小的组件。相比项目启动之初,此时你对应用的整体“映像”会有更好的把握。

从我的经验来看,这个跟在React里不太一样:Vue.js帮我节省了很多时间。在React里,在一开始就要把组件拆分成微组件和微函数,否则你会很容易迷失在乱糟糟的代码里。在React里,你需要花很多时间在一次又一次的整理prop和重构微组件(这些组件可能永远都不会被重用)上面,因为如果不这么做,在修改应用逻辑时就看不清方向。

在Vue里面使用表单是件轻而易举的事情。这个时候双向绑定就会派上用场。就算是在复杂的场景里也不会出现问题,不过watcher乍一看会让人想起Angular 1。在你拆分组件的时候,会经常用到单向数据流和回调传递。

如果你需要用到编译器的一些特性、lint、PostCSS和ES6,你会如愿以偿。在Vue.js 2里,Vue的扩展特性将会成为开发公共组件的默认方式。顺便提一下,开箱即用的组件CSS看起来是个好东西,它们可以减少对CSS层级命名和BEM的依赖。

Vue.js的核心具有简单有效的状态和prop管理机制,它的data()和props()方法在实际当中可以有效地工作。通过Vuex可以实现更好的关注点分离(在我看来它跟React里的Mobx有点类似,都包含了部分可变状态)。

我认为大部分Vue.js场景都不需要Vuex提供的状态管理,不过多一个选择总不是坏事。

Vue.js的不足

最大的一个问题:模板的运行时错误描述不够直观,这个跟Angular 1有点类似。Vue.js为JS代码提供了很多有用的警告信息,例如当你试图改变prop或不恰当地使用data()方法时,它会给出警告。这也是从React借鉴过来的比较好的方面。但对模板的运行时错误处理仍然是Vue的一个弱项,它的异常堆栈信息总是指向Vue.js的内部方法,不够直观。

这个框架还很年轻,还没有稳定的社区组件。大部分组件是为Vue. js 1创建的,对于新手来说有时候难以区分它们的版本。不过你可以在不使用其它第三方库的前提下在Vue里面完成很多事情,你可能需要一些ajax库(如果你不关心同构应用,可以考虑vue-resource)或者vue-router,这在一定程度上平衡了Vue在组件方面存在的不足。

社区软件包里的代码有很多中文注释,这一点也不奇怪,因为Vue. js在中国很流行(它的作者就是个中国人)。

Vue.js是由一个人维护的项目,这个也算不上大问题,不过还是要考虑其它一些因素。尤雨溪是Vue的作者,他曾经在Google和Meteor工作,在那之后他创建了Vue。Laravel也曾经是一个单人项目,不过后来也很成功,但谁知道呢……

在Drupal中使用Vue.js

首先声明一下,我们不打算在Qwintry里使用Drupal 8,因为我们正在转向使用更快跟简单的PHP和Node.js架构,况且我们的遗留代码是基于Drupal 7。

因为我们的遗留系统qwintry.com是基于Drupal开发的,所以有必要在Drupal里对Vue.js进行测试。对于遗留代码,我并不引以为豪,不过它们能够正常运行,并为我们带来价值,所以我们尊重它们,并对它们进行改进,还增加了很多新的特性。以下列出了我们在Vue和Drupal里所做的事情:

就地编辑复杂的订单节点对象。这个功能包括为客户生成票据以及快速对商品进行编辑。它要求提供基本的JSON API来加载和保存节点数据,没有什么特别的,就是一些菜单回调函数。

为我们正在使用的SaaS软件系统提供了两个基于REST的仪表盘,有了这两个仪表盘,我们的客服就不需要登录到不同的站点去检查客户相关信息,他们可以直接从仪表盘上看到这些。

我知道还有很多后端开发人员被困在Drupal 7的Ajax系统里,仍然停留在2010年的水平。

我知道,当你试图使用Drupal的核心特性来构建多步Ajax交互表单时,事情会变得多么复杂,代码会变得多么的难以维护。是的,罪魁祸首就是ctools_wizard_multistep_form()和Ajax render!

Drupal的开发人员同时面临着构建现代UI的挑战,但现代JS框架的复杂性又让他们望而却步。我在一年前就是这样的。我可以告诉你,现在正是使用Vue.js来改善UI的最佳时机,把Vue.js的代码库放到/sites/all/libraries里,通过drupal_add_js把它们添加到模板里,然后开始改造吧。你会惊奇地发现,在客户端(包括表单)使用Vue,会让hook_menu里的JSON回调变得相当易于维护。

在Yii 2里使用Vue.js

一个有趣的事实:Yii是由一个叫作薛强的中国人创建的,所以Yii+Vue技术栈的发音并不难,它们是中国式的技术栈 :)

对于新版的Qwintry.com(还没有公开发布),我们选择Yii 2,我们相信它是最好也是最快的PHP框架之一。它当然不如Laravel流行,但我们用得很开心(不过我们也研究过Laravel,他们确实做得不错)。

我们将逐渐减少Yii 2和PHP生成的HTML代码量,而在REST后端生成越来越多的JSON,这些JSON是为VueJS客户端生成的。对于我们的Active Record模型,我们遵循的是API先行原则。

我们非常重视API,这也是为什么我们花了很多时间完善API文档,虽然它们只是在内部使用。

使用PHP 7和最新的MySQL数据库,Yii 2 JSON后端的响应速度几乎跟Node.js没有什么区别(差别也就是15到20毫秒),这对我们来说足够了,更何况它比Drupal要快上10到20倍。这是一直以来PHP跟其它第三发库最好的整合方式,足以保证我们手头代码的稳定。

总的来说,Yii 2和Vue.js的结合所带来的响应速度是很快的,而且我们也很高兴基于这两个框架开发代码。

我们还在内部的很多项目里使用了Vue.js。

结论

我们在三个月的时间里使用Vue.js为不同的项目开发了很多代码,结果也很令人满意。三个月对于后端来说也许算不上什么,但在JS世界里,它举足轻重。我们将会关注后续的进展。如果尤雨溪走对了方向的话,我期待着Vue会在16到24个月之后会变成主要的JS框架,至少对于小型的前端团队来说是这样的。不过我认为React仍然会是2017的主要JS框架,特别是如果React Native能够以之前的速度改进并成熟起来。