我所在的项目组主要负责公司的A产品A1模块的界面开发。通过上半年紧锣密鼓、加班加点地开发以后,终于在7月份在国内的L局点成功上线。当时那个激动啊,苦逼的生活终于过去了,你们都跟我high起来!但是到了下半年,因为公司市场人员的给力表现,又在海外开拓了D局点和T局点,真是喜(yu)大(ku)普(wu)奔(lei)啊!javascript
因为L局点的需求尚未明确,因此L局点的事情先按住不表,先说说D局点的需求。
其实,客户的实际要求也很少,对于界面来讲,无非是总体风格要与客户现有的产品保持一致。因此最终预计的工做量就是换换主题色而已,一切都是如此的easy,你们接着high!css
简单修改以后就给一线发了一个联调版本。html
在一线将版本安装完成以后,与客户联调过程当中(咱们的产品是要嵌入到客户系统中,做为客户系统的一个模块工做),客户对界面提了不少意见(该局点的客户都比较严(jiao)谨(zhen))。前端
目前来看,页面的国际化文件要准备两份(国内的L局点中文+英文,海外的D局点英文+德文),样式要准备两套(L局点一套,D局点一套)。若是客户再对页面结构有必定的要求,好比要变动页面的布局、元素等,那么html也要准备两份了。
最终分析来看,最好是能再作一套页面出来。java
按照公司如今的策略,代码分支只能有一个,作出来的版本不能有任何对于局点的定制,也就是说这个版本要在全部局点都能安装使用。webpack
如今问题来了,如何在一套代码中支持两套页面呢?git
咱们知道,一旦在页面中引用一个静态资源,那么这个资源的路径就定死了,不能改动。好比,咱们的页面上要使用到公司的logo,这个logo图片的路径一旦肯定,就不能再修改了,可是D局点要求将logo换成客户的logo。angularjs
既然公司不容许在作版本的时候进行任何的定制,那么怎么解决这个问题,想了半天,对现有代码目录作一下调整,最终肯定了以下方案:github
webapp | | public | | base | | js | theme | | default | | css | image | font | skin2 | | uiwidget | custom | js | theme | | default | | css | image | font | skin2 | | pageL | js | theme | | default | | css | image | font | skin2 | | pageD | js | theme | | default | | css | image | font | skin2 |
剩余要作的:
一是在打包的时候将pageL的内容拷贝到custom目录中,打一个L局点的ui包,再将pageD的内容拷贝到custom目录中,打一个D局点的UI包。最终将这两个ui包打入一个war包中。
二是新增一个脚本,在安装完成以后,根据局点决定使用哪一个ui包,删除无用的ui包。web
先说明一下为什么要这样划分目录。
除了页面变更之外,D局点的另外一个重要的需求就是要对公司产品的功能进行一些裁剪,把不须要的功能去掉。所以,不须要的接口也要通通屏蔽掉。因此,此处将全部页面目录都规划到public下,方便后面接口管理。
其余页面的划分,主要仍是基于以下图方案的考虑:
对于页面来讲,后台的接口是不会变得,因此将与后台的交互模块独立出来,放进base/js目录中,做为公共的Model层并对页面暴露公共的数据API。其余还有一些公共的css、图片等都放到base/theme目录下,做为基础样式。另外,一些公共的utils工具也能够放到base/js目录下。
PageL/PageD 就是根据具体页面具体开发了。咱们组使用AngularJS进行开发,使用RequireJS进行JS模块化管理,具体的开发内容不在本篇范围内,按住不表。
下面就要说说具体的构建过程了。
前端的朋友都知道,出于对页面性能以及用户体验的考虑,一般要对进行静态资源进行压缩、合并、指定缓存策略等,具体措施网上有不少的讨论,google一下能够翻不少页,这里也按住不表。
咱们组采起的措施主要是将css、js文件压缩合并,并对每一个文件进行签名并配置永久缓存。图片的话,主要是将图标类的进行合并,而后经过css sprite进行分割。
在构建工程时,以前一直使用的是公司内别人基于gulp开发的的maven插件进行构建,最后由maven负责打包。每次须要修改什么东西的时候都要求助于工具开发者,实在是有一种寄人篱下的感受。并且功能实在有限,集成到maven中又缺少足够的灵活性,已经适应不了蓬勃的需求变化啦。
如今前端构建工具这么多,何不本身搞呢,后面也能自由修改和管理。因而就google了一翻。
如今构建工具备不少,主流的主要有gulp、grunt、webpack等,因而我选择了grunt。没有什么别的缘由,就是由于grunt的logo比较酷炫。看下图:
前面说了,咱们对grunt的需求主要就是js、css压缩合并,静态资源文件签名。另外还有一点须要注意下,就是文件签名后,文件名前面会多一串hash值,因此全部的原来引用这些资源的地方都要修改成签名后的文件名。RequireJS的路径配置也要修改。
看了一下grunt的插件列表,基本知足要求,惟独没有一个靠谱的替换文件名的插件(也有多是我没有找到)。怎么办,本身动手丰衣足食。
综合考虑了一下,决定采用以下grunt工做流。
clean:build copy cssmin filerev replaceRefrence mkRequirePath clean:package uglify
其中clean、copy、cssmin、filerev和uglify都有现成的grunt插件,并且除了filerev之外都是grunt团队出品的插件,应该比较可靠。其余的两个,replaceRefrence和mkRequirePath,就是我本身开发的了。
replaceRefrence的想法很简单,就是经过filerev插件生成的签名先后的文件映射grunt.filerev.summary
和正则表达式来替换目标目录中的每一个文件中的引用。正则表达式也很简单,就是匹配某一路径开头,以具体的文件后缀(.js.html.css.png等)结尾的文件路径字符串。正则表达式的简要形式以下:
var regExp = /(\/start\/)(\.js|\.html|\.css|\.png|\.jpg|\.gif)/g
其中start就是路径开头。
有了正则表达式,而后经过String.replace(regExp, function(){})
将匹配到的字符串替换掉。具体样例以下:
var fileData = fs.readFileSync(filePath, {encoding:"utf8"}); var newFileData = fileData.replce(regExp, function (str) { return fileMap[str] ? fileMap[str] : str; });
注意: 这里的fileMap是
grunt.filerev.summary
通过必定的处理以后的结果。
经过以上方法就能够将html、css、js文件中引用到的其余文件替换成签名后的文件了。
完成上面的工做以后,就只剩一个任务了,就是如何让RequireJS认识这些签名后的文件?个人想法是,经过
requirejs.config({ paths: { module1: "module1/js", module2: "module2/js" } });
的方式,将签名后的文件路径配置进来。
思路与replaceRefrence同样,仍是经过grunt.filerev.summary
来作。具体的思路以下:
如上面的代码所示,咱们通常都会在paths中设置某个路径的快捷方式,即
require("module1/a.js") 等同于 module1/js/a.js
因此,咱们只须要将这些原始的路径配置拿到以后,根据这些原始配置与grunt.filerev.summary
中的具体路径进行一次匹配处理,就能够将grunt.filerev.summary
输出做为RequireJS的路径配置对象了。
具体的方法以下:
原来grunt.filerev.summary
中一条记录多是
"module1/js/a.js": "module1/js/1234567.a.js"
如今咱们知道,代码中经过
require("module1/a")
加载的js模块,最终要别识别成
module1/js/1234567.a.js
那咱们只须要生成这样一条记录:
"module1/a": "module1/js/1234567.a.js"
具体作法就是用原始路径配置中的kv对中的v匹配grunt.filerev.summary
每条记录中的key,若是key以v开头,则将key作以下处理:
key = k + key.substring(v.length);
这样就生成了正确的路径配置了。
最后一步要作的就是将生成的路径配置保存为一个manifest.js文件,插入到RequireJS的下方,如:
<script type="text/javascript" src="path1/require.js"></script> <script type="text/javascript" src="path2/manifest-2015-11-15.js"></script>
到这里,整个构建过程就基本OK了。