本文主要分享,2014年7月至9月,我在校校任职期间,在前端技术上的实践
Infrastructure
这次项目拉了@Sherwood,商量下来,整个系统采用前后端解耦的架构,一开始自然而然就想到用SPA
的解决方案,这样能够提升整个网站的响应速度,降低服务器的负载,做代码更新,CDN都很方便
SPA的解决方案有很多,包括Google的Angular
,但我个人不喜欢他的模板体系,很难嵌套,至于Ember
,太重、学习成本很高,当时还不是很了解Spine
,风车有用它,不过最近用下来感觉非常不好
由于只招到一个前端工程师@picker,而且也是刚转到前端的,项目鸭梨很大,所以最后就决定自己造轮子
但是基础设施不可能自己造,所以这边基础设施的选择就尤为重要:
- 构建工具套件
2013年的时候就出现了流行前端的构建流程```Yeoman```+```Bower```+```Grunt```,在这次重建整个项目的时候,放弃了之前的所有代码,重新构建了项目
- 模块化标准
当时主流的模块化标准有```AMD```,主要是```RequireJS```实现,```CMD```标准,和```CommonJS```,主要是```SeaJS```实现
由于之前的项目使用过SeaJS,对于他的构建方法和整个体系比较了解,能够适用于大中型的前端项目,所以也没啥好说的就选了SeaJS
- 核心类库/UI框架
这个方面,其实也没啥好说的,考虑到可维护性,选了```jQuery```,考虑到低版本浏览器的兼容性,我们选用了```1.11.x```的```stable```版本
由于选用的版本没有特别针对```drag```,```drop```事件做兼容,我们自己做了hack
UI框架方面也是使用了顺手的Bootstrap
,和jQuery
能够配合使用
Framework
实践的核心是框架:
模块化/分层
前端采用模块化+MVC的好处是,当前端的逻辑变得复杂时,可以重用大部分模块,针对每个页面写特有的逻辑就可以了,这样页面再多也能轻松维护
分层主要有几部分,application.js
是App的核心,也就是main loop
,另外还有控制器,模型层,服务层,公共模块等
当页面加载完
application模块会加载所有页面的模板,控制器,依赖注入系统,路由组件,注册全局事件监听器,初始化用户模型等,根据当前的url确定加载的首个页面
路由组件
路由组件主要由一个json
数组定义
包括目标路由的url形式,对应控制器以及模板,当然部分路由可以匹配到默认规则就是用默认,如果url部分包含动态内容,如:voteId
,需要额外定义一个正则匹配模式
路由组件会在匹配完成,返回一个路由对象,包含各种参数名,控制器等,等待被注入到控制器中,这边主要借鉴的是AngularJS
的注入体系
依赖注入
系统的核心,可以将各处注册,初始化的组件,注入到Controller中,被使用,比如说登陆的用户,路由信息,其他Service,Model都能被注入到控制器层,以免重复Require,主要用了Javascript的反射
只需要在Controller中,绑定一个$injector
属性
全局请求
在请求方面,做了切面处理,根据服务器端约定的协议,拦截数据;
在处理异步请求方面,我们用了Q.js
来处理异步
当服务器返回HTTP级别的异常时,进行错误处理,抛出WebService Error
只有当服务器返回数据时,全局请求器先判断是否有协议内定义的关键字如CAPTCHA,EXPIRED等状态,这时抛出异常或者直接进行处理,其他情况都直接返回结果
最后返回一个Promise
对象,可以链式调用,业务级别的错误在各控制器内分别处理
当然更好的做法是对业务错误有统一的处理,并且能够根据情况Override
模板系统
看了市面上非常多的模板系统,大致分成两种,一种是jade
风格的,有slim
,另一种是原生的风格,诸如handlebars
,artTemplate
,包括像erb
也算吧
在这方面我考略重点是易于维护,并且语法宽松,虽然handlebars
也不错,但是他对于输出的控制非常厉害,不能将部分数据插入到html属性里
最终我选了artTemplate
,因为他语法简单,支持CommonJS
打包,速度一流,尤其是官方就出了grunt-tmod
集成工具,当然我们对他的语法稍稍扩展了一些,在发布时进行AOT静态编译,渲染速度相当快,在代码集成模板也可以很好地控制渲染逻辑
不过3.0版本的简易语法不够用,没有for这样的循环语法,我们自己插入一段loop
语法,当然grunt-tmod
在npmjs上面的包有些问题,我们手动打上github最新版的补丁就好了
表单验证工具
由于表单在HTML前端重要的作用,我们自己做了一整套表单验证模块,代码略长,仅提供接口
通过在模板中定义自定义的html属性来进行规则定义,然后拦截submit
事件,调用getFormData($formElement)
getFormData内部会调用formValid
来校验规则
如果返回数据那么表明验证成功,直接提交数据即可
如果返回数据是false,那么就提示错误
当然为了方便在input
触发blur
或者是change
事件时,进行单独校验,所以第一个接口也是暴露的,并且能够传入两个回调函数
Server
- 服务器
用青云的服务,前端服务器就一台Debian虚拟机,双核2G,能够承受上万的访问量,并没有做特别的优化
- 部署
主要采用scp上传完整包,服务器部署
这边部署我们自己写了部署脚本,基于Shell的,采用plist文件列表指定版本号,并且能够产生log追溯问题版本
具体的脚本放在github
https://gist.github.com/lujiajing1126/eca5358fe7ab114bb83f