2017前端框架何去何从

>这篇文章将从 AngularJS ReactJS Polymer 这几个流行的框架入手,分析前端框架在这几年发展中的关键技术点,做为2015前端技术选型的参考。摘要:html

- 初体验
- 技术特色
- 组件化
- 应用架构前端

### 总结vue

**1. 初体验**
拿TODO来做为引子好了.
         ![](//upload-images.jianshu.io/upload_images/8373224-4e10488b2196f18d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
Angular 的实现
![](//upload-images.jianshu.io/upload_images/8373224-5966342b1a65597b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
React的实现(非flux架构)
![](//upload-images.jianshu.io/upload_images/8373224-fdd1c5436dfee33e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
Polymer的实现
![](//upload-images.jianshu.io/upload_images/8373224-e39a00c0f65ac2e8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
三者共同对比
![](//upload-images.jianshu.io/upload_images/8373224-2e20982ca8e05655.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
在Angular中有controller和component的概念是分离的,而react和polymer中只有component的概念。
实际上三者在最简单的使用场景下差别并不大,Angular和polymer模板和代码分离的方式更贴近于传统的前端作法,而React写法更像后端渲染。关于学习和使用成本的谁高谁低得问题没有什么好争论的,在MVVM已经流行了这么久的状况下,三者入门门槛都差很少,但要用好都须要深刻其中的运行机制才行。react

**2. 技术特色**git

实际上所谓的MVVM框架的关键技术就一个:数据与视图的绑定。在Angular/polymer/knockout/vue/avalon 中,这项技术的实现又能够拆分红两个关键点:模板分析和数据监测。
模板分析的主要目的是对 {{title}} 这样的标记进行收集。收集完成以后生成一个视图更新函数,在函数内部保存着这个标记所在的Dom片断和相关的数据名称,函数被调用时会去从新取数据名称对应的数据(或者由外部将相应的数据做为参数传入),而后更新dom片断。这样就实现了视图的更新。通常框架会在启动时就将模板分析完,生成相应的视图更新函数。当数据更新的时候,就调用这些更新函数来更新视图,那么问题来了,如何检测数据的改动?
knockout/angular/avalon表明了三种方案:github

- 使用自定义的数据对象及其指定的get和set函数。例如你只能使用 user.set("name","john")来给user对象的name属性赋值,由于这样它才能在set函数中知道修改了什么属性,而且只调用相应的视图更新函数。这种方式不太爽的地方在于改变了原有的JS对象使用的方式。
- 使用 Object.defineProperty 的get和set函数来检测对象属性的改动,本质上和上种没有什么区别。可是它有一个缺陷,就是没法检测新增的或删除的属性。有的框架是经过Object.observe来补充这种方案的,不过Object.observe 目前也只有chrome支持。这种方法改良了上面的开发体验,你能够像使用原生JS对象同样来操做你的数据。可是在实现上较为复杂。
- dirty check。这是angular正在使用的机制,它并不能像前两种同样一旦数据发生变化当即触发更新回调。而是必须在调用了angular提供的一些方法,或者触发了页面上使用了ng-click等的元素上的事件后才会触发。这些触发时机是angular内部就已经实现了的,因此你几乎感受不到。这种方法被称为"dirty"的缘由是,它保存了全部属性上一次的值,检测是经过遍历对象的全部属性,对比它和上一次值是否同样来实现的。若是是深层对象的话,它会层层遍历。这种检测方式结合了上面两种的优点,可是对性能形成了负担。web

至此,两个关键技术点都已讲清楚,用一张图来回顾一下
![](//upload-images.jianshu.io/upload_images/8373224-7c3f8defccefd595.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
而在React中则相对简单,React用的是相似于重绘的机制,当触发了 setState 以后,就彻底从新渲染(并不是当即触发,中间有相似于缓存的性能提高机制)。这看起来比起前面的方案简单粗暴,可是却由于virtual dom的实现化腐朽为神奇了。virtual dom指的是React内部用来模拟真实dom的一种数据对象。当从新渲染时,其实是先生成这样virtual dom,而后将其和上一次的virtual dom进行对比,找出差别,最后由react在真实的dom上更新有差别的部分就够了。由于virtual dom始终在内存中,真实的dom操做很是少,而前面的几种框架在更新视图时经常会有大量的dom操做,所以react在性能上大大领先前一种类型的框架。同时也由于virtual dom仍然是标准的 js对象,因此使得"服务端渲染"也成为可能。值得注意的是,虽然React自己并不会像前面的框架同样深刻的去检测数据的哪一部分发生了变化,可是能够经过官方提供的addon 和immutable.js来进一步提升这一块的性能。
(web前端学习交流群:328058344 禁止闲聊,非喜勿进!) ajax

**3. 组件化**chrome

在组件化的方向上 react 和其余几种框架几乎已经分道扬镳了。从 angular2.0的设计和新出的 aurelia 等框架中能够看到你们都在尝试往 webcomponent 靠近。polymer号称下个版本代码将大幅减小,那无非是由于浏览器将实现标准了。靠近 webcomponent 的好处在于任何一个框架都将再也不封闭,以 custom element做为接口层,能实现生态圈的融合。虽然 react 也有封装成 custom element的方案,可是 react 并无很好的调用其余框架生成的 custom element 的方案。"像使用原生dom元素同样使用custom element"的组件使用方式意味着尊重原生的dom使用方式,包括dom的事件等等。这和react"不操做真实dom"的基础已经方向相悖了。
react和其余框架的分歧其实目前看来并没有优劣之分,由于webcomponent目前除了chrome之外其余浏览器支持仍然不全面。另外考虑到特殊国情的话,大公司的产品仍然要面对IE8。不幸的是目前polymer的polyfill最低也只到IE9。而React能无痛支持IE8。再考虑到移动端的浏览器状况的话,也是使用react的技术阻力远小于webcomponent。
整体来看,webcomponent确定会是趋势,而且将促进各个框架变得更加开放,更易互相融合。而react也仍将继续依靠本身在实现上的优点继续走下去。也许将来在这中间又将诞生新东西。
暂时抛开react和webcomponent。咱们继续深刻两个目前讨论得不多可是却很重要的问题(下面讨论的组件问题都以封装成custom element为基础):
- 如何能把组件变得更易重用? 具体一点:
- 我在用某个组件时须要从新调整一下组件里面元素的顺序怎么办?
- 我想要去掉组件里面某一个元素怎么办?后端

- 如何把组件变得更易扩展? 具体一点:
- 业务方不断要求给组件加功能怎么办?

针对第一个问题,我所在的团队目前提出一个叫作"模板复写"的规则,这个规则又分为"彻底重写"和"部分重写"两种规则:
![](//upload-images.jianshu.io/upload_images/8373224-fa4f09d37ea116cc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
 
**部分重写**

![](//upload-images.jianshu.io/upload_images/8373224-3cdbf825598ef138.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这种方案已在angular中实现。而且在组件重用率高的系统中已经验证很是实用。但它也有缺陷,缺陷在于你必须知道当前组件的实现方式和原有模板才能复写。

第二个问题,能够用一种称为"共享做用域"的方式来解决。例如上面的例子中story没有显示like数量,如今要显示出来。常规方案有两种:

- 改组件,在组件中增长这个功能。

- 给组件增长api用于获取统计数据,同时在统计数据发生变化时抛出事件通知外部。

第一种方案可能碰到的问题是当再次发生变化,例如统计数据不要显示在组件里面了。就得继续改为第二种方案。 第二种方案可能碰到的问题是可能不断有新的需求提出来,最后不得不把每个内部状态都暴露出来,每个操做过程都抛出事件。

"做用域共享"共享的方案是: 经过在一个特殊标记 "import-to" 将某一段外部html引入到某个组件中去一块儿参与"模板解析"和"数据绑定",当完成时再放回原来的位置。这样这个外部html就能获取到组件内部任何状态和数据了。这种方案看起来有点像hack,但其实只是换了一种方式来理解组件:组件分红两个部分,一是数据,二是视图。视图理论上应该只受到它的逻辑是否足够内聚的约束,而不该该受到它的子元素是否放在一块儿的约束。可是目前咱们恰好使用了dom做为视图的基础,因此视图受到html结构的约束,这个约束是不合理的。咱们来用图对比一下使用"做用域共享"先后的场景:

![](//upload-images.jianshu.io/upload_images/8373224-cddbe4ace31d7d1e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

固然,这种方案的缺陷仍然是你必须知道组件的具体实现。但这并非一个不可克服的缺陷,咱们看下aurelia的设计,它将template等等关键部分都设计成了可插拔的形式,这种结构意味着将来有可能实现一种通用的模板语法来实现上述两个功能。这样就再也不和底层耦合。

**4. 应用架构**

应用架构的范围太广,咱们这里只讨论那些已经很好地组件化了的应用,或者是没组件化可是有明确层级划分的应用。咱们以React 对应的 FLUX 为切入点。 

![](//upload-images.jianshu.io/upload_images/8373224-5b9215333e5428dc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

咱们再来结合facebook的官方FLUX代码示例来看看每一个部分:

![](//upload-images.jianshu.io/upload_images/8373224-a4d8d0fb2c5ff03a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

facebook在介绍FLUX的时候的主要观点是"MVC扩展性不够,FLUX可扩展性高"。暂且不去讨论FLUX与MVC的区别, 咱们先来它是如何扩展的,从上面的代码中能够看到,ACTION不仅是一个界面上的点击事件所产生的,ajax请求、甚至一个初始化过程均可以产生动做,"动做"只是一个抽象。动做将传递给dispatcher,有dispatcher在去触发store注册的回调。你可能会想,从这个dispatcher实际上什么也没干,这和我直接定义一个方法,触发事件就直接调用这个方法有什么区别?区别在于,当应用增长功能、进行扩展时,应用可能有多个部分要协同对同一个action进行响应,而且不一样的协同部分可能在执行顺序上有严格的前后之分。

> 举个例子

若是我要对上面的TODO增长一个"统计区块",若是是传统的MVC写法,你可能要新增一个statisticModel,而后在controller中的createTODO、deleteTODO中增长代码来操做这个新的statisticModel。而FLUX不用修改已有的任何代码,只须要写新的store,并注册一些回调到createAction、deleteAction中就够了。因此能够看作是将MVC中的 "C主动操做M" 反转成 "M来决定什么时候运行"(固然这种状况也就没有C了), 但更好的是理解成是一种"事件系统"的变种。这就是它和MVC的区别。严格来讲 FLUX 并不能算是facebook"发明"出来的,这样的模型在不少事件驱动的后端框架中很常见,如[zero](https://github.com/sskyy/zero)、[yii](http://www.yiiframework.com/),只不过拿到前端来做为应用架构时比较新颖。

FLUX是目前高度推荐的应用架构方式,它并无强制使用的库或者框架,因此并不局限于react,在angular、polymer中一样能自由实现。特别是目前angular、polymer中的应用开发并无一种应用架构的最佳实践。angular中的模块化既没有异步加载也没有做用域隔离的做用,实际使用时很鸡肋。可是angular中的依赖注入、filter、service的设计很是全面,若是再能加上FLUX的架构的话,威力不容小觑。对polymer来讲状况更简单,应为polymer目前只考虑到element这一层,因此上层的应用架构能够自由实现。
值得补充的是,FLUX中的store,dispatcher能够更好地增强一下。store可使用一些自动支持REST的库来简化开发,dispatcher可使用支持自定义顺序等高级的事件代理实现。

**5. 总结**

2015将是前端框架相互借鉴相互融合的一年,随着webcomponent的落地,你们都在像标准靠近。提早储备这方面的技术确定没有问题。再深刻到框架的技术细节中,咱们看到在"渲染机制"、"数据绑定"、"组件化"、"模块化"这些关键技术点中各个框架中都有很是精彩的实现,值得深刻学习。React异军突起,也推荐持续关注,特别是在"应用架构"上,FLUX确实在整个业界起到了启发的做用,相信会愈来愈流行,而且有愈来愈多实现方式。

相关文章
相关标签/搜索