对当前单页应用的技术栈思考

@xufei 2016-09-20 05:52:51发表于 xufei/blog

这几年来,各类前端组件化框架层出不穷,江山代有框架出,各领风骚几个月。

回头看两三年前(2014年初)的情况,大致是这样的:

  • Backbone / Knockout,有较大用户量,但已经逐步衰落了
  • Angular1,很火,快速增长
  • React,起步略晚,快速增长
  • Ember,不少人用
  • Polymer,新东西
  • Vue / Avalon,新东西,没有angular那么强的约束
  • RactiveJS,新东西

现在来看,他们的状况分别是这样:

  • Backbone / Knockout,衰落,很少人提起
  • Angular1,停止增长,存量大,大家都觉得过时了
  • React,持续增长
  • RactiveJS,平缓
  • Polymer,衰落趋势
  • Ember,平缓

这里面,最大的冷门莫过于Polymer的衰落。Polymer的衰落意味着Web Components的受重视程度下滑。

原先,人们需要Web Components,是因为觉得在大型应用做组件化方案的时候,缺少一些东西,主要是一些逻辑和样式隔离,但这些事情,被用工程手段解决了,主要的贡献方是React和Webpack,它们用构建的方式,让逻辑和样式在构建之后,互相不影响,从而变通解决了这个问题。

在此之前,人们很少会觉得前端的编译、构建是这么必要,但现在,大部分人都觉得这么做理所当然,所以前端开始配置环境,编写实际上在浏览器中还未必能直接运行的ES新特性,给它们添加函数式编程的特性,添加基于类的面向对象模式等等。

如果一个3年前的前端穿越过来,他很可能会像清朝末年内地的人到了1920年的上海,瞠目结舌,不知所措,而从传统软件开发者(桌面端或者移动端)角度来看,Web前端开发终于走上正规道路了。

另外两个冷门,莫过于Angular1的停止增长,React的如日中天。

3年前,Angular1击败Backbone和Knockout的原因,是它的开发效率。无论是Backbone,还是Knockout,他们的数据模型都需要层层包裹,更新数据不能使用原生方法,而是必须使用包装过的方法。而Angular使用POJO,可以不必对数据进行包装,所以代码会简洁很多。这个时代,人们的主要关注点是数据驱动的界面。

Angular1虽然当时很火,但还是存在不少缺陷的,主要在于组件化理念的不强势,复杂数据变化的难于追踪等,还有一些前端领域不特别必要的理念,而React在这些方面是有所作为的,所以吸引了不少的用户。但在我看来,React能这么火的最大原因还是其工程化配套设施非常强大,而其他相比Angular的亮点,往深了看,仍然有很大改进余地。

此外,Vue也取得了很大的增长,能取得这么大增长的原因包括:在Angular1停止增长的同时,有很多人对React那套东西不完全认同,而Vue在很多方面,能满足这个群体的要求;Vue自身充分拥抱工程体系,也从React吸收了很多优点。

前几天,Angular2也发布了正式版,细节不评论了,在上次的回答里已经写得比较多。https://www.zhihu.com/question/50666914/answer/122280198

除此之外,还有几个值得关注的东西:

以上叙及,都是侧重视图层,当视图层的组件化做大、做深以后,除了其中少数框架,都需要额外借助某些数据层方案,以达到让数据流转方向单一,容易收敛的目的,比如React的Redux,Mobx,Vue的Vuex,Angular2中的RxJS等,另外一些框架比如CycleJS,从底往上彻底依赖RxJS这样的东西,有自己独特的一种方式去实现整个应用,所以无需借助额外的库和理念。

如何使用一个框架,很大程度上取决于我们的业务场景,常见的场景中,数据层并不会很复杂,例如各类控制台,所以在这类情况下,不引入数据层辅助机制也可以做得很好,使用Redux之类反而有自找麻烦的嫌疑。https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367#.ww12q7y31

当组件层级较多的时候,可能会需要在组件通信的时候引入一些东西做转发,在这个方面,大家争议并不多,有不少React阵营的人会在单个组件内部也使用Redux,在这个点上,我有些保留。

这段时间我在RxJS上面作了不少思考,在数据层比较重的场景下,使用它会是一个很好的选择,然而,也会面临一个问题,这样构建的数据层,它与组件结合的那个位置应当怎么写代码?

如果参阅CycleJS的那套理念,它的上层也是完全使用Observable去处理DOM事件,界面更新之类,对此我也不能完全认同,这样开发效率还是偏低。在我的理想中,由RxJS构建的下层数据层,加上轻量MVVM视图层组件可以比较平衡地达到理想状态。

越是函数式,或者Reactive的东西,在数据层能起到的作用越大,但是在视图层则未必,因为视图层是最讲开发效率的地方,如果在这个层面也大量应用函数式的理念,实际上是加大了思考负担,因为这里可能导致函数不纯的东西太多了,而实际上大多数都不会影响结果,反而会让一直使用函数式编程的开发者面对很多干扰,陷入不必要的纠结:“我这里不纯,是不是不对,要不要想办法改成无副作用?”

而如果把这块放心地交给MVVM框架,就可以免去很多这种麻烦。我们可以尽可能地去把数据处理过程剥离到函数式的管道中,而仅仅是最后一步与视图的关联交给框架去自动处理,当框架触发一些事件的时候,它再反过来调用到这些函数式管道,就可以在数据正确性和开发效率之间达到一种取舍平衡。

实践的时候,有不少地方要考虑的。