前端自动化构建(一)

背景

互联网的发展推动了整个web前端技术快速的向前,各种前端库/框架百花齐放。在多样化的同时对于web开发人员来说变得更复杂了。
在这种背景下,我们需要了解新的知识,使用新的工具来降低这种复杂度。
本文重点总结前端自动化构建在前端workflow的作用。初入前端半年,总结不到位的地方请各位大神多多提点。


前端自动化

前端自动化范围非常广,是很复杂的工程问题,我将其分为三类:

  • 自动化构建(开发)
  • 自动化测试(开发&测试)
  • 自动化发布

其中,自动化构建是将源代码利用工具自动转换成用户可以使用的目标的过程。'目标'在这里指的是js,css,html的集合。'用户'可以是你、测试或者真正的用户。

这些工具使我们能够在更高层次上工作,并自动化重复性任务,从而节省时间;工具简化了我们的工作流程(workflow),使我们可以专注于创造性的工作,而不是花费数小时的时间浪费在繁琐的任务上。例如通过gulp,我们可以让它监听每个源文件的变化,一旦你按下ctrl+s之后,它会自动将新变化内容显示在浏览器中。

这些工具构成的系统,可以称之为构建系统(build system);并且这些工具可以叫做自动化构建工具,其本质是插件或者脚本程序,能代替人去执行繁琐又容易出错的任务。


前端workflow

在开始介绍自动化构建前,我们先从前端的基础workflow开始,一步一步引入自动化构建,逐步完成我们的workflow。

base workflow.jpg

如图是很常见的前端基础工作流程,这边有几个明显的问题

  1. 文件大小—都是源码发布,所有文件都没有经过压缩合并处理,客户端访问需要逐个建立http会话下载资源,会很慢。

  2. 一致性—没有版本管理,假设果客户端已经缓存a.jpg文件,而事实上你新的发布已经修改了a.jpg,客户端需要手动刷新缓存才能看到新的内容。

  3. 效率—你每次修改新的内容都需要重新推送到webSrv并且手动刷新浏览器。

如果你的工作流跟上图不一致也没有关系,因为我只是抽出了基础的部分。

为了解决上面的workflow提到的几个问题,我们需要对源码进行一些操作。下面是改进后的流程:

优化后的workflow.jpg

将html、css和js进行minify操作不会对它们影响它们的功能,minify之后将多个css合并成1个css,同理将多个js合并成1个js。这样在加载他们的时候会更快,不需要建立多个http连接。

在上面的流程图中,作为输入资源的是我们源码(css、js、html),进行minify,concat的处理输出到目的的区域。

实际对照我们的开发环境中,通常本地IDE的项目架构会有两个顶级的目录,一个是/src—目录存储着你所编写的项目源码;另一个是/build—源码经处理后可用于交付的代码存放处,即目的区域。

也有人喜欢用dist(distribution的缩写)来命名目的区域,这个跟开发规范和习惯有关系,我一般将用于测试的构建版本放在build,将用于生产的构建版本放在dist中。

我们来看一个表格:

目录 Linux Window
src /home/kikupotter/myweb/src D:/myweb/src
build /home/kikupotter/myweb/build D:/myweb/src

在项目开始的时候,强烈建议将src和build独立分开,刚开始写前端代买的时候时候,将所有内容都放在src中,特别不规范。以下总结了关于build的注意事项:

  1. 禁止修改build(dist)目录中构建好的内容!!!一旦文件被修改就会导致src和build里头的内容不一致,如果遇到问题很难被定位

  2. 尽可能在build版本中进行测试,而不是src中。因为build是最终的在客户端运行的版本,这样不仅可以测试功能,还能发现构建后的一些潜在的问题。

  3. 如果你正在使用git(gitlab/github)作为项目版本管理,务必将build添加到ignore file中,一般情况下只要保证src的版本就能完整构建出功能一致的build版本。

经过以上步骤,我们来看现在的实际工作流向:


workflow-src-build.png

在此基础上,假设我们有两个js文件:src/js/a.js,src/js/b.js,我们将它minify之后,合并成build/js/all.min.js;同理,有两个css文件:src/css/first.css,src/css/second.css,我们将它minify之后,合并成build/css/all.min.css。

最后还需要处理一个明显的问题,一般情况下你的src/index.html会引用了上面的a.js、b.js、first.css、second.css.

<head>
  <script src="/js/a.js"></script>
  <script src="/js/b.js"></script>
  <link rel="stylesheet" media="all" href="/css/first.css">
  <link rel="stylesheet" media="all" href="/css/second.css">
</head>

经处理的静态资源名称都变了(a.js/b.js->all.min.js ,first.css/second.js->all.min.css),这样会导致build发布后,加载index.html时出现404。所以我们在有合并操作时,需要同步修改build/index.html的引用(其他引用的地方也需要修改)

<head><script src="/js/all.min.js"></script><link rel="stylesheet" media="all" href="/css/all.min.css"></head>

到这里为止,我们将build目录的内容开心的push到远程websrv,打开浏览器查看你最新更改的内容。你可能会好奇,我们是如何来管理src到build这个构建的过程,并且如何保证构建后的build代码跟src功能一致?我们不可能靠手工方式来进行构建或者保证他们的一致性,这样效率太低而且容易出错。所以下面就引出我们的自动化构建工具。


使用工具实现自动化构建流程

构建工具是一个能将前端源码转换成可交付代码过程,并且这个转换过程是可以是完全不用人工干预,即所谓的自动化构建。

任何时候,只要你的源代码有变动,构建工具就能自动的将源码转换成最新的构建版本,并且自动在浏览器中显示刚才的变更。

可用的自动化构建工具

目前的主流的自动化构建工具很多,其核心的理念都是一致的,让机器去做哪些枯燥的重复性的劳动,你只需要编写好构建工具(build tool)的配置文档,这些小机器人就能按照你的预定任务将源码进行构建,最终呈现在你面前的是一个可交付的代码,它可能正在交付给测试,或者已经通过测试具备上线条件的代码。

常用的构建工具有:
1. grunt
2. gulp

我平常使用的是gulp,原因:
1.我熟悉Linux,gulp的工作原理跟linux的管道|非常类似,容易理解。
2.gulp使用js语法,插件式比较好管理,学习成本低,并且能解决我大部分的构建问题。

准备构建工具配置文件

配置文件描述所有的构建细节,文件中的内容实际上是定义构建工具的task集合。每个task执行特定的构建任务,task之前有依赖关系,并且多个task可以组合起来实现完整的构建工作。
在gulp中这个文件通常为gulpfile.js,如果是grunt这个文件通常为gruntfile.js:

目录 Linux Window
src /home/kikupotter/myweb/src D:/myweb/src
build /home/kikupotter/myweb/build D:/myweb/src
gulp /home/kikupotter/myweb/gulpfile.js D:/myweb/gulpfile.js
grunt /home/kikupotter/myweb/gruntfile.js D:/myweb/gruntfile.js

定义构建文件中的task

构建工具是根据构建文件中定义的task来执行的,如gulpfile.js里定义的gulp.task('copyfile',fn),其功能时将src内容拷贝到build中;通常可以在命令行中运行:

bash#gulp copyfile

构建任务可以组合,也可以被其他任务调用,这就意味着任务之间有依赖关系。我们通常会定义clean任务,其功能是每次开始构建时都将build目录清空,保证新的构建不会有历史记录的影响。所以新的构建应该是这样:


//定义clean任务

task('clean',fn)

//定义copyfine任务

task('copyfile',fn)

//bulid任务将clean和copyfile组合在一起,每次运行build都会先执行clean后copyfile,他们之前有依赖关系

task('build',['clean','copyfile'])

综上,一个task需要定义需要如下信息:

  1. taskname — 一个有意义的task名称,切记不要取task('a',fn1),task('b',fn2),无法确认具体是干啥的。

  2. 注释(功能描述) — 写注释或者功能描述,目的是让团队其他人知道你定义task的功能。

  3. 依赖 — 任务组合在一起时需要关注任务之间的依赖关系,你不可能将clean放到copyfile之后运行,这样你会得不到任何东西(我就这么愚蠢过T T)。

  4. 功能(fn)—通常是一段代码或是配置,用来实现task具体的功能。


编写配置文件定义构建流程

本文不会实际的编写gulpfile.js,每种工具的配置语法和表现形式不一样。通过伪代码的方式表达,主要是体现思路,也好理解。
简单执行构建任务:

base-build.jpg
task clean说明

删除build构建目录中的所有内容。

task build说明

build有4应该有4个任务task-scripts,task-css,task-images,task-html,分别对应处理4种静态资源,并且将处理完的文件移动到build的4个目录。在这一步中可以有两种运行方式(选择具体的执行方式可以查看相应工具的文档说明):

1. 异步执行,4个任务同时跑,互补影响。

2. 同步执行,依次执行。

不管是那种运行方式,这里结果是一样的,并且异步会更快。

task-scripts

将src/js/a.js,src/js/b.js:minify->合并->移动到build/script/all.min.js

task-css

将src/js/first.js,src/js/second.js minify->合并->移动到build/script/all.min.css

task-images

将src/images图片:逐个压缩->移动到build/images目录。

task-html

将src/index.html :修改index.html中静态资源引用指到build**->minify->移动到build/index.html

task执行完成

task执行完成,所有处理完的资源都保存在build目录中,可以给网站正常使用。build的目录结构一般在task里定义,根据工程规范进行命名。还有如下两种常见的目录结构:build/(libs,scripts,styles,images,html)
、build/scripts|build/assets/(libs,css,images,html)


插件

一般每个任务都有对应的插件来完成相应的功能,我们不需要额外写大量的代码来处理,只需安装所需功能的插件,进行一些基础的配置就能完成任务。
下表对应上小结中相关的插件:

Task gulp插件 grunt插件
clean gulp-clean grunt-contrib-clean
scripts gulp-uglify grunt-contrib-uglify
css gulp-minify-css grunt-contrib-cssmin
images gulp-imagemin grunt-contrib-imagemin
html gulp-htmlmin grunt-contrib-htmlmin

改善之后的workflow
优化后的workflow.jpg

我们所需要关心的事情是如何高效编写源码,剩下的构建工作都交给工具。PS(现在人工智能发展这么快,哪天出个智能工具将设计直接能转换成源码,那真的就失业了,可怕。。)

这个workflow还有几个步骤(发布,刷新页面等)。我还想再懒一点,我不想手动执行构建,我想的是每次修改完src的文件后,按下ctrl+s,构建工具就能自动运行一个默认的构建任务,在本地启动一个websrv而不是每次都推送到远程websrv,之后自动打开或者刷新浏览器,将新的变更显示屏幕上。

剩下可构建的部分.jpg

我们还需要几个task来完成上面提到的自动化任务。

task watch

如果每次修改完内容都需要手动的执行构建任务,这样会非常繁琐。有可能因为敲错参数导致构建失败。凡是需要手动重复执行的任务我们都要想办法将它自动化。

watch就能完美的处理这个问题:

task-watch.jpg
task websrv

大部分时间构建好的代码都是拿来测试,我们不需要每次都将build代码push到远端websrv。并且远端通常是linux服务,对于不熟悉linux的开发会带来许多困扰,如权限、启停websrv等问题,同时也节约推送的时间。所以在本地启动一个websrv是非常明智的,通过本地服务直接访问页面。


task-localsrv.jpg
task refresh browser

每次修改src后,最终都能自动在本地浏览器中打开,并且显示最新的内容。不需要再每次都ctrl+F5.

task-refresh.jpg

利用构建工具能完成上述的几个任务,他们都有相应的插件。

Task gulp插件 grunt插件
watch gulp-watch grunt-contrib-watch
local-websrv browser-sync grunt-contrib-connect
refresh-browser browser-sync grunt-contrib-watch

最后,我们单独的提取出所有可以自动化的流程:

all-task.jpg
我们最终的构建文件
task 依赖 描述
default build 默认的task,不需要任何参数
build clean, scripts, styles, images, html 对源码进行minify,合并后拷到build目录
watch local-websrv,refresh-browser 启动本地服务,监听src目录,任何变化都会触发default任务,将最新的内容在客户端中展示。

构建工具还能做什么

处理上面提到的内容,构建工具还有其他方面的功能,这里我罗列出我有用过的,没用过暂时不贴。

Linting

简单的说Linintg就是对源码文件进行静态分析,语法检查。省去大量的时间放在语法检测上。

  • JavaScript可以使用jshint的工具进行自动检查

    1. 检查js语法让代码更健壮

    2. 检查js代码风格提高代码质量,易维护。

    3. 检查安全和性能相关内容

  • Css可以使用csslint工具进行自动检查

    1. 检查css语法

    2. 检查css代码风格

  • Html可以使用htmlhint工具进行自动检查

    1. 检查css语法
    2. 检查css代码风格

同样他们有对应的插件:

功能 gulp grunt
Lint css gulp-csslint grunt-contrib-csslint
Lint js gulp-jshint grunt-contrib-jshint
Lint html gulp-htmlhint grunt-htmlhint
发布

一般公司都会有前端发布系统,我们只需要把git地址丢给发布系统剩下的工作就不需要关系了。如果有个人博客,没有时间去独立整理一套发布系统,这个功能是你需要的。

功能 gulp插件 grunt插件
FTP gulp-ftp grunt-ftp-deploy
Github pages gulp-git-pages grunt-gh-pages
rsync gulp-rsync grunt-rsync

同样他们有对应的插件:

功能 gulp插件 grunt插件
FTP gulp-ftp grunt-ftp-deploy
Github pages gulp-git-pages grunt-gh-pages
rsync gulp-rsync grunt-rsync

总结

构建工具可以用来做很多事情,甚至可以自行开发插件来实现你所需的功能,因此它容易被滥用。

所以这里有一些简单有效的原则可以帮助我们正确使用构建工具。

  1. src是你的,build是构建工具的,互不侵犯

    • 不编辑build目录的任何内容。

    • 不用构建工具对src目录进行修改操作。

  2. 每个tack尽可能小,并且可以被组合使用

  3. 日常workflow对应到一个main(default)task.

  4. 不是常规工作流的任务独立执行,不放到main task中。

    • main(default) task 常规开发,用户本地环境

    • deploy task 常规发布,用于生产

  5. 不应该在构建文件中使用跟用户鉴权相关的信息(例如账号密码)

  6. 引入source map,方便本地开发调试


写在最后

笔者前端水平有限,希望在日常工作中不断学习总结,提升自己。我自己本地其实有工作笔记,记录在有道云中。如今分享出来,一是对自己工作的总结(云笔记比较零散),二是希望对也是刚入前端的同学们有一些帮助(不要是误导就行哈),最后总结不对地方烦请各路大神指正,谢谢大家!!!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • gulpjs是一个前端构建工具,与gruntjs相比,gulpjs无需写一大堆繁杂的配置参数,API也非常简单,学...
    井皮皮阅读 1,274评论 0 10
  • gulpjs是一个前端构建工具,与gruntjs相比,gulpjs无需写一大堆繁杂的配置参数,API也非常简单,学...
    依依玖玥阅读 3,123评论 7 55
  • gulpjs是一个前端构建工具,与gruntjs相比,gulpjs无需写一大堆繁杂的配置参数,API也非常简单,学...
    build1024阅读 518评论 0 0
  • gulpjs是一个前端构建工具,与gruntjs相比,gulpjs无需写一大堆繁杂的配置参数,API也非常简单,学...
    小裁缝sun阅读 897评论 0 3
  • 在现在的前端开发中,前后端分离、模块化开发、版本控制、文件合并与压缩、mock数据等等一些原本后端的思想开始...
    Charlot阅读 5,388评论 1 32