距离上次发文,已经过去了三年半,这几年技术更新节奏异常迅猛,.Net进入了跨平台时代,前端也被革命性的颠覆。
回顾
2015年,正当我还沉迷于JQuery + EasyUi的封装时,突然意识到技术已经过时。
JQuery在面对更加复杂的UI需求时显得力不从心,EasyUi虽然组件比较完善,但界面风格老旧,响应速度慢,且是收费商业产品,在一个商业产品上投入精力封装并不划算,所以我果断弃坑了。
然后开始封装JQuery + Bootstrap,在一个SPA(单页应用)项目里暴露出很多问题,让我认识到JQuery不适合做SPA,我开始寻找新的方案。
AngularJs(ng 1.x)是谷歌出品的JS框架,几本书下肚以后,我开始琢磨着如何把AngularJs操作封装起来,为了封装成链式调用,甚至改了它的源码以支持服务定位器模式。
正当我准备封装基于AngularJs的UI组件时,发现它也过时了。由于UI封装工作量巨大,而前端环境异常混乱,不再敢轻举妄动,直到前端三大框架浮出水面。
2016年,.Net Core兴起,为了方便学习交流,Alexinea(刘怡)发起了.Net Core学习小组,Kiler(谢炀)、Lemon(刘浩杨)和我做为第一批成员加入了小组。
微软当时尚未提供.Net Core中文文档,为了方便国内.Net Core的学习和推广,.Net Core学习小组组织了一批.Net爱好者进行翻译,直到官方提供了中文文档为止。
2017年,.Net Core学习小组决定发布开源项目以推动.Net Core的发展,.Net Core学习小组也正式改名为.NET Core Community(.Net Core中国社区),简称NCC。
最初加入的项目有Lemon的AOP框架AspectCore,Savorboard(杨晓东)的分布式事务解决方案CAP框架,以及我的应用框架Util。随后一些优秀的开源项目加入了NCC,包括爬虫解决方案DotnetSpider,分布式微服务框架Surging等。
Util产生的背景
我长期混迹于小型软件公司,小公司资源有限,人少事多,水平也参差不齐,如何降低团队的学习成本,如何提高项目的开发进度,如何降低Bug率,是我的主要关注点。
Util应用框架是我在多年的项目实战中积累起来以解决上述问题的利器,实践证明,它在多个小型团队和多个中小项目上起到关键作用,甚至包括一些濒临流产的项目。
中小项目的瓶颈在哪
对于中小项目,简单CRUD(增删改查)占据大量篇幅,核心模块包含复杂业务逻辑,报表包含复杂查询,另外还需要权限控制、流程控制等,不管模块再多,大体不出这个范围。
前端UI体验变得越来越重要,工作量也越来越大。一个简单CRUD,服务端API开发半小时,UI可能需要折腾一天。
如何打破瓶颈
如果能够迅速拿下CRUD,并且能有效减轻前端开发任务,那么就能将更多精力投入核心业务,从而提高项目交付能力。
对于服务端CRUD,通过封装基类再配合代码生成就能很好解决,如何提升前端开发效率?
正确认识"前后分离"
一些人鼓吹“前后分离“原则,前端界面交给专业的前端人员,后端人员只负责提供API就好了,这似乎是提升前端开发效率的灵丹妙药。
问题在于小公司资金预算有限,而专业前端人员薪资要求很高,以低价招聘的前端人员,往往只会HTML + CSS,JS耍得并不溜,最后还得服务端程序员上场。
另一方面,前端还有前台和后台之分,前台是面向终端用户的网站,比如门户网站,商城一类,前台偏重展示,规律性不强,后台是面向用户或管理员的表单系统,偏重功能,规律性比较强。大多公司的前端人员主要开发前台网站,而管理后台还是服务端程序员开发,前端人员顶多帮助界面布局,功能还是后端人员完成。
"前后分离"不一定是人员的分离,也不能降低工作量,主要是指前后端依赖关系的分离,如果前端技术或后端技术可随意替换,而不是绑定在一起,就认为分离成功,这提升了项目的可维护性。
Js框架的选择
为了降低前端开发难度,选择一个好的Js框架显得特别重要。
对于一个复杂界面操作,JQuery同样可以完成任务,但与现代主流的Js框架相比,JQuery操作Dom的方式更加复杂,效率低下,且难以维护。
现代前端三大Js框架是Angular(ng 2+),Vue和React。通过学习,发现Angular更符合我对开发效率的追求。
要提升开发效率,最关键的特性就是代码提示。不相信??请在记事本上用C#写几句试试。
Angular是谷歌开发的Js框架,默认采用Typescript(Ts)语言开发,Typescript是微软开发的强类型脚本语言,它是Js的超集,在VS或VSCode上具有代码提示。
你可能会说,Js不也有代码提示?这完全不一样,Ts具有强类型的代码提示,你只能看见对象上明确定义的成员,提供了非常精确的提示,而Js的代码提示滚动条好几米长,和当前对象无关的信息也显示出来,这严重降低了代码提示的作用。
除了代码提示,Ts还填平了Js固有的一些设计缺陷。
很多开发人员不选择Angular的原因是需要多学一门脚本语言,认为成本高,这其实是一种误解。
Ts在语法上有点像Js + C# + Java的混合体,对于C#或Java程序员,上手成本非常低,开发起来让你爽不停。
作为微软系.Net程序员的我们更应该大力支持。
声明式编程
现在我们在开发服务端和前端脚本时都有了代码提示,工作效率得到提升,还能更进一步吗?
现代流行的前端框架都有组件或指令的概念,用来支持声明式编程,它_通过扩展Html自定义标签或自定义属性的方式来调用Js_。
这种方式将Js隐藏在内部,将Js转化成了Html,形成更好的封装性。毫无疑问,使用Html编写的页面,比使用Js具有更强的表现力。
如果文本框需要非空验证,只需要在标签上加个属性,就像下面这样,你并不需要调用任何Js就完成了验证工作,这相当酷。
<input required=”true”>
不过我发现很多团队并不使用声明式编程方式,更愿意使用Js,原因在于Html是弱类型标签,自定义属性并没有代码提示,这确实是个大问题。
TagHelper - 编写Html的救命稻草
我在2012年学习Dwz这个前端框架时,接触到声明式编程的概念,也被Html无提示的问题所困扰,后面发现Asp.Net提供的HtmlHelper能够封装Html,提供强类型的代码提示,这正是我需要的特性。
2015年用HtmlHelper封装了EasyUi,虽然只完成了一些基础工作,但使用过的人都认为它大量提升了开发效率,它现在甚至仍然是一些公司的主打技术。
HtmlHelper也有缺陷,它看上去是C#代码,而不是标签,比如HtmlHelper封装的文本框长成这样。
如果需要前端同学来帮助你调整界面,这就不太友好了。
另外HtmlHelper与Html混在一起也让代码看上去不直观。
微软发现了这个问题,在Asp.Net Core推出了TagHelper,TagHelper与HtmlHelper类似,也是用来封装Html的强类型工具,不过它用起来是个标签,如果你不注意,根本看不出这是Html,还是TagHelper,这对前后端人员配合完成Html页面具有重大意义。
现在TagHelper封装的文本框长成这样了。
TagHelper与Angular结合的艰难之路
使用Angular或Vue框架,你通常不会从0开始工作,寻找现成的UI组件库是更明智的选择,比如Angular可以选择Angular Material或Ng-Zorro。
这些库都提供了大量组件和属性,如果直接使用Html编写,没有代码提示,你必须随时打开官网,四处翻上翻下以寻找你需要的属性,哪怕你曾经用过它,如果你记得不是十分精确,从官网复制粘贴会时刻伴随着你。
现在好了,你知道TagHelper是救命稻草,可能已经迫不急待想要动手封装了,不就是把Html拼接出来输出到页面,这能有多难?别急,小伙子,将Angular组件封装成TagHelper可不是你想的这么简单。
如果你关注过Asp.Net Core Angular这个模板项目,你会发现这个模板使用的依然是Html,而不是TagHelper,这是为什么?自家这么好的技术,你都不推广一下?
我凭借之前封装HtmlHelper所积累下来的经验,将Asp.Net Core 2.0 Angular这个模板项目改造成支持TagHelper,这是使用Angular提供的JIT编译特性做到的。JIT就是即时编译,会在运行时动态获取Html页面并编译成内部Js,我在Github上也发现国外有些人在使用这种方式,不过都处于Demo级别。
我用TagHelper封装了Angular Material组件库,并将它应用在项目上。
很快爆发了严重的性能问题,这时候项目上的页面也才上百个。
从启动到看见登录页面,需要半分钟,F5刷新还需要这么久,这个速度是无法接受的。
我观察到系统在启动时会请求所有页面,原来我虽然使用了Angular模块,但没有使用延迟加载,主模块直接引用了子模块,导致在启动时加载全部页面。
使用延迟加载缓解了这个问题,启动能在三秒左右完成,进入子模块也需要几秒,虽然能勉强使用,但性能和AOT编译相去甚远。
AOT编译是Angular提供的预编译,能在发布阶段把Html文件直接编译成Js,这样在运行时就不再访问Html页面,对性能具有极大的提升。
使用了TagHelper以后,并没有Html文件,AOT编译无法访问服务端Url,所以没办法使用。
鱼和熊掌都想兼得,如果能够得到TagHelper的编译时检查和代码提示功能,又能得到AOT编译的运行时性能,这该多好啊。
我当时寄希望于Asp.Net Core团队,希望他们能提供一点支持,在Github提了Issue之后,Asp.Net Core团队表示TagHelper与Angular这种Js框架水火不容,机制上的问题。我后面仔细想想,确实如此,TagHelper主要为服务端MVC提供支持,而使用了Angular这样的Js框架后,路由都走客户端,通过AOT编译后,服务端除了提供WEB API,真没它啥事。
由于TagHelper提高了我团队的开发效率,延迟加载能让这个机制勉强使用,我也就坚持了下来。直到有一天灵机一动,用TagHelper生成出Html文件不就好了吗,和玄冰同学折腾了一天,终于解决了这个问题。现在,TagHelper充当了代码生成器的角色,服务端MVC相关的特性被全部抛弃。
小结
前面介绍了Util的一些背景和动机,同时也解释了为何我花大把时间在UI封装的原因。
未完待续,下一篇介绍Util包含的主要功能,以及让你把TagHelper + Angular Material封装的Demo运行起来。
Util应用框架的下载地址为:https://github.com/dotnetcore/Util
写文需要动力,请大家多多支持,点下推荐,Github点下星星。
Util应用框架交流一群: 24791014