forked from xufei/blog
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
269 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
影响企业应用前端开发效率的因素 | ||
==== | ||
|
||
原先是在知乎上回答一个[问题](http://www.zhihu.com/question/22426434/answer/21433867 "")的,整理了放这里: | ||
|
||
我们来分析一下究竟哪些因素让企业应用的前端开发这么困扰。 | ||
|
||
先看看界面部分吧。 | ||
|
||
#1. 命令式还是声明式 | ||
毫无疑问,就写界面来说,声明式的代码编写效率远高于命令式: | ||
|
||
<Panel title="Test"> | ||
<Button label="Click me"/> | ||
</Panel> | ||
|
||
Panel p = new Panel(); | ||
p.title = "Test"; | ||
Button b = new Button(); | ||
b.label = "Click me"; | ||
p.add(b); | ||
|
||
第一种容易写,容易理解。 | ||
<!--more--> | ||
#2. 控件标签集 | ||
不管你的软件面向什么行业,至少都要一些控件,或者是基本的表单输入,或者是复杂的比如树形表格,里面还可以跨行跨列渲染的。 | ||
|
||
如果我们有一套映射到控件的标签,那么写代码是肯定会简单很多的,比如说,在HTML里面没有原生的Panel,那么,刚才第一段代码可能就要变成: | ||
|
||
<div class="panel panel-default"> | ||
<div class="panel-heading"> | ||
<h3 class="panel-title">Simple HTML Loader</h3> | ||
</div> | ||
<div class="panel-body"> | ||
<button>Click me</button> | ||
</div> | ||
</div> | ||
|
||
我们为了使得界面代码编写更高效,毫无疑问会倾向于把这么一堆东西简化成一个Panel标签,这样就会逐步建立一套面向自己行业的标签集。 | ||
|
||
#3. 带逻辑的控件 | ||
刚才这个例子为什么简单呢,因为它只是一个普通容器,静态的,不带逻辑,所以即使你用什么静态模板也能解决问题。如果复杂一点,是一个TabNavigator,就要考虑切换的事件,再复杂一些是个树形表格,那就更麻烦了。 | ||
|
||
我们来看jQuery提供的插件方式实现TabNaviator: | ||
|
||
<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> | ||
|
||
<script> | ||
$(function() { | ||
$( "#tabs" ).tabs(); | ||
}); | ||
</script> | ||
|
||
从我个人的角度看,这种代码很愚蠢。蠢在何处呢?HTML这类声明式的界面描述语言,写起来本来应当直观一些的,但是被这么一搞,又往命令式的方向去了。而且两种东西混杂,声明和渲染居然分了两处,又增加了维护的成本。 | ||
|
||
难道就没有别的办法来解决这个问题吗? | ||
|
||
我们看看其他语言和框架,比如Flex和Silverlight。 | ||
|
||
<mx:TabNavigator id="tn" width="100%" height="100%"> | ||
<!-- Define each panel using a VBox container. --> | ||
<mx:VBox label="Panel 1"> | ||
<mx:Label text="TabNavigator container panel 1"/> | ||
</mx:VBox> | ||
|
||
<mx:VBox label="Panel 2"> | ||
<mx:Label text="TabNavigator container panel 2"/> | ||
</mx:VBox> | ||
|
||
<mx:VBox label="Panel 3"> | ||
<mx:Label text="TabNavigator container panel 3"/> | ||
</mx:VBox> | ||
</mx:TabNavigator> | ||
|
||
上面这段是Flex里面的TabNavigator,在这个链接底部有运行结果:TabNavigator | ||
|
||
为什么它可以看不到逻辑的代码,但是又确实能有动作呢,因为它的实现类是mx.containers.TabNavigator,在这个代码里,可以自己手动去处理一切内部实现,但是暴露给业务开发人员的就是这么简单的标签。 | ||
|
||
我们看看在HTML和JS这个体系里用什么办法去解决。不要提JSF这类服务端技术,因为它的思路也是不好的,展示代码的生成和渲染都不在一个地方,会有很多问题。 | ||
|
||
#4. Polymer与Angular | ||
|
||
早期IE里有HTC,也就是HTML Components,因为别的浏览器厂商不喜欢,所以快要消亡了。在W3C新的HTML规范里,有一个Web Components,参见这里:Introduction to Web Components | ||
|
||
这个东西跟HTC的思想本出同源,它引入了Custom Elements和Shadow DOM这两个概念,也就是说,我可以自定义一个标签,然后在内部随便怎么折腾,用这个标签的人可以很方便。 | ||
|
||
很美好,是不是,但是只适用于比较新的浏览器,基于这个理念架构的框架Polymer的目标也只是支持一些比较新的浏览器。Polymer | ||
|
||
那么怎么办呢?我们还有Angular,它也可以自定义标签,然后用directive的方式写内部实现。 | ||
|
||
<tabs> | ||
<pane title="Localization"> | ||
</pane> | ||
<pane title="Pluralization"> | ||
</pane> | ||
</tabs> | ||
|
||
<script id="components.js"> | ||
angular.module('components', []) | ||
|
||
.directive('tabs', function() { | ||
return { | ||
restrict: 'E', | ||
transclude: true, | ||
scope: {}, | ||
controller: function($scope, $element) { | ||
var panes = $scope.panes = []; | ||
|
||
$scope.select = function(pane) { | ||
angular.forEach(panes, function(pane) { | ||
pane.selected = false; | ||
}); | ||
pane.selected = true; | ||
} | ||
|
||
this.addPane = function(pane) { | ||
if (panes.length == 0) $scope.select(pane); | ||
panes.push(pane); | ||
} | ||
}, | ||
template: | ||
'<div class="tabbable">' + | ||
'<ul class="nav nav-tabs">' + | ||
'<li ng-repeat="pane in panes" ng-class="{active:pane.selected}">'+ | ||
'<a href="" ng-click="select(pane)">{{pane.title}}</a>' + | ||
'</li>' + | ||
'</ul>' + | ||
'<div class="tab-content" ng-transclude></div>' + | ||
'</div>', | ||
replace: true | ||
}; | ||
}) | ||
|
||
.directive('pane', function() { | ||
return { | ||
require: '^tabs', | ||
restrict: 'E', | ||
transclude: true, | ||
scope: { title: '@' }, | ||
link: function(scope, element, attrs, tabsCtrl) { | ||
tabsCtrl.addPane(scope); | ||
}, | ||
template: | ||
'<div class="tab-pane" ng-class="{active: selected}" ng-transclude>' + | ||
'</div>', | ||
replace: true | ||
}; | ||
}) | ||
</script> | ||
|
||
这么一来,也就有些接近我们的目标了,看到现在,我们还记得目标是什么吗?是尽可能精简的面向领域的容器和控件标签集,有了这个,写界面代码才能更简单。 | ||
|
||
#5. 为什么HTML默认标签集这么小 | ||
|
||
事情结束了吗?没有呢。我们的HTML体系为什么标签集这么小?因为他要解决的是通用领域的东西,怎样才能通用呢?要的是尽可能无歧义。 | ||
|
||
怎样的东西会没有歧义?那就是它的含义尽可能少,比如说单行文本输入框,总没人对它有歧义吧,它无非就是可以设置最大最小长度,是否只读,是否禁用,最多通过某种规则来限制输入字符,最多最多,也就这些可做的了,大家都认同。 | ||
|
||
Button就不同了,一开始他是 | ||
|
||
<input type="button" value="Click"/> | ||
|
||
后来大家想要各种各样的button,于是开放了 | ||
|
||
<button></button> | ||
|
||
这样的标签,可以在里面写各种HTML,我记得当时很多人在中间加上下和左右两层marquee,简直玩坏了。 | ||
|
||
现在HTML里面又有了数字输入,日期时间输入这样的东西,数字的没什么疑问,就是最大最小值,步进值等等,日期时间这个就复杂了,它怎么做,都有人不满意。有人要日期排左边,有人要时间排上面,有人只要年和月,有人只要分和秒。有人要点空白表示选中,有人要双击日期表示选中,还有人想用农历、波斯历、尼泊尔历,简直没完了,还不如不做,谁要谁自己做…… | ||
|
||
所以,面向各领域的人们,自己动手,丰衣足食吧。 | ||
|
||
#6. 界面修饰 | ||
|
||
好了,控件集的问题解决了,我们来看看界面的修饰。 | ||
|
||
你们发现没有,不管用什么非HTML的标签体系,可能写代码会很快,但是有时候要修饰界面,比如只是调整一下所有容器的边距,某些按钮的圆角之类,就会生不如死。 | ||
|
||
这时候你会发现,HTML里面的CSS真是神器,什么都能干,而且是面向切面的,只要你的HTML结构是良好的,完全不需要调整这个层面的代码。为什么其他体系的CSS没有这么强呢?比如说Flex也可以写CSS,QT也可以写CSS。 | ||
|
||
因为CSS的部分实在是太复杂了,复杂到整个浏览器里面绝大部分的代码都在处理这方面的东西,像Google的Chrome团队有1000多人,别的体系没法有这么大投入,只能看着羡慕。 | ||
|
||
上次看到一个问题,近30年来软件开发体系有哪些本质的改进?我觉得CSS真的可以入选,这是一个把结构和展现完全分离的典范,并且实现得很好。 | ||
|
||
我们的前端开发一般都是面向某个领域的,不管什么领域,CSS方向都可以有一个很独立的规划,因为它可以不影响界面的结构。所以这个方面,其实不太会对前端开发造成太多压力,压力只集中在维护CSS的人群身上。 | ||
|
||
好了,上面扯了那么多,其实到现在还在界面的层次,一直没有去谈到真正的逻辑。那么,最让我们困扰的部分是哪里呢? | ||
|
||
#7. 模块化和加载 | ||
|
||
Web前端开发有个最苦闷的事情就是选型,因为HTML这个体系很开放,提供的默认能力又不是很足够,如果要做复杂交互的东西,会需要很多额外的工作。有各种框架从各种角度来解决问题,但怎么把这些东西整合到正好符合自己的需要,是一个很花精力的事情,很多时候恨不得自己把全部轮子都造一遍。 | ||
|
||
真正的开发工作中,跨浏览器,踩各种坑应该是最烦闷的事,其他部分,如果有做好自己领域里标签的定义,或者不用标签用其他方式,应该不算特别困难。 | ||
有人说JavaScript语言本身比较松散,所以写业务逻辑比较头疼,这不算大问题。基于B/S的开发,有一个大坑是你在运行的时候要先把代码加载过来,然后才能跑。你看那些C/S软件,有这困扰吗?再看看后端程序员,谁还要关心自己的代码执行之前要做的事情? | ||
|
||
所以后端程序员写前端代码,都情不自禁地会引入一大堆库。我们形象一点来描述一下这个过程: | ||
|
||
嗯,大家都用jQuery,我也引入,抄了两段代码发现真不错。咦,我要个树控件,网上逛了一圈,拿了个zTree回来。再埋头苦干半个小时,缺数据表格控件,于是过了一会,jQuery UI被整体引入了。再埋头苦干,上网乱点了点,浏览器跳出个广告,一看叫做Kendo UI,看看发现不错,引进来再说,用里面的某个控件。又过了一阵,听说最近Angular很火啊,看了看例子,表单功能怎么那么强,我也要用!捣鼓捣鼓又加进去了。项目里又要用图表库,看了半天眼睛都花了,百度的ECharts不错哦,引进来。哎呀我界面怎么那么丑,人家的怎么那么清爽,查看源码,一看,Bootstrap,去官网一看,真乃神器,不用简直对不起自己。 | ||
|
||
没多久之后,这个界面已经融合了各种主流框架,代码写法五花八门,依赖了几M的JS库,更要命的是里面某些JS有冲突,某些样式也互相覆盖,快疯了。 | ||
|
||
这里有哪些问题呢? | ||
|
||
- JS代码要先加载到界面才能执行,而这么几M的代码加载过来就要好久了,然后每个框架还要把自己初始化,又耗不少时间,半分钟之后自己写的JS才开始执行,用户等得都快怀孕了。 | ||
- 不管是JS还是CSS,都应当控制基准的代码,这件事的主要意义是避免冲突,因为整个体系都比较松散,如果不加控制,就会造成冲突。即使在服务端写Java,也有类签名一致性之类的问题,所以这个部分必须要重视。 | ||
|
||
刚才这两点,第二点暂时不是我们要探讨的范围,第一点,引出的话题就是异步加载,这是一个可以展开说很多的话题,也不再说了。异步加载和缓存是面对复杂场景必做的优化措施。 | ||
|
||
但是这个里面规范就有好几种,具体实现方式就更多了。ES6的module也许可以解决这个问题。harmony:modules [ES Wiki] | ||
|
||
#8. 逻辑的分层 | ||
|
||
网站型和应用型Web程序对分层的需求是不一样的。网站型的逻辑大部分都在处理UI,而应用型可能有很多业务逻辑,这部分需要更好的组织,以便复用,或者即使我们的目标不包括复用,为了这个代码的可维护性,也需要有比较好的组织方式。 | ||
|
||
本质上这些组织方式与传统的客户端软件开发没什么不同,主要要做的无非就是UI层的隔离,或者模板化,或者别的什么方式。纯逻辑的代码大家都会写,但这个逻辑怎么跟界面产生关系,这是个问题。 | ||
|
||
有些框架通过在HTML元素上设置额外属性,然后启动的时候读取,在框架内部做一些相关的事情,比如Angular、Avalon和Knockout。有的框架在视图层中让开发人员手动去处理界面,就像未引入框架的那样,比如Backbone,两者是各有利弊的。 | ||
|
||
前面这种,一般功能是会很强大,但是它自身所做的东西必须足够多,多得帮你做掉绝大部分本来该自己做的事,你才会特别爽。所以,用这类框架来做表单型应用的时候,是会非常舒服的,因为这些需求他做框架的时候能预见,所以比如校验、联动、存取之类的都会处理掉。假如你要做一个绘图类应用,这就麻烦了,不管你是用Canvas还是SVG,它所能帮到的都不多。这时候,后面这类可能反而适合一些。 | ||
|
||
这些数据分层框架的原理是什么呢?是要做一层表单与数据的对应关系,所以他要检测数据的变动,比如一个Object,它某个值变更了,要去把对应的界面更改之类。这里面也有很多的坑,可以一步一步踩过来。。。 | ||
|
||
到现在,我大致可以回答你的问题,什么情况下前端开发会比较轻松呢? | ||
- 针对自己领域的界面标签库比较完善,或者易于扩展 | ||
- 样式容易调整,并且独立于界面元素 | ||
- 逻辑模块化,层次分明,在某种统一规范上存在大量可用库 | ||
咦,我这三点好像在说微软的WPF体系吗? |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
为什么企业应用这么“钟情”于IE6 | ||
==== | ||
|
||
企业内部的信息化,很多起步都很早,但B/S化基本都是从2000年以后开始的,之前有各种客户端,比如基本的Win32 API开发,或者Delphi体系(VCL等,C++ Builder也算在内),微软的几个封装化的体系(ATL,MFC,还有昙花一现的JFC),Java体系(AWT、Swing,SWT等),这些技术本身都比较成熟,IDE的支持也不错,但最终,很大一部分迁移到Web上了。 | ||
|
||
迁移到Web的最直接因素是部署成本,B/S架构有天然的部署优势,无需分发,这一点人所共知,但另外还有个重要因素是开发成本。很多人对这一点不相信,为什么呢,因为现在前端码农也不便宜啊,怎么就他降低开发成本了? | ||
<!--more--> | ||
早期的Web界面并不花哨,所以对一些技巧的要求远不如现在高,而且只存在一个主流浏览器(IE6),更是无需更多的知识,逻辑基本都放在后端,前端要做的事情基本只有写表单和表格类的HTML,从这个角度看,门槛确实低得可以,写标签的难度远远低于用高级语言写界面。 | ||
|
||
在这种情况下,大量的表单类应用就被迁移到B/S模式了,其中有时候会出现复杂一些的需求,比如流程的建模,在对VML不熟悉的情况下,很多这种东西会被开发成一个独立的客户端,跟B/S系统连到同样的数据库,变通解决这个问题。但其实在IE6里面,VML本身已经非常可用了,所以这时候也有很多图形化的东西用VML做,比如我自己05年写的这个,抓了个录像在这里:http://xufei.github.io/assets/iom.swf | ||
|
||
作为企业应用,另有个重要的事情就是代码的组件化复用,比如说,上面我这个录像里面的树型结构和选项卡,其实给开发人员用的时候很简单: | ||
|
||
<z:tree id="tacheTree"></z:tree> | ||
|
||
document.getElementById("tacheTree").loadXML(xml); | ||
|
||
这两句分别是界面中的声明跟JS代码中的调用,为什么可以这么写呢,是因为IE中有HTC(HTML Component),可以自己用HTML和JS定义标签,只需要用命名空间引入就可以。这个其实是非常有用的功能,一般的企业都必定会在此基础上有所积累,即使现在的Web Components,也没有比它高端到哪里去。 | ||
|
||
以上这些是从开发视角看的,作为一个运行平台来说,IE6已经非常足够,布局有些小问题,大家其实无所谓,用点技巧也可以摆得比较整齐。如果有大模块的集成,就来个iframe,也都工作得很好。 | ||
|
||
我们再来看看为什么很多企业软件固守IE体系。提到企业软件,大家经常有个误会,认为企业软件的前端就都是照着IE6来开发的,其实不是这样,企业软件产品是有延续性的,在已有产品上做升级的代价非常大,比如说一套很深入使用了IE only特性的系统,想要迁移到跨浏览器上,这个代价有多大?首先要构建同等复杂度的控件库,然后把功能逐个迁移,最后还要考虑一些已经被废弃了但是暂时没有替代方案的东西,整个过程是非常痛苦的。 | ||
|
||
很多时候,并不是开发人员意识不到这些问题,而是他没有解决办法。技术上的困难,经过一些努力都是可以克服的,更大的困难在于软件升级的成本。比如说,你评估了把代码修改成跨浏览器,需要50个人做一年,谁为这个过程买单?如果你站在企业决策者的角度,是会搞50个人来做一年,还是让你的2000个员工都稍微克服一下,只使用IE来访问系统? | ||
|
||
即使你说这次升级完了就解决了现在的跨浏览器问题,如何确保以后再出个什么浏览器也能支持?如何保证再也不会伤筋动骨地修改代码?谁也说不准未来是什么样,所以,他只有到了不得不升级的时候,才会考虑做这件事。会是现在吗?不一定,因为即使很新版本的IE,也还有兼容模式,他只要还能用下去,就不太有动力去折腾。 | ||
|
||
那么,我们再看看企业软件开发商是如何处理这个问题的。毫无疑问,企业软件开发商是希望你每年都升级的,因为传统的软件都是一次买断,然后收点维护费,客户是卖一个少一个,但你想升级,那就得交钱,谁会跟钱过不去呢?从这些企业长久的发展来说,它也会让自己的产品逐步去贴近标准,老的产品就这样了,你总不能现在开发个东西还说IE only吧,那估计都不好意思拿出去卖。所以说,这类企业在开发新系统的时候,反而会采用更激进的策略,比如说我就从某个基线往上支持,IE10+,Chrome 27+之类。在江苏电信的CRM系统里,从2011年开始就是推荐客户使用Firefox来访问系统的,大家去电信营业厅办业务,可以观察一下营业员使用的浏览器,其实在很多方面,传统软件厂商也不像外界猜测的那么固步自封。 | ||
|
||
很多时候,从企业软件开发商的角度看,新的浏览器标准并未带来多少比IE6为代表的“低端浏览器”更有价值的特性,SVG取代了VML,Web Components取代了HTML Components,多了一些储存、文件之类的本地接口,还有摄像头之类偶尔用得上的东西,之前大家用Flash的Socket,现在变成WebSocket,该XMLHTTP的还是XMLHTTP。HTML5体系中提升最大的标签语义化、布局和样式等在这个领域带来的震撼并不强烈,最有价值的反倒是JS性能上的大幅提升。 |