• 记一次前端工程构建


    需求背景

    我所在的项目组主要负责公司的A产品A1模块的界面开发。经过上半年紧锣密鼓、加班加点地开发之后,终于在7月份在国内的L局点成功上线。当时那个激动啊,苦逼的生活终于过去了,大家都跟我high起来!可是到了下半年,由于公司市场人员的给力表现,又在海外开拓了D局点和T局点,真是喜(yu)大(ku)普(wu)奔(lei)啊!

    由于L局点的需求还没有明确,所以L局点的事情先按住不表,先说说D局点的需求。
    其实,客户的实际要求也不多,对于界面来说,无非是整体风格要与客户现有的产品保持一致。所以最终预计的工作量就是换换主题色而已,一切都是如此的easy,大家接着high!

    简单修改之后就给一线发了一个联调版本。

    在一线将版本安装完成之后,与客户联调过程中(我们的产品是要嵌入到客户系统中,作为客户系统的一个模块工作),客户对界面提了很多意见(该局点的客户都比较严(jiao)谨(zhen))。

    目前来看,页面的国际化文件要准备两份(国内的L局点中文+英文,海外的D局点英文+德文),样式要准备两套(L局点一套,D局点一套)。如果客户再对页面结构有一定的要求,比如要变更页面的布局、元素等,那么html也要准备两份了。
    最终分析来看,最好是能再做一套页面出来。

    按照公司现在的策略,代码分支只能有一个,做出来的版本不能有任何对于局点的定制,也就是说这个版本要在所有局点都能安装使用。

    现在问题来了,如何在一套代码中支持两套页面呢?

    方案制定

    我们知道,一旦在页面中引用一个静态资源,那么这个资源的路径就定死了,不能改动。比如,我们的页面上要使用到公司的logo,这个logo图片的路径一旦确定,就不能再修改了,但是D局点要求将logo换成客户的logo。

    既然公司不允许在做版本的时候进行任何的定制,那么怎么解决这个问题,想了半天,对现有代码目录做一下调整,最终确定了如下方案:

    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     |
    • public目录 存放所有对外开放的静态资源文件,public以外的其他目录外界不可访问。
    • base目录 主要放置与后台的数据接口模块,也就是公共Model层。另外还放置公共的、框架性的样式文件以及一些工具类。
    • uiwidget目录 放置组件库
    • custom目录 页面最终的工作目录
    • pageL目录 L局点要求的页面
    • pageD目录 D局点要求的页面

    剩余要做的:
    一是在打包的时候将pageL的内容拷贝到custom目录中,打一个L局点的ui包,再将pageD的内容拷贝到custom目录中,打一个D局点的UI包。最终将这两个ui包打入一个war包中。
    二是新增一个脚本,在安装完成之后,根据局点决定使用哪个ui包,删除无用的ui包。

    先说明一下为何要这样划分目录。
    除了页面变动以外,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了一翻。

    现在构建工具有很多,主流的主要有gulpgruntwebpack等,于是我选择了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了。

  • 相关阅读:
    ST3 插件和技巧
    博客园中 代码使用 sublime-text 的主题
    JavaScript中的 prototype 和 constructor
    JavaScript 代码小片段
    JavaScript 静态方法和实例方法
    JavaScript 闭包的例子
    关于linux 交叉编译器的安装
    scrapy 实现mysql 数据保存
    django 实现 内网访问 和 用花生壳进行内网穿透
    django 中从外界借助多个网站时 static 的存放和整理
  • 原文地址:https://www.cnblogs.com/tangshiguang/p/6746116.html
Copyright © 2020-2023  润新知