这篇是我参加QCon北京2014的演讲内容:html
企业应用在软件行业中占有很大的比重,而这类软件多数如今也都采用B/S的模式开发,在这个突飞猛进的时代,它们的前端开发技术找到了什么改进点呢?前端
B/S企业软件前端开发模式大致上与桌面软件相似,都是偏重量级的,在前端可能会有较多的业务逻辑,这些业务逻辑如何被合理模块化,与界面分离,以便测试,成为这个领域的一个重要挑战。另外一方面,因为企业应用的界面相对规整,偏重的是数据存取,没有太多花哨的东西,因此常见的界面控件也是可枚举的,如何让开发界面的工做能更快完成,甚至由不擅长编写代码的业务设计人员来作,与界面原型的工做合二为一,能提升很多开发效率。node
在AngularJS等MV*框架出现以后,给这个领域带来一些契机,架构师们可以有机会去从新规划前端的架构,甚至是开发流程,从而让整个软件的生产更为高效。git
本文将探讨它给这个领域带来的变化。 github
企业应用系统是一种很常见的软件系统,这类系统的特色是面向某个行业,功能较复杂,对界面的要求通常是整齐,不追求花哨。这类系统一般有C/S和B/S两个流派,其中的B/S方式由于部署和集成的便利,使用得较为广泛。npm
一样是在浏览器中作东西,写企业应用和网站的差异也很明显。企业应用的业务逻辑较重,前端有必定的厚重性,可是对效果并不追求不少,主要是各种控件的使用,表单的存取值等等。gulp
企业应用产品的一些特色以下:后端
通常用户使用互联网产品,都是片断时间使用,好比购物或者阅读,作完以后就刷新或者关闭浏览器了,而企业应用每每是工做的所有,从早上上班开始打开,到下班才关掉,一天绝大部分工做都在上面完成,好比一个呼叫中心的操做员。设计模式
企业应用对视觉的追求是比较低的,通常不会要求花哨效果,以业务操做的流畅性为第一目标。浏览器
企业应用的界面布局相对有模式可循,能够用不多的场景来穷举,界面横平竖直,比较规整,使用到的控件元素也是可穷举的,基本没有什么特效。
因为企业应用的用户都相对比较专业,在上岗以前须要通过统一培训,并且每一个用户使用的频度较高,不少时候他们会用尽可能快捷的方式来作操做,好比键盘,这一点在互联网产品中比较少见。因此,有时候你们为了追求好看,把系统原生的select用div来替换,在这种状况下反而增长了用户的麻烦。
我以前所在的行业中,业务逻辑很复杂,前端可能会须要写不少复杂的逻辑,JS代码大部分是在处理逻辑,而不是界面交互。
互联网产品每每很重视首屏优化,可是其策略可能与企业应用不一样。好比说,3个200k的模块,在网站型产品中可能优化成一个100k加三个150k的模块,但在企业应用中,极可能优化成一个400k加三个50k的模块。为何会这样呢?由于内容型的网站讲究的优化策略是分摊,若是首次加载太慢,会很影响用户的信心,但企业应用用户的容忍度是较高的,他并不在意刚打开的时候慢一些,由于打开了以后就要用一天,对于以后每步操做的模块加载速度却是要求很高。另外,对于内存泄露的处理,也要求得比较高一些。整个这些策略,实际上是来源于C/S系统的影响。
不少时候提到企业应用,你们的想法就是低端,IE6,但其实这个的缘由是客户只购买软件,运维通常本身作,每一年不会有不少持续的投入来改进,因此致使不少老系统不能持续升级。软件厂商其实反倒能够用更激进的策略去升级浏览器,用户对这个的接受度仍是比较高的,使用系统的群体也是比互联网用户小不少的,抛弃老旧浏览器的事情也确实能够干,好比我就见过几年前某电信营业系统预装的都是Firefox。
在开发B/S企业应用前端的人群中,有很大一部分群体选择了服务端的组件化方式,好比JSF之类,它的弊端是与异构服务端的第三方系统集成比较麻烦。也有很多人使用Bindows和ExtJS这样的框架,最近的KendoUI也是个不错的选择。
每种类型选一个有表明性的来讲说:
早期有些团队采用的方式,通常会跟XMLHTTP等结合使用,易于使用,界面代码整洁,但已被主流浏览器抛弃。
之后端为主的架构师最推崇的方式,受Struts的MVC模型影响很深,弱化了前端,使得前端蜕化为后端的一种附属。
写其余语言来生成HTML和JS,通常会依赖于一种前端UI库。这种方式也比较受后端架构师喜欢,由于他们以为写JS很头疼,宁肯写Java。
这是另一种极端,从Bindows开始,使用纯逻辑代码来描述界面,走着跟Java Swing同样的道路,也有很多人喜欢。但这种方式在没有好用的界面设计器的状况下很是痛苦。
这条路实际上是对Java Applet的一种延续,好处是能够不受HTML体系的制约,独立发展,因此其实这些体系在企业应用领域的成熟度远超HTML体系。
有一段时间,咱们几乎只有IE6,因此那个时候的前端开发人员很快乐,没有兼容的压力。那时候,咱们如何构建前端应用呢?
参见http://weibo.com/1858846672/B1fL3vuYN?mod=weibotime
这是最好用的声明控件的方式。
尽管尚未AJAX的概念,但咱们已经能够用它作先后端分离的传输机制了。
在IE里面画矢量图,不使用插件,有其余选择吗?
把XML数据转换成HTML,跟如今的前端模板像吗?
建立右键菜单最好的方式。
当时这些系统的构建方式也能够算单页应用,咱们用iframe来集成菜单,每一个菜单有本身独立的功能,整个主界面是始终不会刷新的。
时光飞逝,这些年,前端有了什么本质的改变,产生了翻天覆地的变化吗?
有时候咱们回顾一下,却发现多数都是在增长完善一些细节,真正有颠覆性的有好比以RequireJS和SeaJS为表明的模块定义和加载库,npm这样的包管理器,grunt,gulp,百度fis这样的集成开发模式。为何它们算是本质改进呢?
由于这些标志着前端开发从粗放的模式,逐渐变化到精确控制的形态。好比咱们不再能无论代码的依赖关系,也不能一打开界面就不分青红皂白把全部可能要用到的代码都马上加载过来,那个时代已通过去了,从任何角度讲,现代的前端开发都在精细化,从代码的可控,到界面体验的精细优化,到整个团队甚至公司甚至互联网上的组件共享,以及前端团队协做流程的改进,这已是一个很成规模的产业了。
咱们把眼光放到2013年,在这一年里最火的前端技术莫过于NodeJS和AngularJS,前者给咱们带来的是一种开发方式的改变,后者是一种典型的前端分层方案。Angular是前端MV*框架的一个流派,用过的人都会以为很爽。它爽在什么地方呢?由于它帮咱们作的事情太多了,一个双向绑定,无所不包,凡是存取值相关的操做,基本都不用本身写代码。在企业应用前端功能里,表单的存取值和校验占据了很大的比例,这些事都不用干了,那简直太好了。
若是就由于这个用Angular,那还有些早。有一些第三方代码被称为库,另一些称为框架,Angular是框架而不是库。框架的含义是,有更强的约束性,并不是做为辅助功能来提供的。
先看一下企业应用的一般形态吧,会有一个可配置的菜单,而后多半会采用MDI的形式,能打开多个业务功能,用选项卡的形式展现起来,能够随时切换操做。每一个人天天经常使用的功能是能够穷举的,他进入系统以后,通常要用到下班才关掉。因此这种系统很是适合作成单页应用,开始的时候加载一个整体框架,每点击一个菜单,就加载这个菜单对应的功能模块,放在一个新的选项卡或者别的什么地方展现出来。
在早期作这种系统的时候,通常都会用iframe来集成菜单,这种方式很方便,可是每一个菜单页都要载入共同的框架文件,初始化一个环境,数据之间也不能精确共用。
因此如今咱们作企业信息系统,再也不适合用iframe来集成菜单,全部菜单的业务代码,会在同一个页面的做用域中共存。这在某些方面是便利,好比数据的共享,一个选择全国城市的下拉框,在多个功能中都存在,意味着这些城市的数据咱们能够只加载一次。但从另一个角度来讲,也是一种挑战,由于数据之间产生干扰的可能性大大增长了。
咱们回顾一下在传统的客户端开发中是怎么作的,早在经典的《设计模式》一书中,就提到了MVC模式,这是一种典型的分层模式。长期以来,在Web开发人员心中的MVC,指的都是Struts框架的那张图,但咱们单页应用中的MVC,其实更接近最原始的《设计模式》书中概念。因此咱们要在前端分层,而不只仅把整个前端都推到视图层。
作单页应用,前端不分层是很难办的,当规模扩大的时候,很难处理其中一些隐患。分层更重要的好处是可以从全盘考虑一些东西,好比说数据的共享。跨模块的数据共享是一个比较复杂的话题,搞得很差就会致使不一致的状况,若是考虑到在分层的状况下,把各类数据来源都统一维护,就好办多了。
因此,以AngularJS为表明的前端MV*框架最重要的工做就是作了这些对于分层的指导和约束性工做,在此基础上,咱们能够进一步优化单页应用这类产品。
构建一个大型企业应用,最重要的是创建整套组件体系。通常针对某行业的软件,长期下来都会有不少固定的模式,能够提炼成组件和规则,从前端来看,体现为控件库和前端逻辑。控件库这个是老生常谈,在不少框架里都有这个概念,但各自对应的机制是不一样的。
从写一个界面的角度来说,最为便利的方式是基于标签的声明式代码,好比咱们常见的HTML,还有微软的XAML,Flex中的MXML等,都很直接,设想一下在没有可视化IDE的状况用相似Java Swing和微软WinForm这样的方式编写界面,毫无疑问写XML的方式更易被接受。因此,咱们能够得出初步的结论,界面的部分应该写标签。
很遗憾,HTML自带的标签是不足的,它有基本表单输入控件,可是缺少DataGrid,Tree之类更富有表现性的控件。因此绝大多数界面库,都采用某种使用JavaScript的方式来编写这类控件,好比:
<div id="tabs"> <ul> <li><a href="#tabs-1">Nunc tincidunt</a></li> <li><a href="#tabs-2">Proin dolor</a></li> <li><a href="#tabs-3">Aenean lacinia</a></li> </ul> <div id="tabs-1"> </div> <div id="tabs-2"> </div> <div id="tabs-3"> </div> </div>
$(function() { $( "#tabs" ).tabs(); });
若是这样,这些复杂控件就都要经过JavaScript来建立和渲染了,这与咱们刚才提到的原则是违背的。那咱们寻找的是什么呢,是一种能扩展已有HTML体系的东西。在早期,IE浏览器中有HTC,能够经过引入命名空间来声明组件,如今的标准浏览器中又引入了Web Components,在Polymer这个框架中能够看到更多的细节。说到底,这类方式要作些什么事情呢?
从另一个角度讲,为何咱们非要这么作不可?最大好处来自哪里?对于大型项目而言,管理成本和变动成本都是须要认真考虑的。若是一个组件,须要在DOM中声明一个节点, 而后再用一个js去获取DOM,把DOM渲染出来,再填充数据的话,这个过程的管理成本是很大的,由于HTML和JS这两个部分丢了一个都会有问题,不管在何时,维护一个文件老是比维护多个文件要强的,咱们看HTC那种方式,为何它的使用成本很低,由于它能够把控件自身的DOM、逻辑、样式所有写在本身内部,整个一个文件被人引用就能够了。在如今这个阶段不存在这么好用的技术了,只能退而求其次。
因此,在这个点上,Angular带来的好处是可扩展的标签体系,这也就是标签的语义化。Angular的主打功能之一是指令,使用这种方式,能够很容易扩展标签或者属性。好比,业务开发人员能够直接写:
<panel> <tree data="{{data}}"></tree> </panel>
这样多么直观,并且能够跟原有的HTML代码一块儿编写,不形成任何负担。语义化的标签是快速编写界面的不二法门。
有了语义化标签以后,若是咱们只写界面不写逻辑,那也够了,但现实每每没有这么美好,咱们还要来考虑一下业务逻辑怎么办。
企业应用通常都是面向某行业的,在这个行业内部,会有一些约定俗成的业务模型和流程,这些东西如何复用,一直是一个难题。以往的作法,会把这些东西都放在服务端,用相似Java这样的语言来实现业务元素、业务规则和业务流程的管理。
这种作法所带来的一个缺点就是对界面层的忽视,由于他只把界面层看成展现,对其中可能出现的大量JavaScript逻辑感到无所适从。不少从事这一领域的架构师不认同界面层的厚度,他们认为这一层只应当是很薄的,纯展现相关的,但在这个时代,已经不存在真正轻量级的界面了。
前面提到,咱们在前端做分层,把展示层跟业务逻辑层彻底隔离,带来的好处就是逻辑层不存在对DOM的操做,只有纯粹的逻辑和远程调用,这么一来,这一层的东西均可以很容易作测试。对于一个大型产品来讲,持续集成是颇有必要的,自动化测试是持续集成中不可缺乏的一环。若是不作分层,这个测试可能就比较难作,如今咱们能把容易的先作掉,并且纯逻辑的代码,还能够用更快的方式来测试。
以前咱们作前端的单元测试,都须要把代码加载到浏览器来执行,或者自行封装一些“无头浏览器”,也就是不打开实际的展现,模拟这个测试过程。这个过程相对来讲仍是有些慢,由于它还有加载的这个网络传输的过程,若是咱们能在服务端作这个事情呢?
咱们看到,最近很火的NodeJS,它从不少方面给了前端工程师一个机会,去更多地把控整个开发流程,在咱们这个场景下,若是能把针对前端逻辑的单元测试都放在node里作,那效率就会更高。
咱们来看看,有了这么一套分层机制,又有了界面标签库以后,该作些什么呢?
作企业软件的公司,有很多会作二次开发平台,这个平台的目标是整合一些已有的行业组件,让业务开发人员甚至是不懂技术的业务人员经过简单的拖拉、配置的形式,组合生成新的业务功能。
从界面的角度看,拖拽生成很容易,不少界面原型工具均可以作,但要如何整合数据和业务?由于你要生成的这个功能,是实实在在要拿去用,不是有个样子看就能够,因此要能跟真实数据结合起来。 但这事情谈何容易!
就好比说,界面上有一个选择所属行业的下拉框,里面数据是配置出来的,对这个数据的查询操做在后端,做为一个查询服务或者是业务对象管理起来,有些传统的方式多是在后端做这个关联,Angular框架能够把这个事情推到前端来。相比Backbone这样的框架来讲,Angular因为有双向绑定,这个过程会变得特别省事。一个界面片断想要和数据关联起来,要作的事情就是各类属性的设置,因此动态加载和动态绑定都会比较容易。
好比:
partial.html
<ul> <li ng-repeat="item in items">{{item.name}}</li> </ul>
main.html
... <div ng-include="'partial.html'" ng-controller="CtrlA"></div> ...
a.js
function CtrlA($scope) { $scope.items = [{name:"Tom"}, {name:"Jerry"}]; }
b.js
function CtrlB($scope) { $scope.items = [{name:"Donald"}, {name:"Micky"}]; }
在上面的例子里,这个列表显示什么,彻底取决于ng-controller="CtrlA"这句,若是咱们把这句搞成配置的,就很容易把数据源换成另一个CtrlB,甚至说,即便在同一版本上作项目化,引入另一个包含CtrlA其余版本的js文件,也基本无需更改其余代码,这就达到了二次开发的一个目的:尽量以配置而不是编码去新增、维护新功能。
如今的企业软件已经不能只考虑PC的浏览器了,不少客户都会有移动办公的需求。响应式设计是一种常见的解决方案,可是在企业应用领域,想要把复杂的业务功能设计成响应式界面的代价太大了,何况界面设计自己就是开发企业软件的这些公司的短板,因此咱们的比较简单的办法是对PC和移动终端单独设计界面,这样就有了一个问题了,这两种界面的业务逻辑并无差异,若是咱们要维护两套代码,代价是很是大的,能有什么办法共用一些东西呢?
若是不采用分层的形式,那这个很麻烦,咱们注意到两种系统的差别只在UI层,若是咱们用分层的模式,能够共用UI层之外的东西。具体到Angular里面来讲,好比service,factory,甚至controller都是能够共用的,只有directive和HTML模板随设备产生差别就能够了。
以前咱们不多看到有基于Angular的移动端开发框架,但如今有了,好比Ionic,使用这样的框架,能够直接引用已有的业务逻辑代码,只在展现上做一些调整。这么作有不少好处,同时也对代码的架构水准有必定要求,须要把业务逻辑跟界面展现彻底切割开。
这样带来的好处也是很明显的,独立的业务逻辑,由于它不依赖于界面了,因此很容易控制,作单元测试,集成测试,打桩等等,总之它是纯逻辑的东西,在后端能够用什么方式保证代码质量,在前端的业务逻辑也同样能够用,业务逻辑能够所以而清晰稳定。
对于企业应用而言,这么作能够极大程度地复用以往的业务逻辑,只在负责最终展现的代码部分做差别化。
上面这些技术性的问题都解决了,剩下的都是规模带来的边际效应,这须要咱们从工程化角度去考虑不少问题:
这些话题,篇幅所限,不在本文中叙述,能够查看我另外的关于Web应用组件化的文章。
原文地址: 基于AngularJS的企业软件前端架构