zoukankan      html  css  js  c++  java
  • 记一次前端工程构建

    需求背景

    我所在的项目组主要负责公司的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了。

  • 相关阅读:
    119. Pascal's Triangle II
    118. Pascal's Triangle
    112. Path Sum
    111. Minimum Depth of Binary Tree
    110. Balanced Binary Tree
    108. Convert Sorted Array to Binary Search Tree
    88. Merge Sorted Array
    83. Remove Duplicates from Sorted List
    70. Climbing Stairs
    陌陌面试经历
  • 原文地址:https://www.cnblogs.com/bingooo/p/4966935.html
Copyright © 2011-2022 走看看