紧跟前端发展,共享一线技术——推荐关注 InfoQ 垂直公众号前端之巅。
最近,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 能够以之前的速度改进并成熟起来。
感谢郭蕾对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ , @丁晓昀),微信(微信号: InfoQChina )关注我们。
评论