聊聊前端自动化

先来说说为什么要自动化。凡是要考虑到自动化时,你所做的工作必然是存在很多重复乏味的劳作,很有必要通过程序来完成这些任务。这样一来就可以解放生产力,将更多的精力和时间投入到更多有意义的事情上。随着前端开发不再是简单的作坊式作业,而成为一个复杂的工程时,还涉及到性能优化一系列工作等等,这时自动化已然是迫切的需求。

早期的网站开发

在还没有前端工程师这种分工如此明确的岗位时,大家所理解的前端工作无非就是制作网页的人,包括html、css、js等。稍微高级点的可能就是php了,可以读写数据库,可以称之为动态的网页。通过近几年的发展,分工越来越细,在大公司如BAT三家,基本是把前端分开来了,有专门的人写js,有专门的人写css。以前一个网页可以一个人搞定,包括切图,写页面到写逻辑,无非是几个资源链接拼凑起来。当然逻辑性不强,页面不重。
最简单的页面如下:


<html>
<head>
<style>....style>
head>
<body>
hello world
body>
<script>
....
script>
html>

这样几个html标签就能展示成网页了。顺便写几个逻辑,填写几个表单就差不多了。这就是早期的网页。

javascript库

有一次需求你做了一个页面,然后第二次需求,你领导又让你做了页面,只是这次与上次的逻辑都差不多,就改改样式皮肤,修改图片等等。这样你就要从原来的地方拷贝一份代码过来。如果有一天发现一个bug,你就需要修改两处地方,这使得你非常的恼火,于是就把公共的逻辑抽取出来,两个页面都引用这段代码,这样就很好的解决了这个问题。以后有第三个第四个页面,你也不会担心了。渐渐的公共的代码越来越大,又是个独立的文件,这个文件就成为了一个库文件了,就像jquery等等。

模块化

随着业务的不断扩大,页面越来越多,逻辑越来越重,之前你提取出来的库文件越来越大,功能越来越多。A页面只引用了其中的一部分函数,B页面C页面同样如此,后来你决定将库文件拆分成更小的模块,由各自的功能决定应该在哪个模块。这样一来前端开发就此演化为模块化开发方式。你开发的产品就像搭建积木一样,将各个模块组装在一起。

网页优化

好了,现在你的工程很庞大了,文件数量新增了非常的多,JS模块也很多,这时候一个页面也能加载了上十个js文件或者好几个样式文件。用户访问你的网页的时候需要把这些资源从服务器下载下来,所以理论上来说,想要加快你的网站,比必须减少下载的时间。可以从下载的数量和下载的大小出发,在不做大改变的前提下就是减少HTTP请求数以及压缩文件大小。雅虎的网页优化十四条军规中很大一部分是针对这种情况进行优化,如:
1、合并请求
2、使用雪碧图
3、压缩js、css代码(除去空格,混淆等等)
4、延迟加载

在PC时代,这些问题可能不是那么尖锐。移动互联网的兴起,对网页速度提出了更高的要求,因为网速相对比较慢。也有新的优化措施出现,比如缓存js文件等。可是要做到这些,并不是很容易。假如你的页面引入十个JS文件,这样发布出去显然是不好的,所以你要手动合并,将十个JS文件合并成一个,或者几个。然后还要将合并后的文件进行压缩。发出去之后产品经理发现有个小问题需要优化一下,这时候你又要重复刚才的工作。如果这样的事情发生三次,或者更多次,你会变得恼火。当然这只是令你恼火的一点点因素而已,更多的还有合并雪碧图等等。

经历过几次痛苦之后,你会发现性能优化与工程本身之前存在一些矛盾的地方。就是在优雅的开发的同时,兼顾性能方面的考虑实在难以做到。这时自动化工具太有必要了,将开发与发布隔离开来。按照优化的准则,利用构建工具,在发布的时候轻松一键搞定,这将是最理想化的作业方式。

一些构建工具

nodejs的出现,给前端开发带来了一些改变。在没有任何语言障碍的情况下,前端同学可以转为后台开发,nodejs带来另外的一个福音便是构建工具。之前的压缩合并工具大多是由java编写的,像雅虎的yui compressor,但对没有java基础的前端开发来说,至少要走不少弯路。然后最近一两年比较火的是国外的grunt和gulp,以及国内的FIS。相比而言,国外总是走在前头,在探索更好的开发方式,做出了很多探索。

  • grunt

之前在鸟厂的时候,在组内也尝试推广过模块化的开发方式,主要是用seajs模块加载器,关于这个的好处我这里就不展开说明了。这种方式从11年开始到现在基本已经得到了认可,或者说是未来的趋势,ECMAScript 6已经将模块化加入了规范。等到浏览器支持应该是几年之后的事情。这种模式主要是参考了nodejs,将其思想运用到浏览器上。seajs模块化的诟病之处便是网络请求,上线前面临的一个问题就是模块太多,有十几个文件,需要合并和压缩,而且还不能简单的将文件合并在一起。有一套他自己的规则。

在开发的时候,一个模块对应一个文件,如下:

//  a.js
define(function(require, export, module){
    require('./b')
    // xxxxx
});
//b.js
define(function(require, export, module){
   //  xxxxx
})

如果上线前简单的将这两个文件合并在一起,那么a模块是不能找到b模块,因此这样的做法是行不通。一个规则是根据文件的路径,给每个模块加上一个唯的id,id的值就是该模块的路径:

//  a.js
define('a', function(require, export, module){
    require('./b')
    // xxxxx
});
//b.js
define('b', function(require, export, module){
   //  xxxxx
})

所以在自动化的过程中,需要提取模块的唯一ID,然后再进行合并。

grunt的插件很多,也非常的成熟,能够满足各式各样的需求。作为新手,我是琢磨了大概半个月的时间才大概了解了其配置文件的意思,但也只是知道用,不明白个所以然。grunt的运作方式就是根据配置文件加载相应的插件,完成相应的任务(task)。当时中文资料比较少,英文也比较晦涩,尤其是配置文件中各配置项的意思不太明白。而当文件多起来了,构建的效率明显跟不上。加上组内也不太重视,所以就没怎么去折腾了。转去鹅厂之后,grunt就无疾而终了。

  • gulp

没有在实战项目中运用过,暂不说明

  • 组内的构建工具

刚来到鹅厂时,也有自己的一套构建工具,比较单一,项目不大。主要是将上文提到的seajs所带来的文件解决掉,提取模块ID,合并之。随着业务的发展,越来越不满足实际的需求,后来索性放弃了它,转而投向百度的FIS。

  • FIS构建工具

放弃之前的构建工具是因为一个尖锐的问题引起的,就是在发布的时候,有一段时间将会引起外网报错,就是页面已经出去,js文件却还是旧的,即使你在文件后面加上了查询参数也不太起作用。加上浏览器和CDN本身的缓存,这个问题一时之间找不到好的解决方案,唯一的办法就是采用新的文件,文件加上自身的hash值作为文件名。这样一来就瞄准了百度的FIS,它能够简单的实现我们所需要的功能。

核心思想是文件的定位,也就是在开发测试的阶段并不需要将文件hash,只需要在上线的那一刻来做这步。找到原有文件的依赖,将其替换为新的文件名。

理想都很美好,但现实真的很残酷。FIS上手相对于grunt是要简单些,但其一点也不简单。需要理解整体的思想才能去实施,而且小白用户基本不会使用,除非是一些简单的合并需求。但复杂的业务场景FIS是满足不了的。包括前面提到的模块ID的提取,seajs模块文件的合并等都需要进行二次开发才能够做到。

很多页面是嵌入到APP内部的,APP本身也可以做一次缓存,根据特定的文件名规进行缓存,彻底消灭了304请求,如果每次都是新文件,在服务器端也可以配置更长久的缓存期,所以这里也需要定制开发。

FIS最值得诟病的地方就是全局构建,这一点不仅使得构建速度快不起来,而且生成的新文件特别多,导致发布的时候去挑选文件。在多人协作开发的时候,会产生冲突,发布的过程极其的痛苦,不知道百度内部的人是怎么使用的。望着文件夹满是hash的文件名,瞬间就奔溃了。这是小组内部使用FIS后,血的教训。

后来决定大刀阔斧的对FIS进行改造。在构建的时候只生成相关的资源文件,核心是找到该文件所依赖的所有的资源文件。当然还是全局构建,只是在生成文件的时候不生成其他不相关的。

这样发布工作变得非常轻松,一目了然。然后配合现在的发布工具,基本上整个开发发布流程也比较顺畅了。那天小组会议讨论,构建工具的理想目标是什么,这个问题我也没有细想过,总之就是要从重复的劳动中解放出来,把时间花在值得的地方。或许可以采用一键发布,这可能就是理想,也是后期可以考虑再优化的地方。

现在组内所采用的构建工具,已经和FIS本身有很大的差别了。进行了很多地方的改造,后面有机会进行整体的开发发布流程分享,以及我的一些构建思路。

你可能感兴趣的:(javascript,技术)