前方预警:这将是一篇长文,我尽可能把主题区分开并做到良好的排版,因此你可以根据标题来检索你感兴趣的部分。
不过在我展开之前,先得丢几句不是那么“干”的货。从恨到爱,这个标题源自于一位同行的文章,在这篇文章里 Ember 只是一个引子,但却因为作者对于 Ember 的态度所发生的转变而使得他怀疑自己“讨厌 React”的态度可能也是错的。
我自己对 Ember 的态度倒没有这位老兄这样的极端化,至少我没有恨过它。对于技术性的东西,如果让你产生了恨意,那多半是因为自己太蠢:要么掌握不了它的用法,要么没搞清楚它真正的用武之地。可是人非圣贤,主观的好恶是免不了的,如果因为工作环境的需要你不得不使用一些你不喜欢的工具,那么你要么从容应对化腐朽为神奇,要么另谋高就,寻找更加适合自己的团队。恨,是解决不了问题的,它只会污染你和你身边同事的情绪;而作为工程师,我们是天生擅长解决问题的那一群人,何去何从就要问问自己的内心了。
本文最大的受益群体应该是那些对 Ember 无感甚至有些讨厌 Ember 的人,并且你们因为工作或事其他什么原因不得不用这个框架(这么一说,真没多少人吧……),既不算喜欢又不能逃避,这的确挺煎熬的。那么我希望能够通过分享我们团队使用 Ember 的一些经验让你们对 Ember 的了解能更加完整一些深入一些,如果能让你们获得一些“啊哈”的瞬间那就更棒了,我相信用不了太久你们就能从恨它到爱上它,或者从路人转粉,我的一点心愿吧。
我认为是的。
当然也有业界专家们也这么看:
上图截取自 2016 年 11 月发布的 ThoughtWorks 技术雷达白皮书,详情可见:https://www.thoughtworks.com/radar,有中文版的。
可以看出,在新的或是有持续进展的框架中,ThoughtWorks 对 Ember 成熟度的认同是最高的,和 React + Redux 同属一个级别——值得注意的是,React 算不上真正意义上的框架,即使搭配了 Redux 要达到 Ember 的完整度也还要花很多功夫的。
Angular 哪儿去了?v1 已经不再推荐了,v2 由于比较新,目前也很难下一个结论。我不否认 v2 有着一些很吸引人的特点,不过这不是什么“独占”的技术,需要的话我在 Ember 当中也可以容易的添加上它们。更重要的是非技术层面的观感,下面是一段引文:
You were part of the team at Angular, and you talk about “irreconcilable differences” between yourself and the team, could you talk a bit about that?
We disagreed on various technical choices which I felt wouldn’t be tenable in the real world, wouldn’t be flexible enough. But coming back to the business side of things, what I began to see when I worked there was that the entire development process was completely disconnected from the community. There was a lot of speak that went out that was “Thank you, we love our community…” and that was genuine, they really do, but when they went back to build version 2, there were no use cases or case studies coming from anyone, not even from inside Google. There were groups inside of Google using things that they could have done an official case study with, and that didn’t happen there and it didn’t happen publicly.
If you do a bit of research you’ll find that Angular is actually a subteam inside of something called GreenTea which is an internal app (specifically Google’s Adwords CRM). Really, that is the driver for Angular, and if Angular does anything at all, it’s going to be this one app. Because of how they’re set up there, Angular isn’t really this independent Google product that exists to do what people think it does. It does some of those things, but that whole business side is missing. It really exists because it’s funded internally by this other thing that has it’s own agenda.
捉虫:现在 Ruby China 的 markdown 无法正确处理引言的分段了
Rob Eisenberg 是 Aurelia 框架的作者(Aurelia 也在上图中有),同时也是 Ember 社区的活跃份子之一,最近 Ember Engines 的新功能就是他主写的。之前他曾经受邀加入了 Angular 2 开发组,主要负责新的路由系统,但后来他主动离开了。上面的引言来自于一次访谈,大概讲述了离开的原因。在这里我也无意展开去说什么道理,只是我觉得有些事情还是值得去了解一下的,所谓“兼听则明”吧,反正我一直对 Angular 的社区无感,个人意见。
这些事情对于一些人来说可能也无足轻重,但至少说明了部分业内人士对于 Ember 的评价是相当不错的,所以我认为它足够成熟,也足够让我向别人推荐。当然我们总是需要考虑实际的业务场景的,因此接着我就来说说:
咱一上来先说 Ember 不适用的地方,这样可以节省一些读者的时间。如果你觉得你现在做的事情和下面的描述比较接近的话,Ember 大概是无法给你带来什么特别的好处的。但如果你已经是一个经验丰富的 Ember 工程师,那么你也应该知道即使在这些场合之下 Ember 依然可以发挥它的优势,善于利用的话也可以改变一些典型的思路并获得出其不意的效果——这全看你对你做的事情的理解,不必强求。
而在你心里没什么谱的时候,下面这些情况是你可以不需要 Ember 的。
这个分类涵盖的范围极广,小到只有几个页面的官方网站,大到一个 CMS(比如现在遍地都是的网络小说网站)都算是以内容呈现和展示为主的网站。在过去十到二十年的时间里,互联网的主体内容就是这些,我们在日常获取绝大部份信息的渠道也正是这些。哪怕是今天,基于 Web 的应用程序这个概念炒得火热的 2016 年,我想至少八成以上的开发者做的事情也离不开这个范畴。这一类型的网站存在的时间足够长,大家对它的研究和探索也足够深入和广阔,因此能够把它做好的技术也是足够成熟和稳定的,用不用得着 Ember 真的是见仁见智的事情。在我看来,多数情况下不需要用到 Ember 也绰绰有余。有鉴于此,Ember 的用户基数很小在我看来一点儿也不奇怪。
但是近年来,Angular/React/Vue 等前端框架或工具的崛起吸引了很多开发者的目光,所以大家也都纷纷试水开始使用它们来开发这一类型的网站。在这个过程当中,那些基础好、善于钻研、并且对 web 的理解比较到位的人并不会觉得有什么不便,并且还能够从中发掘出这些新兴工具的闪光点,让它们能够为自己的日常开发起到更多的助益;反之也会有另外一部分人会觉得非常不适应,一面增加了自己学习的负担,一面却体会不到新工具带来的好处(注:这是客观存在的现象,而不是说一定要分出个高下来,各位看官请自行体会),于是会放弃它们回归到自己的舒适区域,这在我看来也是很正常的事情。
所以,你觉得新的工具在眼下帮不了你什么,那就不用它便是了,没必要产生一些额外的情绪,也没必要反感和排斥那些乐于探索新世界的人。想想前言里那位同行老司机吧,说不定有一天你也会产生同样的感受。另外,就算是停留在那些稳定舒适的技术区里,你就一定以为自己已经掌握的足够出色了吗?即使是那些今天看起来不那么 fancy 的工具和技术,它们所代表的基础技术素养也是极为重要的。我这里有几个例子,留着后面铺开了讲,下面再说说第二种:
还有一些个网站本质上也属于内容呈现类的,但却不局限于传统的文本、图片等媒介,而是大量采用动画、音视频、绚丽的色彩、大胆的布局、前卫的交互技术,甚至包括 3D 特效等技术。这类网站可以看作是第一类的升级加强版,会给用户带来美妙到震撼的感官享受。擅长做这类网站的前端工程师也是值得敬佩的,因为他们往往也要花费大量的时间在学习和钻研前沿技术上面,并且他们还要擅长设计,从传统的平面设计一直跨越到 Web 设计,同时也要包括各种形式的媒介展示技术。他们或许并不擅长那些知名的应用程序框架,或许也很少制作大型应用程序的经验,但是他们所擅长的东西也足以让偏工程化为主的开发者们羡慕不已了。
开发这样的网站不用 Ember 也是很正常的,因为这类网站的技术聚焦点不在于应用程序架构,而是更具体一些的技术,这个涉及的层面就很广阔了,比如说最近两年如火如荼的 HTML5 之中就有很多相关的技术点和 API 是为这类开发而生的。在过去很长一段时间里,这类网站主要被 Flash 的开发者们所垄断,后来鬼知道 Flash 都经历了些什么,反正这帮人基本上都转战 Web 了。可能像 Ember 这样的应用程序框架对他们而言是陌生的,或者是不对路数的,而没有 Ember 的话也不会让它们觉得缺少了些什么。
在我看来,Ember 的组件化技术其实也是可以派的上用场的,不过仅仅考虑视图这一块的话,用 jQuery 操作 DOM 已经足够优秀,喜欢组件化的话也可以选择 React 之类的工具,所以 Ember 在这里就无关紧要的很了。
这个类型其实算是一个临界点,拿 Ember 来说用或者不用当然也都可以,但个人认为用它带来的好处会更大一些。信息管理类的网站相比于内容展示类的网站最大的不同点在于:
有很多现成的例子,比如博客引擎 Ghost 的后台,论坛 Discourse 全站,Linkedin 的管理后台和移动应用,Yahoo 内部使用的一些管理后台等等都是采用了 Ember 作为开发框架。在国内多数此类的应用都是基于 Angular 开发的,鉴于本人拥有长达四年的 Angular 开发经验,类似的企业级管理后台也做过几个,我可以负责任的说综合评比 Ember 绝对比 Angular 好用,至于 Angular 2 我不熟,暂时也没有要研究它的打算,就不予置评了。
不过这里我们说的是可以不用 Ember 啊是不是?没错,因为这类网站大家也都是做的快要吐了的,不用 Ember 也没什么,只不过从这里开始使用 Ember 会带来更好的体验罢了——代价也有,比如说你得先学,而且在这类场景下想用的足够爽,你要学的还不能只是皮毛,否则发挥不出原有的十分之一也只会让你觉得束手束脚。
以上差不多涵盖了绝大部分基于 Web 的应用场景了。我说你可以不用,但我并没有说你不能用,也没有说用了不好,比较准确的意思应该是你不用影响不大,其他的技术手段足够应对,并且 Ember 或其他框架也不是万能的,某些角度的考量下它(们)也有自己的劣势。所以在这些场景下,Ember 可以不是第一选择。接下来我从自己的角度来说说什么时候 Ember 会是我的第一选择。
我们经常可以看到很多人在争论一个问题:jQuery 还是 xx 框架?而这种争论的源起大概应该是多数的现代前端框架都不推荐使用 jQuery 了,或者换个说法,它们都认为可以不需要使用 jQuery 了,并且在绝大多数情况下效果只会更好。
这样的意见倾向当然会引起争议,但人们在争论的时候却显然忽略了一个本质的问题:说可以不用 jQuery 的并不是框架,而是数据驱动这种新的思路,而数据驱动这种思路不是孤立存在的,它需要有相对完整的架构链路来保障数据的通路——也就是说,当你用数据驱动的思路去开发应用程序的时候,DOM 仅仅是一整个链条中的一个部分,甚至都不是重心。jQuery 不是不能在数据驱动的思路中发挥作用,但只靠 jQuery 是肯定不够的。当你做的应用在应对数据处理时的规模不太大或者不复杂,又或者数据相关的业务逻辑都是服务端帮你完成的时候,你对此的感觉是非常微弱的。那么这些情况我都已经在前文中列举过,所以你觉得 jQuery 够了不需要框架那也是十分正常的事情。
然而这些并不代表框架没有什么用处,或者框架在排斥 jQuery,我个人是很难理解为什么会有这样的想法,当我看到这些观点的时候我的感觉主要是两个原因:
另外一方面也有一些人觉得框架可以在任何场景下代替 jQuery 为代表的传统开发技术栈——注意,我又一次强调了可以这两个字——不能说这是错的,因为的确可行,但也不代表就是正确的、明智的选择。这种说法的确会引起一些意见上的冲突,不过可以不等于应该,阅读这些观点的时候我们应该有自己的判断能力。
如果遇到下列的场景,我也会毫不犹豫的使用 jQuery:
所以如果你问我 jQuery 有什么问题,我会说它没有什么问题,真正的问题在于我们每一个人身处的业务环境不同,技术栈环境不同等等,很多时候是环境在约束你的技术栈选择,再接下来才是个人的倾向。你可以设想一下,接下来如果你只有 API 可用且不给你后端 View 层,在目前的应用发展趋势下你若是还能坚持 jQuery 去搞定一切的话,那你也算得上一条汉子了。
那么有人就要问了:凭什么不给我后端 View 层?这么多年我都用得好好的呀!
你可以把前端能写的东西都混合在 MVC 的 View 层,这个时候 jQuery 似乎就足以应付了。
如果现在要你做另一个应用呢?需要不同的 UI,但是数据和 Model 都是同一份。OK,可以设置新的路由,然后派发给新的控制器 + 视图来做,控制器或视图的重复可以抽取出来。
如果现在要你做一个混合移动客户端呢?OK,我们可以判断 UserAgent 为移动端编写新的 View(且不论判断 UserAgent 是否准确,这里只是举例)
如果现在要你做一个原生移动客户端呢?呃……写 API 吧
如果你的应用程序的数据可以应用在更广泛的场合(而不仅仅是桌面浏览器里),那么为它编写一个 API(无论是内部使用的还是公共使用的)是迟早的事情。面对不断扩张的客户端需求,与其绞尽脑汁去修修补补现有的框架,还不如未雨绸缪的建立起 API 服务。
如果你的应用程序才刚刚开始,你甚至都会考虑后端只提供 API,而不再使用臃肿的 MVC 架构——这也是一种趋势,在很多新兴的互联网公司、团队、项目中都有着广泛的实践。
如果你现在所处的开发环境就是如此,那么有什么理由不去试试前端的框架呢?让我来描述一下最理性的思路:
即使你现在所处的开发环境还不是这样的,提供 API 这件事情从长远来看也是必要的。目前你可以依赖后端的 View,可要是遇到了我上面提到的一些场景,使用某种前端框架也是必要的了。
刚巧前两天有一位国外同行写了一篇文章,文章的内容在这里不重要,重要的是里面几张图画的很好,我就借花献佛一下:
图的意思我就不浪费篇幅了,相信各位都能看明白。知乎上有人做了中文翻译,顺带分享一下地址:https://zhuanlan.zhihu.com/p/23412169
(本章待续……)
静态资源的管理其实是每一个前端工程师的必修课,尽管这件事情和 UI 的关系没有那么紧密,但是只有前端工程师才对如何管理和使用静态资源最有发言权(当然到了存储和伺服的环节就会偏向服务端了),这也是为什么自打 node.js 诞生以来,成堆的任务运行器和模块打包/构建机制疯狂滋生的主观原因——我们需要优秀的管理工具。
当下最火的当属 webpack 派系了,不过由于 Ember CLI 诞生的较早并没有使用 webpack,而是借助了相对冷门的 broccoli 作为基础工具体系,然后在其基础上打造了专职服务于 Ember 体系的构建工具。也正是由于这个原因,大多数工程师对 Ember CLI 都不够熟悉,我在日常的交流中对此感触很深,因此我想单独撰写一章总结一下我所知道的一切。
Bower 很烦人,一个需要修正的历史。实际上目前 Ember CLI 对 bower 的依赖只有两个了:ember
和 ember-cli-shims
。前者已经重新发布到了 npm,目前正在集成测试中,预计下一个版本的 Ember CLI 就可以正式移除该依赖;而后者呢则可能会直接去除,新的 module resolution 方案将不再需要这些 shims。
但是在很长一段时间之内 Ember CLI 还是会保留对 Bower 的支持,因为已有的项目可能会用 Bower 安装一些其他的依赖。在我的开发经验当中,几乎所有的依赖都可以找到对应的 npm modules 来取代 bower components,唯一的例外是 device.js——一个很小巧但是对移动端开发非常有用的库——不知道作者搞什么,npm modules 上发布的版本从来不更新,逼得人用 Bower。
我用 APP_NAME
指代当前项目,MODULE_NAME
指代目标模块,EDITOR
指代使用编辑器编辑文件,FILE_PATH
指代目标文件路径。另外如无特别说明,当前目录一概假定为项目的根路径。不再赘述。
$ bower install MODULE_NAME --save
$ EDITOR ember-cli-build.js
// before `return app.toTree()
app.import('./bower_component/MODULE_NAME/FILE_PATH'[, options])
options
是一组可配置的参数,它的诸多用法可参照 Ember CLI 文档的 Managing Dependencies 一节。其中要重点说一下 options.prepend: true
,这个参数可以让被 import 进来的依赖文件追加到 vendor.{js,css}
的前面,这在很多时候都是必要的。比方说你要使用 normalize.css
,它就应该追加到 vendor.css
的最前面才能发挥应有的作用。如果有多个文件都要 options.prepend: true
,那你就要小心安排 import 它们的次序了。
一般来说,一个 Ember App 只需要最终构建两个样式文件和两个脚本文件:APP_NAME.{js,css}
和 vendor.{js,css}
,这些文件默认都已经引入在 app/index.html
文件中了。然而很多人都喜欢按照一定的规则把样式/脚本拆分成多个子文件,然后希冀于“按需加载”并以此来获得预想中的“快速加载”体验,其实对于 SPA 应用来说这么做反而是得不偿失,理由有三:
document.write
“秘法”实现伪按需加载是没有道理的,且不说 document.write
已经被打上了 bad practise 的标签(现代浏览器会警告你),就是每次路由跳转都要等待零零碎碎的请求和加载就会影响用户的体验,你可能不得不定制 loading state 来粉饰背后的“龌龊”,而且也使得离线应用特性变得名不符实;说到 Ember Engines,由于我还没机会去在正式产品中应用它,所以我估计这次是没法介绍太多了。明年 EmberConf 将会是 Ember Engines 和 Ember FastBoot 正式登场的时候,我希望届时再开专坑来填。
因此我个人的建议是不要刻意去搞什么按需加载,为了按需加载你可能要在 UI 和体验上做很多妥协来弥补不成熟技术的缺陷,注意:不是所有的应用程序都需要追求极速的页面加载的,这种追求通常对于内容型网站会比较有意义,因此它们多采用服务端渲染,而工具/服务型应用的加载等待只要不是太过分,用户是完全可以接受的。与其绕很多弯路,不如去追求更好的用户体验和引导,比如说我们可以学习一下 Slack 是怎么处理用户等待加载的。
另外在现阶段,我们也可以在工程方面多注重一下控制依赖的体积,尽量避免盲目的使用大堆的第三方模块。说到体积,我估计多数人都认为 Ember 在物理体积上很庞大吧?给大家看两张截图对比:
上图是一个新建 Ember 项目的构建结果,下图是我自己的 Univera 脚手架(基于 webpack 的 React+Redux+SSR)的构建结果,单从数字比较 Ember 多了 1/3,但需要说明的是:
问题是:为什么 Ember 会这么大呢?原因是 Ember 提供了非常非常多的“宝石”,看看它的 API 文档就知道了,即使你使用 Ember 框架好几年,你依然会觉得很多东西都没有学会怎么去用;但是另外一个角度来看,有很多潜在的问题也因为 Ember 而解决在无形之中。举个例子,如果你要在 IE 9 使用 Function.prototype.bind 你必须要自己添加 es5-shims,而 Ember 内置了增强版的 Ember.run.bind 等等。
不过也有很多时候我们并不需要默认提供给我们这么多东西,也许我们更希望能够自己挑选需要的部分,这是一种非常合理的诉求,只是因为从前“模块化”这件事情是 JavaScript 的痛,Ember CLI 开发的时候也没有 webpack 这样的模块处理机制,所以一直没能成为现实。
Ember 社区当然不会视而不见,最新的一个 RFC 已经将此事提上了日程(实际上已经开始做了),Ember 框架将被会以独立模块的方式用 npm 提供,于是用户可以选择用什么或者不用什么,这将从根本上解决体积相对较大的问题。有兴趣的不妨读读这个 RFC,特别是末尾的附录——你可以看到体积的对比之下,Univera 缺少了太多太多东西。
我还是一样的观点:体积不是最重要的,毕竟我是冲着媲美桌面应用的 SPA 去的,缩减体积并不是最重要的优化因素,而我已经做过大多数人都没有做过的尝试,我深知这里面水的深浅。这个问题在不远的将来将不会再是问题,就此搁笔,何去何从且悉听尊便。
(本章待续……)
ember-runloop
and why it is so important?extend
means put into prototype
which might fool you on testing