一个月前离开呆了9年的中兴软创,有不少东西值得写下来,千头万绪,不知从何写起,自己留下了10多万字的回忆,但这里面涉及的东西太多,不便公开,还是把这些年对工作的感悟写一下吧,这9年的工作基本都是围绕前端框架的。

中兴软创的主要业务称为BOSS,也就是电信行业的运营支撑软件。早期的BOSS系统一般都不是Web化的,而是C/S架构,当时大家做所谓的“前端”,用的是Delphi,C++ Builder,或者Java Swing。后来B/S流行之后,大家就逐渐往浏览器上迁移。

那个时期的浏览器,不像现在这么多样化,一般指的都是IE,而且可以具体到三个版本,5.0,5.5,6.0。第一批迁移到B/S模式的系统,多半是那些简单表单的系统,界面只是填值,作个简单校验,然后提交给服务器。可以说,这个时候的Web前端是很乏味的,因为没什么可做的,用table布局,里面放些form,极少量的JavaScript代码,更谈不上用CSS。

不久,Web系统就复杂化了,在C/S里面,我们可能有大量的“控件”可用,基本的输入框这些不谈,在HTML里也有,时间日期这类,就要费些周折了,更复杂的,比如Tree,DataGrid,甚至TreeGrid,就更折腾。当时写JavaScript的人还是有不少的,面对这种情况,也想出了一些办法。

这些办法的根本原理,都是用已有的HTML来拼凑出一个控件的样子,再加上事件,一直到现在也没有更好的办法。比如说,日历,就用table标签来生成一个表格,然后当前日期加个颜色。又比如DataGrid,也是一个表格,然后tr上面加点击事件。有的流派是跟现在一样,把控件的声明和初始化都放在JavaScript代码中,只在HTML里留一个容器标识,更主流的方式是用HTC来封装控件。

如果仔细看过早期ASP.net的代码,就会发现它带了几个htc文件,比如treelist,tabstrip,multiview等等,这些控件的功能已经基本能满足需要了,就是有些丑,有些人在此基础上作美化,04年的时候,中兴软创的多数基础控件就是这么来的。HTC提供了一种扩展HTML标签的机制,业务开发人员用起来很方便,所以很有效地降低了开发门槛。

再看另外一个方面,传输的问题。最开始大家都是把数据用submit按钮提交给服务端的,但是提交就会刷新整页,效果不好。我们知道,AJAX的概念是05年提出的,但是在此之前好几年,就有不少用XMLHTTP的人了,我自己入职之前,03年的时候,就用过这个,入职之后看公司的前端框架,也一眼发现了这些东西。中兴软创的这套传输机制是在微软顾问的帮助下创建的,传输的原理就是在前端把表单数据序列化成XML,通过XMLHTTP传给后端的Servlet,当然,那时候用的是同步传输,传的时候界面会卡一会。

05年我刚入职的时候,还没看过系统,老大问我对前端这块有什么看法,我说可以考虑做组件化,把更多的东西封装成HTC那样的组件,然后组件内部通过XMLHTTP跟服务端通信,他听了之后说我们现在已经有一些HTC控件,通信也基本都是XMLHTTP了。回想起来,当时我的思路是纵向的组件,端到端,每个组件实际上只通过事件和方法与其他组件交互,各组件自身就可以独立运行,应该算是早期前端组件化的一种思路。

为了通用性,前端封装了一个方法叫callRemoteFunction,三个参数,分别是后端的Java类名,方法名和参数对象,用XMLHTTP发送到后端的Servlet之后,通过前两者反射得到对应的Java方法,执行结果再返回给前端。这样,在JavaScript里“调用”后端代码,就像调用普通的JS函数那么方便。也有这样调用动态SQL的,后来这两者统一成服务,只要传入唯一的服务名和参数,不用管是Java服务还是SQL服务。

有了这些东西做保障,业务系统的B/S化就容易多了。当时的开发模式是前后端分离,后端负责写服务,前端写界面和JavaScript,这种模式也带来很多好处,比如有的业务系统后端从.net迁移到Java,前端部分基本除了登录之类,都没什么要改动的,人员的协作也是很顺畅的。

在迁移系统的过程中,也有其他一些混杂技术,比如说,处理一些监控图形之类的,由于缺乏经验,加之为了重用之前的Swing代码,搞了一些Applet,虽然混搭的风格不太好看,但当时是没什么人讲究这个的,业务系统能用就行了。因为我入职之前搞过VML,所以极力鼓动把各种图形的东西搞成VML,这个东西在当时最大的优点是不需要给浏览器安装插件,其他任何方式都做不到。

后来就有了IOM系统那个很典型的自动布局流程建模界面,核心部分有3k多行JS,花了近2个月,期间还重构过一次,后来陆续改需求,到06年下半年才不太改动了。从此之后,公司的Web图形这块,基本都是用VML,不再有人提Applet的事了,而且几年内也没有用Flash做这类图形的,据我所知,业界当时用Flash做图形界面比用VML的还多些。

到了07年,Firefox就占不小的市场比例了,而且HTC这个东西,微软自己也不太看好,所以不得不未雨绸缪,考虑这些东西的替代方案。正好当时调动部门,新部门打算彻底翻新产品,所以有机会考虑前端的新方案。作为前端的整合框架,有两条道路可走,一条就是选择别人的方案,比如早一点的Bindows,还有当时比较火的ExtJS,另一条就是先引入一个JavaScript基础库,然后在上面自己做控件。经过慎重考虑,还是选了后者,因为我们的业务需求比较复杂,改控件的情况很多,要是用了ExtJS这类,虽然看起来什么都有,但是改东西估计就痛苦了。

接下来就是选基础库了,流行的有Prototype,Mootools,jQuery,甚至还有万常华的JSVM,在那个时候其实很难预料到后面jQuery这么火,就算到现在我也不能理解,所以我们的选择是Prototype,然后在它基础上构建外围库,主要是控件。

当时看过很多UI库的机制,比较来比较去,觉得最能接受的还是YUI的方式,所以大致按照这种方式做下去了。我们的控件体系是比较松散的,彼此之间无任何依赖关系,可以独立引用,控件的唯一参数就是父容器,然后传入初始化参数,加载数据之类。

这一代控件的DataGrid和TreeGrid是我做的,跟上一代最大的区别是简化了事件。比如说,之前的控件选中行用的是点击,但是键盘的方向键也可以改变选中行啊,这时候业务方需要监听控件的两种事件,在每种里面都做选中行变更的操作。这一代里面我只给业务方开放change事件,不管实际是从什么事件发起的,最终需要关注的只是这个change,在控件上,行的点击事件这种过于原始的事件是没有意义的,直接抛给业务方非常不合适。刚开始改成change的时候,有些业务开发人员不太习惯,不过很快就觉得这样方便了。

这一代的TreeGrid控件我作了懒加载,但实现的细节上有些考虑不周了,比如说,下层节点在未展开的时候,DOM不创建,这没有问题,但是我连节点对象都没创建,当业务方要访问未展开的节点数据时,只能从数据源上去获取,已展开的节点和未展开节点的访问方式不同,这算是一个败笔。

整体来说,这一代的框架运作还是很成功的,比较稳定,但整个版本关键的一点没有达到,就是跨浏览器,也就是说,即使把控件代码改成纯JS的,也没让整个版本跨浏览器,这很悲剧。一个关键问题是版本时间太紧,框架层从无到有三个月之后,业务侧就大量启动开发,有不少问题没有来得及解决,更本质的问题在于当时我们缺乏经验,没有对业务开发人员作约束,比如说,有些要避免的写法没有列出,对于跨浏览器怎样测试,也没有时间作考虑。等到打算解决这些问题的时候,面对海量的业务代码,已经无从下手了。

这个版本中,也遇到一些比较新的需求,比如说有的监控需求,要实时通信,那时候没有WebSocket可用,就用Flash的Socket,搞了一个不显示的Flash,专门用来连Socket,然后再用JS跟它交互,效果还可以,只是因为Flash的跨域策略升级过几次,导致踩了一些坑。

说到这个Flash,又扯到另外一些话题,早期搞前端的人,多数都玩过它。Flash内置一些控件,比如基本表单输入,还有调用WSDL格式WebService的通信控件,整个体系其实成熟度不比HTML低,只是我一直对时间轴很痛恨,所以即使搞,也都倾向直接用AS写,很少用元件转MovieClip那些东西。后来2004年推出的Flex1.0,彻底不一样了,我研究过一阵,也想过如果在企业应用领域,全部用它来构建前端如何?

这个想法是有些激进,但对于企业应用而言并不过分,企业应用连Applet都能接受,机器上要装10多M的JRE,那用1M多的FlashPlayer不是更好嘛,而且当时很多开发人员写不好JS,尤其是代码规模较大的时候,但他们写Java都还凑合,如果用AS来写,代码效果应该好不少。

当时的Flex是要部署到应用服务器里的,运行机制大致就像JSP那样,文本代码经过一个预编译,然后发到浏览器端来执行。当时制约Flex发展的主要因素是客户端机器的配置,Flash体系的界面效果较好,但比较占资源,而且在开发阶段的优势也体现不出来。

但我一直认为,Flex体系在较大一个时间段中很适合企业应用体系,因为浏览器混战的时期很长,乱象环生,老的浏览器迟迟不去,多少年也抹不平兼容的坎。对企业应用而言,搞跨浏览器兼容这方面并非它的核心价值,如果有一种技术能暂时抹平这些浏览器的差异,优势会是很明显的。要说占资源大,难道ExtJS占资源就小了?企业应用连ExtJS都可以接受,当然更能接受Flex。

所以从09年开始,又逐步进行Flex的引进,当时的Flex发展到了3.0,整体算是比较成熟了,后来陆续花了两年时间支撑业务产品的开发,效果还可以,但从引入时机来说,还是略有些晚,如果再早两年引入,状况会更好一些。

另外一方面,BOSS领域的应用系统并不局限于企业应用类,也有一些是面向个人用户的,比如说自服务和网上商城,前者类似10086.cn那种模式,个人消费者可以登录办理一些简单业务,后者就是典型的网店,只是所卖的限于电信类的实体商品(手机、上网卡等)或者虚拟商品(套餐,流量)等。

这个场景跟之前的内网应用大有不同,算是真正的互联网模式了,所以它所用的前端框架就与其他的不同。由于精力所限,开始几年在这方面的投入很少,一般都是用jQuery外加一些开源的控件,这样整合起来用,页面不花哨也不复杂,基本功能也是能够满足的,做的效果只能算是凑合,主要是没有熟悉CSS的人。

在做电信业务运营支撑的这类公司,UI一直是薄弱环节,不可能得到本质上的重视。整个中兴的整个体系里,软件的重视程度并不如硬件,比如从手机上面就看得出来,卖了手机之后就不太重视后续软件升级了,还是卖老的功能机的思路。在软件体系里面,前端也处于相对弱势的地位,毕业生入职的时候,都会优先让编程水平较高的做后端,在前端里面,逻辑和业务的重视度又高于UI,所以UI保持能用就不错了,在关键的一些跨浏览器兼容,CSS规划方面,基本是没有什么进展的。好在近两年,由于有了BootStrap这样的东西,把很多原本要做的事情做掉了,所以只要对界面没有特别的需求,光会写JS也能把界面搞得像模像样。

这部分的前端框架,其实也不是这么搞就完事的,基于传统的思维,做这些界面的时候,开发人员仍然倾向于使用偏重量级的控件,而不是使用界面模板库等方式来做一些数据展示的效果,这一方面带来的是观感的不佳,另一方面,由于引用的一些控件库没有很精细地隔离,往往都是整套控件一起引入,甚至在一个界面里还出现同时引用多种界面库的恶劣情形,一个并不算复杂的界面,引用的压缩之后的文本代码就高达1-2M之多。

所以从这个方面讲,公司的多数前端人员并不专业,专业与不专业体现在什么地方?是要有一个整体的优化。前端与后端开发方式的一个本质差异是引入任何东西的代价都比较大,因为你的代码要先经过一次网络传输才能执行到,而且还要注意避免冲突。如果只要引用某个功能,就不应把其他不相关的东西也一起引入,所以那种一个大控件库整体打包的方式在这种面向互联网终端用户的模式下非常不合适。这个道理并不难理解,但为什么操作的时候很少有人注意避免呢?

因为两个原因:

  • 精确控制的代价较大。这一点确实是个大问题,要做精确控制,最小依赖,需要把整个框架的依赖关系理清楚,在现有的开发体制下,谁为这个时间买单?既然没有,那基本上就没人管了。
  • 加载的字节量未作为系统上线的考核指标。从反面说,如果这么做了,功能倒是能用,但系统加载慢了,有多慢,这个没有预设的性能底线,一般赶时间做的系统也都不会太纠结在这上面,能用了按时上线了就大家都谢天谢地。

从决策层的观念上,也有一个误区,比如认为自服务类系统不算核心系统,对开发技能的要求也不会多高,凑合能用就行了,事实并非如此!企业应用型的系统,才是不特别考验开发技能的,考验的更多是架构水平,它在前端的坑并不多,所以完全可以由个别架构水平高的带着一群偏弱点的开发人员做,而网站类的对每个开发人员的前端技能水准要求都更高,如果不改变以往的思维方式,后续这类系统会经常收到投诉。

近两年,因为要考虑未来老旧浏览器淘汰之后的事情,所以我花了不少时间研究了一些懒加载框架,还有一些前端MV*框架,尤其在AngularJS上,花了很多精力,比如12年的时候打印了源码来看,也做了各种尝试。这些东西用在企业应用领域,是极好的。第一次看到AngularJS,是因为当时在寻找通过HTML属性实现数据绑定机制的方案,然后就看源码,看同类方案,一发不可收拾。

后来的规划,是用它来实现核心逻辑,而外围的directive层分为PC浏览器和移动终端两类,这样可以实现逻辑的共享。到了该考虑移动端的时候,又碰到了Ionic,真是想什么来什么,也说明我的这些路不孤单,还有一些人用同样的思路在走。

之前公司也搞过移动端的系统,用了响应式设计,也碰到一些坑,从我的角度看,公司用响应式设计还是要慎重,因为完全没有熟悉CSS的人,要用这个风险很大。

近两年考虑的另外一些事情是前端开发的工程化,这个路也不孤单,各大公司都或多或少的在做,比如前端组件的管理,自动化测试,发布等等,典型的有百度FIS。当系统规模扩大的时候,在代码管理和发布问题就特别多,前几天看到winter的微博,应该也是踩到不少坑。。。所以说,架构师要考虑的事情,一方面是系统自身的架构,另一方面要考虑团队在协同开发时候可能遇到的问题,从技术角度尽可能及早把这些东西化解。这方面花费的精力很可能比真正在产品里花的还多,而且是很痛苦的,做了很多之后还不容易看出作用。

去年在上海一家公司面试的时候,跟面试官聊得非常投缘,他问一句我答一句,有时候他话没说话我就接着说下句,我话没说完他就接着说下去,最后两个人相对大笑,那是发自内心的苦笑,前端架构这个大坑啊。他说,对吧,架构这事,比的就是你踩过多少坑,我们这一路上踩过来的坑,都是血和泪。我俩笑得像《投名状》电影里,刘德华最后流着泪笑得样子。

碰到的另外一个聊得很投缘的人就是支付宝的玉伯,可能因为业务场景比较接近,而且大家的努力方向都在前端的工程化方面,所以很多东西都是所见略同。

早些年,公司的前后端分离开发,效率很高,问题也少,不知为什么做着做着就成了不分的模式,开发人员从HTML,JS,Java一直写到SQL,什么都搞,什么都不专业,很可怕,我提了不知多少次意见,从未得到回应。虽然最近业界很多鼓吹全栈工程师的,但这只能让那些个人能力较强的去做,作为补充,不能成为普遍做法,对于招聘人员水准比不上互联网公司的传统软件商,更是应当把人员分工搞好,这样才可能真正做好产品。

过去的事情都过去了,回头看看自己这些年,在工作上还是花了不少心思,每次有想法,都会说出来,哪里觉得不对,都会认真提出自己的理由。努力做一些与众不同的事情,会写一些工作方面的文章,会用业余时间组织培训交流,会自己出钱买书送给同事。从未提出过让自己团队任何人加班,研发过程奖也从未给过自己一分钱。有时候真不知道自己的坚持是为了什么,努力过之后,发现能改变的东西还是太少,很失落。

曾经是一个缺乏勇气的人,下棋或者打游戏碰到形势不好就立刻认输,后来看我同学阿龙打星际,屡屡被人打得只剩一个农民还到处逃窜开矿企图翻盘,看得多了,也就比以前肯坚持。人的一生,两件事最重要,一是努力,二是选择。这两者都不容易,这次狠心选择了新的道路,希望能坚持下去,不知道再有9年之后,会是什么样?