民工精髓V:AngularJS在苏宁云中心的深入实践

jopen 9年前

摘要:以AngularJS、Vue.js、avalon为代表的MVVM流,及以React为代表的组件流,成为当前前端框架的两种主要思考方式。本文将重点介绍2015年企业在AngularJS上的实践分享。

现状

2013年下半年到2014年,AngularJS作为最热门的前端MV*框架,吸引了很多眼球,但是到了2014年末,它的光辉逐渐被React所掩盖,不再像以前那么“火”了。

在各种技术领域人群中,Web前端与客户端开发者是最时髦的群体。前不久举行的深JS(JSConf 2015)中,来自台湾的开发者赫门用一句精辟的戏谑说明了这种状况:“每18-24个月,前端就会难一倍”。爆炸式的技术增长,层出不穷的新名词让人眼花缭乱,作为追随者,何去何从?

所幸的是,在乱象中,我们仍然可以摸出两条主线,一条是以AngularJS、Vue.js、avalon为代表的MVVM流,一条是以React 为代表的组件流,这两者是对前端框架的不同思考方式,各自吸引了很多追随者。在发展过程中,两边的理念也在互相学习,比如Vue.js也逐渐强化组件的概念。

MVVM流的几个框架中,AngularJS拥有最庞大的周边,但其实,这些框架的很多第三方组件略加修改之后,都可以很容易互相迁移,除了数据变更监控方式的不同,剩下的都只是模板语法的差异了。

相比其他一些流派,AngularJS的使用群体比较低调一些,因为它的大部分使用者都是管控系统的开发人员,也就是传统的B/S软件开发者,有很多之前不是专门做前端开发的,或者是使用类似ExtJS这类框架进行开发的人员。

这两年,各种社区中可以听到很多对AngularJS的反对声音,大部分来自于传统前端开发人员。

有这么一个有趣的例子,某公司有一些后端开发人员听说了AngularJS,想要用它开发一个管理后台,但是害怕自己不能把控,就向前端们提出,希望获得一些支持,而这家公司的前端们之前也没有接触过AngularJS,就同时开始学习。

一周之后,后端们已经顺利地用它做了很多功能了,这让前端们很吃惊,因为他们觉得这个东西很难学。其实,这是由于双方要补充的技能不同所致,要学会AngularJS,需要这样的前置技能:

  • JavaScript中级水平;
  • 具备MV*,分层、注入、模板等知识。

所以,前端要学的时候,需要补充的是第二项,后端学的时候需要补充第一项。如果具有其他语言的开发经验,写出比较规整的JavaScript代码还是比较容易的,而第二项所列的那些知识,一般的后端开发人员早就在比如SSH这样的框架组合中习以为常了,所以他们觉得这一切非常自然。

AngularJS可以视为是一种数据优先的框架,在它的三个层面中,数据模型是骨架,视图模型和业务事件是血肉,视图模板和指令是皮毛,这三层合在一起,就形成了一个活生生的Web应用。

我们在开发过程中,也应当遵循这样的实践,优先从数据模型去考虑问题,然后从展现层去切分模板,最后用视图模型(控制器)把它们粘合起来。

实践

从2014年开始,苏宁的云相关体系逐渐在生产上使用AngularJS,参与人员都不是专职的前端开发人员,比较典型的是苏宁公有云的控制台,这是一个典型的管控项目,管理的是IaaS相关的各类资源。

对这么一个产品来说,它的前端部分可以有几种选择:

  • 基于ExtJS这类框架;
  • 基于jQuery和相关的插件;
  • 基于某一个前端MV*框架。

其中,第一种的操作体验并不很适合在互联网上使用;第二种对整个团队前端相关的技能要求较高,也要有一定的代码组织能力;第三种比较适中,但也需要团队中有人能熟悉这样的框架,掌控全局,其他人的技能要求可以比较低,借助Bootstrap之类的样式库的响应式特性,还能够比较容易让系统在不同设备上可访问。

目前市面上大多数云控制台都采用了单页化的模式,对于这种系统来说,仿客户端的操作体验要好很多。这些产品里,使用AngularJS的是主流,也有比如青云这样的,用的是Backbone。

用单页模式做控制台,比较重要的是这么几点:

  • 路由的管理;
  • 代码的模块化和动态加载机制;
  • 模块之间数据的共享与通信。

在AngularJS体系中,有三个路由库可以选择:ngRoute、ui-router和ngNewRoute,在New Route出来之前,大部分人都会选择ui-router,因为它强大,并且很方便地支持路由的嵌套。

AngularJS本身的模块机制与主流的AMD、CMD等有一定差距,如果要做动态加载,需要使用一些Hack手段。

很多管控系统并不是一系列很简单的管理界面的组合,很可能会存在一些聚合与关联。这时候,就要求我们对数据的共享和通信作一些考虑了。

此外,因为整个系统的单页化,会存在一些全局性的交互方式。比如说,在某个界面上发起文件上传之类的长时间操作,在此过程中,如果允许它切换到其它界面继续操作,上传过程变成在页面边角的一个进度条,然后整个上传过程一直有动态提示,整个交互体验就会比较好。这时候,可以考虑在AngularJS中封装一些service,用于做一些全局统一的交互处理。

使用AngularJS做这类有一定规模的单页应用,是有很多优势的。最典型的优势就是代码量,实现同样的功能,AngularJS可以比Backbone之类的少很多代码量,采用类似架构的Vue.js,avalon也有同等优势。

代码主要省在几个方面:

  • 数据模型是POJO,无需额外封装;
  • 绑定的配置直接写在HTML里,不必在JS层做判断、更新;
  • ng-click,ng-dblclick等事件作了封装,“直接”在模板中“调用”控制器上的方法;
  • AngularJS自身提供的功能足够强,$http,$q,jqLite等内置组件使得我们可以基本不引用第三方库;
  • 由于绑定语法的便利性,原先需要“控件”来解决的大部分问题可以轻松解决,也可以少引用很多第三方库。

此外,可以还用一些小技巧让代码更精炼。

使用成本

我所见过的几乎每一篇文章都会说,AngularJS的学习成本高,确实如此,因为要用好它,需要掌握的东西太多了。但是对于一个团队而言,使用AngularJS的成本并不高,因为团队是可以有分工,有技能搭配的。

比如,同样是做一个项目,一个很熟悉AngularJS的人带着五个只有入门技能的人很顺利做出来,因为用AngularJS的话,可以把代码分得很规整,大部分代码可以是很简单的写法,甚至能够让多数参与者在并不了解AngularJS相关机制的情况下把功能中规中矩地实现出来。但是如果用其他一些框架,可能里面至少要有半数人的基础较好,对个人能力的考验就相对较高了。

在使用AngularJS的项目中,绝大部分JavaScript代码都是纯逻辑,这部分代码可以使用ES6这样很先进的语法编写,然后通过 Babel转换成ES5或者ES3代码执行,熟悉ES6的人会拥有比较高的开发效率。退一步讲,如果团队中存在一些只会使用Java的开发人员,也可以照样写出比较规整的逻辑代码来。

技巧

虽然AngularJS框架本身给我们提供了比较方便的一些功能,也给出了一些Demo,但并不代表我们在实践过程中就应当这样去用。针对不同类型的项目,我们需要有针对性地考虑业务代码的编写方式,主要包括:

代码目录如何划分?

对于小型项目来说,把代码按照controller,service之类分层存放比较好一些,但是对于规模较大的项目,这么做其实是不利于代码管理的。

比较推荐的方式是先按照业务功能做一层目录划分,然后在每个业务目录下再按照分层区分,例如:

    …        order          controller          service          template        user          controller          service          template        …  


另外有一些全项目通用的东西可以另外建目录存放,比如对日期时间、货币的统一格式化filter等等。

组件与模板

在构建应用的过程中,存在不同的实践。有人会倾向于大面积的组件化,也就是把业务模块都封装成指令(directive,也就是自定义标签或属性),然后,整个HTML层会比较清晰。也有人只把“控件”封装成指令,其他的业务模块界面只作为模板来处理。

其实两种都可以用,应当根据团队人员的技能和项目类型来进行。

对于包含复杂交互效果的界面,进行一定程度的封装会比较好一些。平常的简单表单管理之类的,直接用模板比较省事。

如果团队人员平均技能高,可以试试把更多的东西封装成指令,如果技能不高的话,使用模板为宜。

业务逻辑放在哪里?

受官方Demo的影响,很多人会忽视service这一层的代码组织,如果仅仅把这一层当做远程调用的一种简单封装,那是不够的。

无论使用什么框架,数据层都应当单独设计。对于单页应用来说,数据的共享和缓存非常重要,如果不做这个层面的优化,这个应用就还一直停留在初级阶段。

在复杂的Web应用中,建议在service层构建数据仓储,按照业务实体来组织。此外,在复杂的应用中,为实体建立对应的构造函数,也会增强应用的严谨性。

自定义事件的使用

在复杂的单页应用中,模块间的通信不可避免,自定义事件的广泛使用可以减少模块耦合。AngularJS自身的$scope上给我们带了向上向下两个方向的事件传播机制,但在业务代码层次变深的时候,这种传递会变得很啰嗦,所以一般会需要引入全局的事件派发机制,这也就是所谓的 EventEmitter或者EventBus。

我们可以建立一个service专门用于做全局事件的管理,在里面实现一个全局的事件订阅发布机制,并且在使用的时候用完整业务路径来命名业务事件。

尽可能重视测试

在使用AngularJS的时候,我们可以遵循最佳实践,把代码分层,一层一层逐步构建。这样,每一层都是非常模块化的,并且是单向依赖,非常适宜于单元测试。

如果条件具备,可以进一步在这个基础上做场景测试,甚至做界面发起的自动化测试。测试做到哪一步,用什么方式做,需要视系统与人员的配比状况而定。在有条件的情况下应当尽可能多做测试,这对系统的稳定性有很大帮助。

未来

2014年末,AngularJS官方披露了2.0版本的一些细节,各种激进和不兼容的变更在整个社区引发了轩然大波。变更的原因无非是迎合标准,并且解决自身的一些历史问题。

对于开发者来说,将来如何把在用系统迁移到新版本,可能会是一个很重要的考虑点。

应对这个事情,最重要的是保持数据逻辑层面的清晰纯净,尽可能在这一层不依赖于框架的一些特性,然后在迁移的时候,这个部分的改动代价就会很小,大部分的变更都体现在模板层了。

总之,AngularJS框架现在已经进入成熟期,1.4版本大幅优化了性能,也提供了新的路由机制,我们完全可以放心地把它用在以各类管控系统为主的Web应用中,以强大的开发效率去快速推出产品。

作者简介:

徐飞,网名民工精髓V,目前就职于苏宁云计算中心,有十年以上大型企业应用前端架构及开发经验,熟悉AngularJS等框架,对Web组件化有一些思考。博客地址:https://github.com/xufei/blog/issues


来自:http://www.csdn.net/article/2015-08-24/2825527