聚合国内IT技术精华文章,分享IT技术精华,帮助IT从业人士成长

一些前端框架的比较(下)——Ember.js和React

2016-12-29 01:15 浏览: 3256026 次 我要评论(354 条) 字号:

一些前端框架的比较(下)——Ember.js和React

这是前端框架比较和吐槽的第二篇。

Ember.js

Ember.js的extend的写法很类似于JQuery或者是Backbone.js,创建Application,然后在它下面创建相应的Model(Object)、Controller、Router、View和Template,这些都是非常类似的。但是它更为先进的地方在于,一些重复的样板代码,比如给template注入上下文并渲染,如果命名按照CoC的原则正确完成的话,都由框架自动完成,这就省去不少体力活。CoC还体现在URL mapping上面,比如”/books/book_id”配置在books.index的Router里,Controller就是BooksIndexController,Router就是BooksRouter,Template就是books/index。

除了CoC这个亮点,在解耦方面,Router里面设置区分里resource和route的概念,既清晰,又简洁。属性绑定是另一个和Backbone.js比强化了的地方,依然遵照CoC的原则,如果属性以Binding结尾,绑定属性就自动创建,而计算属性则(方法的输出和某些属性之间的依赖关系)使用property方法来显式建立关联。事件方面使用observes方法并传入属性名来建立监听,其实和Backbone.js是差不多的。

我拿它不知不觉地和Backbone.js比较,最初还是因为API长得像的关系,后来才知道,其实这并不奇怪,因为核心开发人员Tom Dale说,灵感就是来自于Cocoa、RoR和Backbone.js,有一些特性则怎么看怎么像语言特性,而不是框架特性,比如Mixin。该文中他自己比较了AngularJS和Ember.js。当然,这篇文章的题目是《Is Angular.js or Ember.js the better choice for JavaScript frameworks?》,自然少不了对AngularJS的吐槽(注:下面括号里的内容都是我的补充,并非来自Tom):

  • 比如讽刺AngularJS是一个“by Google”的项目,而不是一个真正的“社区项目”;
  • 比如AngularJS的HTML属性绑定的形式过于“noisy”,而且难读,而Handlebars的表达方式更好(比如模板里面使用{{#each}},而不是搞一个ngRepeat的HTML属性);
  • 比如基于字符串的模板(对比Angular基于整棵DOM树上绑定属性实现)有诸多优势:预编译,不需要遍历整棵DOM树;
  • 比如在服务器上渲染应用的话,Ember.js不需要启动整个浏览器环境;
  • 比如使用模板方式对惰性加载的支持;
  • 比如AngularJS众所周知的dirty checking的性能弊端(要知道,和AngularJS的双向绑定相比,Ember.js不仅支持双向绑定,还支持“Data Down, Actions Up”的单向绑定)。

最后还说,1.0版本已经lock down了,以后不会有大的变化的(这明显是在含沙射影那个谁啊~,不过1.0之前它自己也不怎么样)。

他虽然那么显摆Handlebars的好处,但是它其实也有一些明显的缺陷,最大的就是表现力上,我一开始被AngularJS震撼的地方一个是双向绑定,一个就是扩展了的DOM,清晰而且解耦,相较而言Ember.js的这部分代码就容易显得啰嗦(代码表现力上,总体来说,用AngularJS的时候我能感觉到自己是时而做设计,时而写逻辑,但是用Ember.js的时候总觉得要么在捣鼓表达式,要么在折腾API);再有就是DOM对象过度创建的问题,有许多标签的实现都是靠最后创建辅助DOM对象来实现的,最后就形成一大堆对页面加载和问题定位不怎么有帮助的臃肿的累赘们。解决方案是使用别的模板引擎来替代Handlebars(比如HTMLBars)。

Ember.js的社区发展旺盛,生态系统也非常完备。事实上,Ember要解决的问题(或者说野心)是一个大问题,是而不只是一个简简单单的框架问题。比如说其中的Ember Data是不得不提的,它做给数据模型层做了非常好的封装,和Node.js一起使用,通信API的部分,传输数据序列化的部分,都不用关心(实现遵照JSON API),把注意力放在它往上的逻辑上就好了。再有一个是Ember CLI,从这个就可以看出,它想要解决的工程的问题,比如创建代码样板和配置各种依赖插件,而不仅仅是前端技术的问题。最后,想想Ember.js自己的口号——“creating ambitious web applications”,“ambitious ”这个词是在确切不过了。

不过总的来说,Ember.js还是一款比较复杂的框架,即便因为CoC的关系,配置和使用的代码量不大,学习门槛依然无法避免地比较高,甚至比AngularJS更高。另外,在选型的时候评估一个技术需要把明确依赖的插件扩展等等一股脑统统考虑进去,这些加起来就很大了。

最后,推荐一下这个Quora帖子,已经变成了AngularJS和Ember.js两派有理有据地“有脑”撕逼现场了,非常值得一读。

React + Redux

这是我比较感兴趣的部分,让我多费一点口舌。

React带来了诸多编程范型的融合,从JSX往大了说,本身声明式语言和命令式语言本身就像是天生的冤家,很少能被放到一起的,但是在React中我们看到了;在往细了说,像函数式编程等等风格都可以见到,尤其是它可以和携带语言层面特性的许多lib很好地融合,比如可以使用Promise,可以藉由它们的力量把一大堆回调拉平,写代码的自由度很高。JSX(JSX其实并非必选,完全可以用传统操作DOM风格的代码来操纵DOM,但那实在是舍近求远了)让以往需要单独考虑的模板层面的代码组织,比如重用、解耦、引入等等,现在只需要在传统的JavaScript层面考虑了,和别的代码没有什么区别;再一个,以往间接的往模板传参和需要独立上下文(栈)来实现的状态保存,都变得直接而且简单。

说到状态,React引入的状态机机制,即通过事件监听来更新状态(setState),从而自动调用render来渲染组件的方式,也实现了绑定。

我认为React本身的难度曲线是比较低的,尤其是和Ember.js等等这些“充满野心”的大块头比起来,自己定位清楚,它本身更多地贡献在View这一层的丰富表达上,单纯得很。但是,也要看到技术栈不同层面的扩展也非常火热。看起来低调,React在干的事情是要革命,革了传统前端开发的命,比如JSX是要干掉HTML的,React Native是要取代诸多终端适配的解决方案,Reactor-Router是要替代各种URL Mapping的……最后,终于发现他低调潜藏着的野心,他要侵入整个技术栈,它要形成一整个“体系”。

紧接着必须要提Redux,因为上面说了,React更多的贡献是在View上面,本身并非一个完整的框架,于是Flux跳出来说:“这样吧,我来定义一些pattern以解决这个问题,至于你们爱谁实现谁实现去”,这才有的Redux。有了Redux,它才可以被归纳到框架的范畴里讨论,这也是Redux被放在子标题上的原因。

于是为了谈Redux,先谈Flux,我觉得它最大的亮点在于解决了多个View+Model的状态之间的同步问题。传统的MVC架构中(左图),分层清晰,但是存在的一个缺陷是,如果有多个view,它们都要和同一个model交互,之间的关系错综复杂,于是一致性关系就很难处理,每添加一个view,就要给每一个交互的关系去增加一个逻辑去解决一旦这个view的数据变更,要引发相关联的model和view改变的问题,这完全不是“对修改关闭”了,不符合开闭原则(两图都来自这个Facebook的分享)。Flux(右图)则保证任何view的改变,都统一回归到上游,由dispather去分发给影响到store,然后再rerender需要变化的全部view,可以看到原来双向流动的数据箭头全部变成了单向,这样逻辑就清晰很多,也不需要那么多同步代码。多提一句,这种数据状态和view之间绑定的问题,其实无论是问题还是解决方案都由来已久,最经典的就是CSS在DOM上的绑定,于是数据状态变化的时候,只需要改变CSS的名字,view就可以改变,甚至包括状态之间的转换效果都可以通过这个来完成;而另一个例子是之前提到的AngularJS等其它框架,它们则是用不同的双向绑定也解决了这个问题,各有优劣——下面会看到Flux这种方式的优势。

一些前端框架的比较(下)——Ember.js和React 一些前端框架的比较(下)——Ember.js和React

在这个分享中,另一个让人兴奋的地方在于,Virtual DOM Tree的使用。对于view的更新难免会有大量的rerender,但是是否一点点修改要把整个component全部渲染一遍?Flux构建一个虚拟的dom树,在状态和数据变化完以后,比较新树和老树,找出差异的部分,然后在实际的DOM树上“只更新差异”,从而减少了render的开销。

一些前端框架的比较(下)——Ember.js和React 一些前端框架的比较(下)——Ember.js和React

扯完Flux以后再来看Redux,它很像是吸纳了以上想法的实现,并且改造和拓展了一下。当然有一些Flux的特性它没有采纳,比如“dispatcher”,因为有了纯函数式的reduce方法来计算状态;再比如Redux是私自默认你只会使用不可变对象,而不会擅自改变其中的状态的。它自己号称自己是“可预测的状态容器”(predictable state container),不过我们通常认为React+Redux就可以成为一个好用的框架了。为什么说“predictable”,是因为当状态变化和异步同时发生的时候(“mutation”和“asynchronicity”),整个系统中的状态和状态一致性是很混乱的,但是Redux就是要解决这个问题,把这些状态和状态的变化变成再编程过程中可以预测的:

  • 只有一个数据源(store);
  • 状态是只读的(数据流动的单向性:只能通过action去改变);
  • 只能通过纯函数(reducer,而不产生任何外部影响)来获取新状态。
关于React+Redux这套组合拳的打法,牛逼已经吹出去了,但是争议的地方也不少。比如对于通常的没有那么多从model到view交叉耦合的应用,这个解决方案有杀鸡用牛刀之嫌;在讨论中也有人担心virtual DOM tree对内存过度占用的风险,而且这种immutable的存储,以及新树和老树的比较,就像编程语言中对不可变对象的使用一样,在一些情况下会有性能的问题;函数式编程的思维对于很多人来说并不容易转变,因此代码往往远非最佳实践;如果要因为动画效果而维护状态的话,问题就更多,比如状态变化过于频繁,比如对于动画开始结束的回调方法会把状态耦合到UI去……

结束语

写到这里,对于GWT、AngularJS、Backbone.js、Ember.js和React+Redux这几款常见的前端框架的比较和吐槽结束,它们都常用且具备代表性,我认为学习的价值是显著的。其实看看这些技术们自己吹牛逼(不要光看第三方撕逼嘛~)也是一件乐事,我来尝试戏虐地总结总结它们:

  • GWT说,人类的最大的问题,也是我要来解决的问题是,你们这帮Java狗的前端技术太屎;
  • AngularJS说不对,最大的问题不是人的问题,而是代码和绑定本身的问题,没有表现力,啰嗦无比;
  • Backbone.js说其实还是把有限的精力放到解决从RESTful API的调用到view的模型生成这一个流程上比较靠谱;
  • Ember.js说太幼稚,世界是你们的,世界是他们的,但世界早晚是Ember.js的。我有你们的一切,我还有一颗充满欲望的心;
  • React说,随你们怎么说,我知道一切状态,我可以预测未来。

至于其中的唠叨,不只是客观事实,个人口味也非常重,而且限于水平,当有许多不当和争议的地方,欢迎指正。至于其中的后两款我还有更深入的学习计划,会在以后记录更多的思考和讨论。其实还想涉及Ext JS和Dojo Toolkit,不过有了上面这些以后,它们的典型性就没有那么强了,而且时间所限,就先作罢吧。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接



网友评论已有0条评论, 我也要评论

发表评论

*

* (保密)

Ctrl+Enter 快捷回复