背景
互聯(lián)網(wǎng)的發(fā)展推動(dòng)了整個(gè)web前端技術(shù)快速的向前,各種前端庫(kù)/框架百花齊放。在多樣化的同時(shí)對(duì)于web開(kāi)發(fā)人員來(lái)說(shuō)變得更復(fù)雜了帕涌。
在這種背景下噩茄,我們需要了解新的知識(shí)下面,使用新的工具來(lái)降低這種復(fù)雜度。
本文重點(diǎn)總結(jié)前端自動(dòng)化構(gòu)建在前端workflow的作用绩聘。初入前端半年沥割,總結(jié)不到位的地方請(qǐng)各位大神多多提點(diǎn)。
前端自動(dòng)化
前端自動(dòng)化范圍非常廣凿菩,是很復(fù)雜的工程問(wèn)題,我將其分為三類(lèi):
- 自動(dòng)化構(gòu)建(開(kāi)發(fā))
- 自動(dòng)化測(cè)試(開(kāi)發(fā)&測(cè)試)
- 自動(dòng)化發(fā)布
其中机杜,自動(dòng)化構(gòu)建是將源代碼利用工具自動(dòng)轉(zhuǎn)換成用戶(hù)可以使用的目標(biāo)的過(guò)程。'目標(biāo)'在這里指的是js,css,html的集合衅谷。'用戶(hù)'可以是你椒拗、測(cè)試或者真正的用戶(hù)。
這些工具使我們能夠在更高層次上工作获黔,并自動(dòng)化重復(fù)性任務(wù)陡叠,從而節(jié)省時(shí)間;工具簡(jiǎn)化了我們的工作流程(workflow)肢执,使我們可以專(zhuān)注于創(chuàng)造性的工作枉阵,而不是花費(fèi)數(shù)小時(shí)的時(shí)間浪費(fèi)在繁瑣的任務(wù)上。例如通過(guò)gulp,我們可以讓它監(jiān)聽(tīng)每個(gè)源文件的變化,一旦你按下ctrl+s之后,它會(huì)自動(dòng)將新變化內(nèi)容顯示在瀏覽器中预茄。
這些工具構(gòu)成的系統(tǒng)兴溜,可以稱(chēng)之為構(gòu)建系統(tǒng)(build system);并且這些工具可以叫做自動(dòng)化構(gòu)建工具,其本質(zhì)是插件或者腳本程序,能代替人去執(zhí)行繁瑣又容易出錯(cuò)的任務(wù)。
前端workflow
在開(kāi)始介紹自動(dòng)化構(gòu)建前耻陕,我們先從前端的基礎(chǔ)workflow開(kāi)始拙徽,一步一步引入自動(dòng)化構(gòu)建,逐步完成我們的workflow诗宣。
如圖是很常見(jiàn)的前端基礎(chǔ)工作流程,這邊有幾個(gè)明顯的問(wèn)題:
文件大小—都是源碼發(fā)布,所有文件都沒(méi)有經(jīng)過(guò)壓縮合并處理膘怕,客戶(hù)端訪(fǎng)問(wèn)需要逐個(gè)建立http會(huì)話(huà)下載資源,會(huì)很慢。
一致性—沒(méi)有版本管理,假設(shè)果客戶(hù)端已經(jīng)緩存a.jpg文件,而事實(shí)上你新的發(fā)布已經(jīng)修改了a.jpg,客戶(hù)端需要手動(dòng)刷新緩存才能看到新的內(nèi)容召庞。
效率—你每次修改新的內(nèi)容都需要重新推送到webSrv并且手動(dòng)刷新瀏覽器岛心。
如果你的工作流跟上圖不一致也沒(méi)有關(guān)系,因?yàn)槲抑皇浅槌隽嘶A(chǔ)的部分篮灼。
為了解決上面的workflow提到的幾個(gè)問(wèn)題忘古,我們需要對(duì)源碼進(jìn)行一些操作。下面是改進(jìn)后的流程:
將html诅诱、css和js進(jìn)行minify操作不會(huì)對(duì)它們影響它們的功能髓堪,minify之后將多個(gè)css合并成1個(gè)css,同理將多個(gè)js合并成1個(gè)js。這樣在加載他們的時(shí)候會(huì)更快,不需要建立多個(gè)http連接干旁。
在上面的流程圖中驶沼,作為輸入資源的是我們?cè)创a(css、js争群、html)商乎,進(jìn)行minify,concat的處理輸出到目的的區(qū)域。
實(shí)際對(duì)照我們的開(kāi)發(fā)環(huán)境中祭阀,通常本地IDE的項(xiàng)目架構(gòu)會(huì)有兩個(gè)頂級(jí)的目錄鹉戚,一個(gè)是/src—目錄存儲(chǔ)著你所編寫(xiě)的項(xiàng)目源碼;另一個(gè)是/build—源碼經(jīng)處理后可用于交付的代碼存放處专控,即目的區(qū)域抹凳。
也有人喜歡用dist(distribution的縮寫(xiě))來(lái)命名目的區(qū)域,這個(gè)跟開(kāi)發(fā)規(guī)范和習(xí)慣有關(guān)系,我一般將用于測(cè)試的構(gòu)建版本放在build,將用于生產(chǎn)的構(gòu)建版本放在dist中伦腐。
我們來(lái)看一個(gè)表格:
目錄 | Linux | Window |
---|---|---|
src | /home/kikupotter/myweb/src | D:/myweb/src |
build | /home/kikupotter/myweb/build | D:/myweb/src |
在項(xiàng)目開(kāi)始的時(shí)候赢底,強(qiáng)烈建議將src和build獨(dú)立分開(kāi),剛開(kāi)始寫(xiě)前端代買(mǎi)的時(shí)候時(shí)候柏蘑,將所有內(nèi)容都放在src中幸冻,特別不規(guī)范。以下總結(jié)了關(guān)于build的注意事項(xiàng):
禁止修改build(dist)目錄中構(gòu)建好的內(nèi)容!!!一旦文件被修改就會(huì)導(dǎo)致src和build里頭的內(nèi)容不一致咳焚,如果遇到問(wèn)題很難被定位
盡可能在build版本中進(jìn)行測(cè)試洽损,而不是src中。因?yàn)閎uild是最終的在客戶(hù)端運(yùn)行的版本革半,這樣不僅可以測(cè)試功能碑定,還能發(fā)現(xiàn)構(gòu)建后的一些潛在的問(wèn)題。
如果你正在使用git(gitlab/github)作為項(xiàng)目版本管理又官,務(wù)必將build添加到ignore file中,一般情況下只要保證src的版本就能完整構(gòu)建出功能一致的build版本延刘。
經(jīng)過(guò)以上步驟,我們來(lái)看現(xiàn)在的實(shí)際工作流向:
在此基礎(chǔ)上六敬,假設(shè)我們有兩個(gè)js文件:src/js/a.js,src/js/b.js碘赖,我們將它minify之后,合并成build/js/all.min.js外构;同理普泡,有兩個(gè)css文件:src/css/first.css,src/css/second.css,我們將它minify之后典勇,合并成build/css/all.min.css劫哼。
最后還需要處理一個(gè)明顯的問(wèn)題,一般情況下你的src/index.html會(huì)引用了上面的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>
經(jīng)處理的靜態(tài)資源名稱(chēng)都變了(a.js/b.js->all.min.js ,first.css/second.js->all.min.css)伤溉,這樣會(huì)導(dǎo)致build發(fā)布后般码,加載index.html時(shí)出現(xiàn)404。所以我們?cè)谟泻喜⒉僮鲿r(shí)乱顾,需要同步修改build/index.html的引用(其他引用的地方也需要修改):
<head><script src="/js/all.min.js"></script><link rel="stylesheet" media="all" href="/css/all.min.css"></head>
到這里為止板祝,我們將build目錄的內(nèi)容開(kāi)心的push到遠(yuǎn)程websrv,打開(kāi)瀏覽器查看你最新更改的內(nèi)容走净。你可能會(huì)好奇券时,我們是如何來(lái)管理src到build這個(gè)構(gòu)建的過(guò)程,并且如何保證構(gòu)建后的build代碼跟src功能一致?我們不可能靠手工方式來(lái)進(jìn)行構(gòu)建或者保證他們的一致性伏伯,這樣效率太低而且容易出錯(cuò)橘洞。所以下面就引出我們的自動(dòng)化構(gòu)建工具。
使用工具實(shí)現(xiàn)自動(dòng)化構(gòu)建流程
構(gòu)建工具是一個(gè)能將前端源碼轉(zhuǎn)換成可交付代碼過(guò)程,并且這個(gè)轉(zhuǎn)換過(guò)程是可以是完全不用人工干預(yù)说搅,即所謂的自動(dòng)化構(gòu)建炸枣。
任何時(shí)候,只要你的源代碼有變動(dòng)弄唧,構(gòu)建工具就能自動(dòng)的將源碼轉(zhuǎn)換成最新的構(gòu)建版本适肠,并且自動(dòng)在瀏覽器中顯示剛才的變更。
可用的自動(dòng)化構(gòu)建工具
目前的主流的自動(dòng)化構(gòu)建工具很多候引,其核心的理念都是一致的侯养,讓機(jī)器去做哪些枯燥的重復(fù)性的勞動(dòng),你只需要編寫(xiě)好構(gòu)建工具(build tool)的配置文檔澄干,這些小機(jī)器人就能按照你的預(yù)定任務(wù)將源碼進(jìn)行構(gòu)建沸毁,最終呈現(xiàn)在你面前的是一個(gè)可交付的代碼,它可能正在交付給測(cè)試傻寂,或者已經(jīng)通過(guò)測(cè)試具備上線(xiàn)條件的代碼息尺。
常用的構(gòu)建工具有:
1. grunt
2. gulp
我平常使用的是gulp,原因:
1.我熟悉Linux,gulp的工作原理跟linux的管道|非常類(lèi)似疾掰,容易理解搂誉。
2.gulp使用js語(yǔ)法,插件式比較好管理,學(xué)習(xí)成本低静檬,并且能解決我大部分的構(gòu)建問(wèn)題炭懊。
準(zhǔn)備構(gòu)建工具配置文件
配置文件描述所有的構(gòu)建細(xì)節(jié),文件中的內(nèi)容實(shí)際上是定義構(gòu)建工具的task集合拂檩。每個(gè)task執(zhí)行特定的構(gòu)建任務(wù)侮腹,task之前有依賴(lài)關(guān)系,并且多個(gè)task可以組合起來(lái)實(shí)現(xiàn)完整的構(gòu)建工作稻励。
在gulp中這個(gè)文件通常為gulpfile.js,如果是grunt這個(gè)文件通常為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 |
定義構(gòu)建文件中的task
構(gòu)建工具是根據(jù)構(gòu)建文件中定義的task來(lái)執(zhí)行的父阻,如gulpfile.js里定義的gulp.task('copyfile',fn)愈涩,其功能時(shí)將src內(nèi)容拷貝到build中;通臣用可以在命令行中運(yùn)行:
bash#gulp copyfile
構(gòu)建任務(wù)可以組合履婉,也可以被其他任務(wù)調(diào)用,這就意味著任務(wù)之間有依賴(lài)關(guān)系。我們通常會(huì)定義clean任務(wù)斟览,其功能是每次開(kāi)始構(gòu)建時(shí)都將build目錄清空毁腿,保證新的構(gòu)建不會(huì)有歷史記錄的影響。所以新的構(gòu)建應(yīng)該是這樣:
//定義clean任務(wù)
task('clean',fn)
//定義copyfine任務(wù)
task('copyfile',fn)
//bulid任務(wù)將clean和copyfile組合在一起苛茂,每次運(yùn)行build都會(huì)先執(zhí)行clean后copyfile已烤,他們之前有依賴(lài)關(guān)系
task('build',['clean','copyfile'])
綜上,一個(gè)task需要定義需要如下信息:
taskname — 一個(gè)有意義的task名稱(chēng)妓羊,切記不要取task('a',fn1),task('b',fn2)胯究,無(wú)法確認(rèn)具體是干啥的。
注釋?zhuān)üδ苊枋觯?— 寫(xiě)注釋或者功能描述侍瑟,目的是讓團(tuán)隊(duì)其他人知道你定義task的功能唐片。
依賴(lài) — 任務(wù)組合在一起時(shí)需要關(guān)注任務(wù)之間的依賴(lài)關(guān)系,你不可能將clean放到copyfile之后運(yùn)行涨颜,這樣你會(huì)得不到任何東西(我就這么愚蠢過(guò)T T)费韭。
功能(fn)—通常是一段代碼或是配置,用來(lái)實(shí)現(xiàn)task具體的功能庭瑰。
編寫(xiě)配置文件定義構(gòu)建流程
本文不會(huì)實(shí)際的編寫(xiě)gulpfile.js星持,每種工具的配置語(yǔ)法和表現(xiàn)形式不一樣。通過(guò)偽代碼的方式表達(dá)弹灭,主要是體現(xiàn)思路督暂,也好理解。
簡(jiǎn)單執(zhí)行構(gòu)建任務(wù):
task clean說(shuō)明
刪除build構(gòu)建目錄中的所有內(nèi)容穷吮。
task build說(shuō)明
build有4應(yīng)該有4個(gè)任務(wù)task-scripts,task-css,task-images,task-html逻翁,分別對(duì)應(yīng)處理4種靜態(tài)資源,并且將處理完的文件移動(dòng)到build的4個(gè)目錄捡鱼。在這一步中可以有兩種運(yùn)行方式(選擇具體的執(zhí)行方式可以查看相應(yīng)工具的文檔說(shuō)明):
1. 異步執(zhí)行,4個(gè)任務(wù)同時(shí)跑八回,互補(bǔ)影響。
2. 同步執(zhí)行驾诈,依次執(zhí)行缠诅。
不管是那種運(yùn)行方式,這里結(jié)果是一樣的乍迄,并且異步會(huì)更快管引。
task-scripts
將src/js/a.js,src/js/b.js:minify->合并->移動(dòng)到build/script/all.min.js
task-css
將src/js/first.js,src/js/second.js minify->合并->移動(dòng)到build/script/all.min.css
task-images
將src/images圖片:逐個(gè)壓縮->移動(dòng)到build/images目錄。
task-html
將src/index.html :修改index.html中靜態(tài)資源引用指到build**->minify->移動(dòng)到build/index.html
task執(zhí)行完成
task執(zhí)行完成闯两,所有處理完的資源都保存在build目錄中褥伴,可以給網(wǎng)站正常使用谅将。build的目錄結(jié)構(gòu)一般在task里定義,根據(jù)工程規(guī)范進(jìn)行命名噩翠。還有如下兩種常見(jiàn)的目錄結(jié)構(gòu):build/(libs,scripts,styles,images,html)
戏自、build/scripts|build/assets/(libs,css,images,html)
插件
一般每個(gè)任務(wù)都有對(duì)應(yīng)的插件來(lái)完成相應(yīng)的功能邦投,我們不需要額外寫(xiě)大量的代碼來(lái)處理伤锚,只需安裝所需功能的插件,進(jìn)行一些基礎(chǔ)的配置就能完成任務(wù)志衣。
下表對(duì)應(yīng)上小結(jié)中相關(guān)的插件:
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
我們所需要關(guān)心的事情是如何高效編寫(xiě)源碼屯援,剩下的構(gòu)建工作都交給工具。PS(現(xiàn)在人工智能發(fā)展這么快念脯,哪天出個(gè)智能工具將設(shè)計(jì)直接能轉(zhuǎn)換成源碼狞洋,那真的就失業(yè)了,可怕绿店。吉懊。)
這個(gè)workflow還有幾個(gè)步驟(發(fā)布,刷新頁(yè)面等)假勿。我還想再懶一點(diǎn)借嗽,我不想手動(dòng)執(zhí)行構(gòu)建,我想的是每次修改完src的文件后转培,按下ctrl+s,構(gòu)建工具就能自動(dòng)運(yùn)行一個(gè)默認(rèn)的構(gòu)建任務(wù)恶导,在本地啟動(dòng)一個(gè)websrv而不是每次都推送到遠(yuǎn)程websrv,之后自動(dòng)打開(kāi)或者刷新瀏覽器,將新的變更顯示屏幕上浸须。
我們還需要幾個(gè)task來(lái)完成上面提到的自動(dòng)化任務(wù)惨寿。
task watch
如果每次修改完內(nèi)容都需要手動(dòng)的執(zhí)行構(gòu)建任務(wù),這樣會(huì)非常繁瑣删窒。有可能因?yàn)榍缅e(cuò)參數(shù)導(dǎo)致構(gòu)建失敗裂垦。凡是需要手動(dòng)重復(fù)執(zhí)行的任務(wù)我們都要想辦法將它自動(dòng)化。
watch就能完美的處理這個(gè)問(wèn)題:
task websrv
大部分時(shí)間構(gòu)建好的代碼都是拿來(lái)測(cè)試肌索,我們不需要每次都將build代碼push到遠(yuǎn)端websrv蕉拢。并且遠(yuǎn)端通常是linux服務(wù),對(duì)于不熟悉linux的開(kāi)發(fā)會(huì)帶來(lái)許多困擾驶社,如權(quán)限企量、啟停websrv等問(wèn)題,同時(shí)也節(jié)約推送的時(shí)間亡电。所以在本地啟動(dòng)一個(gè)websrv是非常明智的届巩,通過(guò)本地服務(wù)直接訪(fǎng)問(wèn)頁(yè)面。
task refresh browser
每次修改src后份乒,最終都能自動(dòng)在本地瀏覽器中打開(kāi)恕汇,并且顯示最新的內(nèi)容腕唧。不需要再每次都ctrl+F5.
利用構(gòu)建工具能完成上述的幾個(gè)任務(wù),他們都有相應(yīng)的插件瘾英。
Task | gulp插件 | grunt插件 |
---|---|---|
watch | gulp-watch | grunt-contrib-watch |
local-websrv | browser-sync | grunt-contrib-connect |
refresh-browser | browser-sync | grunt-contrib-watch |
最后枣接,我們單獨(dú)的提取出所有可以自動(dòng)化的流程:
我們最終的構(gòu)建文件
task | 依賴(lài) | 描述 |
---|---|---|
default | build | 默認(rèn)的task,不需要任何參數(shù) |
build | clean, scripts, styles, images, html | 對(duì)源碼進(jìn)行minify,合并后拷到build目錄 |
watch | local-websrv,refresh-browser | 啟動(dòng)本地服務(wù),監(jiān)聽(tīng)src目錄缺谴,任何變化都會(huì)觸發(fā)default任務(wù)但惶,將最新的內(nèi)容在客戶(hù)端中展示。 |
構(gòu)建工具還能做什么
處理上面提到的內(nèi)容湿蛔,構(gòu)建工具還有其他方面的功能膀曾,這里我羅列出我有用過(guò)的,沒(méi)用過(guò)暫時(shí)不貼阳啥。
Linting
簡(jiǎn)單的說(shuō)Linintg就是對(duì)源碼文件進(jìn)行靜態(tài)分析添谊,語(yǔ)法檢查。省去大量的時(shí)間放在語(yǔ)法檢測(cè)上察迟。
-
JavaScript可以使用jshint的工具進(jìn)行自動(dòng)檢查
檢查js語(yǔ)法讓代碼更健壯
檢查js代碼風(fēng)格提高代碼質(zhì)量斩狱,易維護(hù)。
檢查安全和性能相關(guān)內(nèi)容
-
Css可以使用csslint工具進(jìn)行自動(dòng)檢查
檢查css語(yǔ)法
檢查css代碼風(fēng)格
-
Html可以使用htmlhint工具進(jìn)行自動(dòng)檢查
- 檢查css語(yǔ)法
- 檢查css代碼風(fēng)格
同樣他們有對(duì)應(yīng)的插件:
功能 | gulp | grunt |
---|---|---|
Lint css | gulp-csslint | grunt-contrib-csslint |
Lint js | gulp-jshint | grunt-contrib-jshint |
Lint html | gulp-htmlhint | grunt-htmlhint |
發(fā)布
一般公司都會(huì)有前端發(fā)布系統(tǒng)扎瓶,我們只需要把git地址丟給發(fā)布系統(tǒng)剩下的工作就不需要關(guān)系了所踊。如果有個(gè)人博客,沒(méi)有時(shí)間去獨(dú)立整理一套發(fā)布系統(tǒng)栗弟,這個(gè)功能是你需要的污筷。
功能 | gulp插件 | grunt插件 |
---|---|---|
FTP | gulp-ftp | grunt-ftp-deploy |
Github pages | gulp-git-pages | grunt-gh-pages |
rsync | gulp-rsync | grunt-rsync |
同樣他們有對(duì)應(yīng)的插件:
功能 | gulp插件 | grunt插件 |
---|---|---|
FTP | gulp-ftp | grunt-ftp-deploy |
Github pages | gulp-git-pages | grunt-gh-pages |
rsync | gulp-rsync | grunt-rsync |
總結(jié)
構(gòu)建工具可以用來(lái)做很多事情,甚至可以自行開(kāi)發(fā)插件來(lái)實(shí)現(xiàn)你所需的功能乍赫,因此它容易被濫用瓣蛀。
所以這里有一些簡(jiǎn)單有效的原則可以幫助我們正確使用構(gòu)建工具。
-
src是你的雷厂,build是構(gòu)建工具的惋增,互不侵犯
不編輯build目錄的任何內(nèi)容。
不用構(gòu)建工具對(duì)src目錄進(jìn)行修改操作改鲫。
每個(gè)tack盡可能小诈皿,并且可以被組合使用
日常workflow對(duì)應(yīng)到一個(gè)main(default)task.
-
不是常規(guī)工作流的任務(wù)獨(dú)立執(zhí)行,不放到main task中像棘。
main(default) task 常規(guī)開(kāi)發(fā),用戶(hù)本地環(huán)境
deploy task 常規(guī)發(fā)布稽亏,用于生產(chǎn)
不應(yīng)該在構(gòu)建文件中使用跟用戶(hù)鑒權(quán)相關(guān)的信息(例如賬號(hào)密碼)
引入source map,方便本地開(kāi)發(fā)調(diào)試
寫(xiě)在最后
筆者前端水平有限缕题,希望在日常工作中不斷學(xué)習(xí)總結(jié)截歉,提升自己。我自己本地其實(shí)有工作筆記烟零,記錄在有道云中瘪松。如今分享出來(lái)咸作,一是對(duì)自己工作的總結(jié)(云筆記比較零散),二是希望對(duì)也是剛?cè)肭岸说耐瑢W(xué)們有一些幫助(不要是誤導(dǎo)就行哈)宵睦,最后總結(jié)不對(duì)地方煩請(qǐng)各路大神指正记罚,謝謝大家!!!