上周,知乎上有几篇关于 Angular 和 Vue 对比的文章。本来想着的是,这些文章倒是可以指导下新手,作一些技术选型。可遗憾的是,开始的文章失去了一些偏颇,后面的文章则开始了一些攻击性行为。慢慢的,整个知乎上便是充满了一些戾气,开始了无尽的网络暴力。
于是,我想分享一下之前使用这些 MV* 框架的经验。
前端的摩尔时代
同样吧,在上周结束了《Expert Angular》的审校,这是第三本为 Packt 出版社审校的 Angular 的书。然后,先让我来讲个故事:一年前我开始审校的这本书,当时是基于 Angular 2 beta.4 写的,当时的书名叫 Mastering Angular 2。后来 Angular 2.0 正式版大改,作者们就重新改写代码,书名就改成了 Mastering Angular。完了 Angular 4 出来了,而 Angular 5 也进入了 Beta 版本,因此书名改叫成了《Expert Angular》。
由此可见,前端在这一个时代的变化之快。
场景一:在没有 AVR 时代的 Backbone
刚开始编写大型前端应用的时候,学习的是 Backbone。因为并没有一个好的 MVC 框架,在当时的情况下,仍然是最适合的选择。在当时来看,算是比较早的移动 SPA 应用,也具有它的一系列问题。
对于我们而言,采用 Backbone jQuery Spring 有几个明显的问题:
jQuery 带来的散弹性架构问题
一个 Backbone.View 则需要“继承”(extend)从
对于一个复杂的应用,他则会出现一个 PageView,ListPageView 这样的页面,而 ListPageView 则“继承”自 PageView,PageView 则 “继承”自 Backbone.View。在一些复杂的情况下,还会有 SubListPageView 这样的情况。
如我们所知,JavaScript 并不是一门完整面向对象语言。尽管,我们在写代码的过程中,由于 Code Diff 和结对编程的存在,减少了一些潜在的问题。
在前端还没有 LifeCycle 的概念之时,我们在原始的 View 里采用了 LifeCycle的设计。而在下一层 View,PageView 中则会继承这样的设计,以此类推。
这样的设计,看上去似乎并不存在问题。可当我们需要操作 DOM,我们就会用到 jQuery/Zepto。这个时候,除了当时移动端的手机性能问题。还有一个问题是,排查哪个 View 中操作了 DOM 变成了一件极其困难的事。
全局搜索相应的 ID,再寻找其继承关系,一一调试过来。而除了每一层 View 的关系外,还有在全局中会对一些 DOM 进行处理。
当你在某一层级修改了DOM 的时候,我只能祝你好运了。
而在新的 MV* 框架里,则可以使用模块化来解决问题。
前后端两次渲染的复杂度
Java 在后台渲染 Mustache,而 Mustache.js 则也使用同一个模板。我们所需要做的,便是在构建的时候,只需要用 require.js 将 Mustache 模板文件打包。
与今天的 React 后台渲染类似,API 以 JSON 的形式嵌入在 HTML 中。可与 React 的同构不一样的是,在 Mustache 和 Java 之间同步状态,并不是一件容易的事。
每当新加一个状态,便需要使用 Java 修改 ModelAndView,并启用一个新的 API,这个时候即要修改前端的框架,又要修改大量的后台测试。
除了此, 我们还需要考虑到,用户刷新页面的情况。当用户由在产品详情页,刷新页面时,我们需要将一些数据,通过 URL hash 传递到后台,然后解析 blabla。等这些完了,还要考虑将这个状态再传到前端。
这简直是一场噩梦,对于我们这种全功能团队来说还好,自己上手修改代码就完了。可一旦前后端分离,那你祈祷后端愿意帮你修改。
场景二:使用 React 重构(重写)
随后,我们开始计划使用 React 来重写应用,它能很好地解决我们上面遇到的问题。
我们是在 React 初期采用这个框架的,所以操作起来并不会像今天这么顺利。我们在实现原型系统的时候,需要自己去实现一个又一个的组件。而由于我们的系统,本身就已经是前后端分离,使用 React 对于我们而言,便像是使用新的框架来重写旧的业务。从业务价值来说,并没有太大意义。
不过,这个框架当时主要是用在桌面端版本上的,后来进行响应式设计便也用到了移动应用上。
再说说前后端渲染
React 的同构,能解决前后端渲染带来的问题。可是在当时来看,React 后台渲染所依赖的 Node.js 并没有那么“可靠”,其生态也没有现在完善。
除了直接使用 Node.js 渲染,我们还测试过的一种方案是,直接生成对应静态的页面。其数据量大概在一百万左右,一次生成这么多的数据是一种极大的挑战。测试方案时,采用 Node.js 运行服务,然后用 Scrapy 爬取对应的数据,生成对应的 HTML。完成生成代码后,编写对应的 Message Queue,其将根据后台数据库的增、删、改来生成、删除、重新生成相应的 HTML。
没等项目完,我就换到一个新的项目。在新的项目里,采用的是 Angular。
场景三:Angular 实现桌面端与移动应用代码复用
2015 年底,在移动应用领域,能满足人力成本低、跨平台、速度快的框架中,就要数 Ionic Cordova Angular.js 的混合应用方式。而对于传统公司来说,他们有大量稳定的后台程序员,因而采用 Angular 这样的方案也更容易上手。
代码复用
Ionic 1.x 是基于 Angular 1.x,由于在 Web 端也采用了 Angular。这样做不仅从统一了技术栈,还实现了某一部分的代码复用。
一般的情形下,当两端的业务是一致的,那么必然有大量 Angular.js 的 services 和 factory可以复用,甚至是共用模块。再针对性的,编写相应的响应式布局,就大功造成了——参考场景二的例子。
由于移动应用需要调用某些原生接口,如日志,如 Toast 等等,那么总体上的差异还是蛮大的。可由于移动端业务与桌面端存在不一致,这仍是一个 Desktop First 的项目。在这样的项目里,移动端只有简单的查询等功能。
那么,对于我来说,有一些 services 和 controller 的逻辑就是:Ctrl C 和 Ctrl V 就能迅速解决一大半了。剩下的就是,匹配不同尺寸设备的 UI 和使用原生组件优化。
演进
后来,桌面端从 Angular 1.x 迁移(重写一部分)到了 Angular 4.x,旧的应用还运行在旧有的 Angular 1.x 代码上,而新的应用则运行在新的系统上。
而移动端则如我之前的文章所说,React Native WebView。React Native 编写新的业务,而旧的业务代码则以 WebView 的形式继续运行着。个中缘由,主要是人力不够。
为什么 Angular 在选型里失去优势?
在 Angular 1.x 到 Angular 2.x 这段期间里,有大量的技术人员因为奥斯本效应而选择了其他框架。
奥斯本效应,在IT业界有一个比较出名的“奥斯本效应”(Osborne Effect),源于电脑先驱亚当·奥斯本。在1980年初,颇受欢迎的个人电脑厂商奥斯本,其公司的创新式便携电脑还没有上市,就宣布他们要推出的更高档的机器,而又迟迟无法交货,消费者闻风纷纷停止下单订购现有机种,最后导致奥斯本因收入枯竭而宣布破产。
而 Angular 2.x 在 beta.5 作死的 API 大改,也导致了一部分用户离开,好在最后 Angular 2.x 活了过来。
场景四:Vue 快速上线
在上半年里, 由于某个业务需要,我需要创建一个新的移动 Web 应用:几天内上线是一个小的功能。因为只有两三天时间,我直接排队了 React,我相信没有一天的时间,我是 Setup 不好 React 全家桶的。而 Angular 也被我排除了,因为它要构建出包发布,从流程规范上比较麻烦。最后我选择了:Vue jQuery WeUI。
Vue 引入 vue.min.js 就可以使用了,直接拿代码库就可以发布了,不需要打包。不过直接把 Vue 的模板嵌入到 HTML 与 jQuery 的 ID 直接使用起来。虽然方便,但倒也是一场噩梦。
要是变成了散弹式架构,那么可就是一堆麻烦。
Vue 大法好啊~。
场景五:Dili
https://github.com/phodal/dili
To be continue....