<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          帶你揭開(kāi)前端自動(dòng)化構(gòu)建的神秘面紗

          共 13081字,需瀏覽 27分鐘

           ·

          2021-03-06 01:18

          • 本文已獲得原作者的獨(dú)家授權(quán),有想轉(zhuǎn)載的朋友們可以在后臺(tái)聯(lián)系我申請(qǐng)開(kāi)白哦!
          • PS:歡迎掘友們向我投稿哦,被采用的文章還可以送你掘金精美周邊!

          摸魚(yú)醬的文章聲明:內(nèi)容保證原創(chuàng),純技術(shù)干貨分享交流,不打廣告不吹牛逼。

          前言:對(duì)于我們這些日常基于腳手架項(xiàng)目開(kāi)發(fā),使用yarn/npm run start、yarn/npm run build等命令完成自動(dòng)化構(gòu)建的開(kāi)發(fā)者來(lái)說(shuō),重要的自動(dòng)化構(gòu)建仿佛變成了前端的一個(gè)黑盒知識(shí)。但是,掌握前端工程的自動(dòng)化構(gòu)建,是學(xué)習(xí)前端工程化以及進(jìn)階高級(jí)前端所必不可缺少的部分。

          通過(guò)這篇文章,我嘗試把我自己對(duì)自動(dòng)化構(gòu)建知識(shí)體系的系統(tǒng)化認(rèn)識(shí)托盤而出,希望能夠?qū)﹂w下有所幫助。同樣,我更喜歡的是您能批判我的一些觀點(diǎn)或者指出我的一些問(wèn)題,因?yàn)橹已阅娑谛小?/p>

          注意:文章的側(cè)重點(diǎn)是對(duì)自動(dòng)化構(gòu)建知識(shí)的系統(tǒng)化探討,不會(huì)是對(duì)某一個(gè)具體工具使用上的面面俱到,畢竟那是官方文檔該做的事情。

          對(duì)于自動(dòng)化構(gòu)建知識(shí)體系的系統(tǒng)化認(rèn)識(shí),從個(gè)人認(rèn)識(shí)出發(fā),我用腦圖做了以下整理:

          8b3d38f611560936afd8a23efb113f9a.webp

          接下來(lái)的行文,我都會(huì)圍繞這副腦圖展開(kāi),如果您有興趣繼續(xù)往下看下去,我希望您能在這幅圖上停留多一些時(shí)間。

          好地,按照上述腦圖中的邏輯,接下來(lái)我會(huì)分成以下幾個(gè)部分來(lái)展開(kāi)探討本文。

          • 理解前端工程的自動(dòng)化構(gòu)建
          • 實(shí)現(xiàn)前端工程的自動(dòng)化構(gòu)建:npm script方式
          • 實(shí)現(xiàn)前端工程的自動(dòng)化構(gòu)建:gulp方式
          • 其它方式實(shí)現(xiàn)前端工程的自動(dòng)化構(gòu)建

          好的,理清楚行文思路之后,進(jìn)入第一點(diǎn),理解前端工程的自動(dòng)化構(gòu)建。

          一:理解前端工程的自動(dòng)化構(gòu)建

          1.先理解一下為什么會(huì)出現(xiàn)這玩意

          簡(jiǎn)單來(lái)說(shuō),隨著前端需求和項(xiàng)目的日益復(fù)雜,出于提高開(kāi)發(fā)效率、用戶體驗(yàn)以及其它工程上的需要,我們通常會(huì)借助很多更高階的語(yǔ)法(如es6、ts、less等)或者服務(wù)(如web server)等來(lái)幫助我們更快更好的開(kāi)發(fā)、調(diào)試、增強(qiáng)一個(gè)前端工程。但是,這會(huì)導(dǎo)致一個(gè)問(wèn)題,就是我們寫的代碼會(huì)離瀏覽器或node可解析運(yùn)行的代碼越來(lái)越遠(yuǎn)。

          為了解決這個(gè)問(wèn)題,前端工程構(gòu)建的概念就逐漸豐富完整了起來(lái)。也就是說(shuō),前端工程構(gòu)建就是指前端項(xiàng)目從源代碼到一個(gè)能按需運(yùn)行(開(kāi)發(fā)環(huán)境、生產(chǎn)環(huán)境)的前端工程所需要做的所有事情。由于前端工程的構(gòu)建過(guò)程中會(huì)包含很多任務(wù)并且工程需要頻繁構(gòu)建,所以按照任何簡(jiǎn)單機(jī)械的重復(fù)勞動(dòng)都應(yīng)該讓機(jī)器去完成的思想,我們應(yīng)該自動(dòng)化去完成工程的構(gòu)建,提高構(gòu)建效率

          2.從這玩意的具體實(shí)踐角度來(lái)再理解一次

          前面提到了前端工程自動(dòng)化構(gòu)建的本質(zhì),但是這個(gè)理解離我們具體實(shí)踐它還是有點(diǎn)遠(yuǎn),下面再說(shuō)說(shuō)我對(duì)自動(dòng)化構(gòu)建實(shí)踐上的理解吧。

          我認(rèn)為,前端工程構(gòu)建的具體實(shí)踐形式就是一個(gè)任務(wù)流,完成了這個(gè)任務(wù)流中的所有任務(wù)即完成了前端工程構(gòu)建。而自動(dòng)化構(gòu)建,也就是不用手動(dòng)的執(zhí)行這個(gè)任務(wù)流中的一個(gè)個(gè)任務(wù)。

          好的,經(jīng)過(guò)上面兩點(diǎn)講解,我覺(jué)得我已經(jīng)把我對(duì)它的所有理解都已經(jīng)傾囊相授了。

          為了引導(dǎo)接下來(lái)對(duì)自動(dòng)化構(gòu)建具體實(shí)現(xiàn)的講解,下面我們?cè)購(gòu)?strong>自動(dòng)化構(gòu)建就是完成一個(gè)任務(wù)流這個(gè)實(shí)踐角度理解來(lái)展開(kāi)細(xì)致探討,也就是以下這兩點(diǎn),即:

          • 理解構(gòu)建任務(wù)流中的任務(wù)
          • 理解構(gòu)建任務(wù)流

          3.理解構(gòu)建任務(wù)流中的任務(wù)

          對(duì)比于JavaScript的函數(shù),個(gè)人對(duì)任務(wù)是這么分類的:

          • 單任務(wù):同步任務(wù)和異步任務(wù)
          • 多任務(wù):并行任務(wù)、串行任務(wù)

          同步任務(wù)和異步任務(wù)無(wú)須解釋,這里說(shuō)說(shuō)并行任務(wù)和串行任務(wù)。任務(wù)并行可以用于縮短多個(gè)任務(wù)的執(zhí)行時(shí)間。因?yàn)閚ode是單線程執(zhí)行的,我認(rèn)為它并不能縮短多個(gè)同步任務(wù)并行的執(zhí)行時(shí)間,但是構(gòu)建過(guò)程中的任務(wù)通常都會(huì)涉及到IO流,所以大部分任務(wù)都是異步任務(wù),IO任務(wù)并行可以避免IO阻塞線程執(zhí)行,進(jìn)而縮短多個(gè)任務(wù)的執(zhí)行時(shí)間。

          而任務(wù)串行可以保證任務(wù)之間的有序執(zhí)行,比如在開(kāi)發(fā)環(huán)境下,我們肯定要先執(zhí)行編譯任務(wù),然后才能執(zhí)行啟動(dòng)開(kāi)發(fā)服務(wù)器的任務(wù)。

          理解了構(gòu)建過(guò)程中的任務(wù)之后,下面再列舉一些日常開(kāi)發(fā)的構(gòu)建過(guò)程中,我們所常見(jiàn)到的任務(wù)。

          前端構(gòu)建過(guò)程中的常見(jiàn)任務(wù)

          任務(wù)名任務(wù)職責(zé)
          Eslint檢查統(tǒng)一代碼風(fēng)格
          babel轉(zhuǎn)換ES6 -> ES5
          typescript轉(zhuǎn)換Ts -> Js
          sass轉(zhuǎn)換sass -> css
          less轉(zhuǎn)換less -> css
          html、css、js壓縮.html -> .min.html、.css->.min.css、.js->.min.js
          web server開(kāi)發(fā)服務(wù)器
          js、css兼容兼容不同瀏覽器
          ......

          除了上述表格中列舉的任務(wù)之外,在不同的項(xiàng)目不同的場(chǎng)景中還會(huì)有不同的構(gòu)建任務(wù),這里就不再一一贅述了。上面說(shuō)到構(gòu)建其實(shí)就是完成一個(gè)任務(wù)流,在理解和認(rèn)識(shí)了常見(jiàn)任務(wù)之后,接下來(lái)我們理解一下前端工程當(dāng)中的任務(wù)流。

          4.理解構(gòu)建任務(wù)流

          任務(wù)流的理解可不只是多個(gè)任務(wù)這么簡(jiǎn)單,任務(wù)流是要為目的服務(wù)的,就好比生產(chǎn)一個(gè)產(chǎn)品,必須完整跑完生產(chǎn)流水線一樣。所以我們這里得從構(gòu)建目的的角度來(lái)理解任務(wù)流。

          前端構(gòu)建是為前端工程服務(wù)的,而前端工程又是為用戶服務(wù)的。對(duì)應(yīng)于開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境,前端工程可以分為開(kāi)發(fā)環(huán)境工程和生產(chǎn)環(huán)境工程,其中開(kāi)發(fā)環(huán)境工程為開(kāi)發(fā)者服務(wù),生產(chǎn)環(huán)境工程為用戶服務(wù)。

          滿足工程使用者的需求是我們構(gòu)建工程的終極目的,所以有必要投其所好,根據(jù)工程的使用者不同,完成他所需要的的一連串任務(wù),也就是任務(wù)流。這時(shí)可以根據(jù)構(gòu)建后工程的目標(biāo)使用者來(lái)劃分,把任務(wù)流分為開(kāi)發(fā)環(huán)境構(gòu)建任務(wù)流和生產(chǎn)環(huán)境構(gòu)建任務(wù)流兩種。這兩點(diǎn)認(rèn)識(shí)很重要,所以我們把它兩單獨(dú)拎出來(lái)講解。

          我們先來(lái)理解一下開(kāi)發(fā)環(huán)境的構(gòu)建任務(wù)流。

          5.理解開(kāi)發(fā)環(huán)境的構(gòu)建任務(wù)流

          開(kāi)發(fā)環(huán)境構(gòu)建任務(wù)流構(gòu)建后的工程是為開(kāi)發(fā)者服務(wù)的。開(kāi)發(fā)者需要開(kāi)發(fā)調(diào)試代碼,所以開(kāi)發(fā)環(huán)境任務(wù)流構(gòu)建的工程需要實(shí)現(xiàn)以下功能:

          功能項(xiàng)包含任務(wù)
          語(yǔ)法檢查Eslint
          語(yǔ)法轉(zhuǎn)換ES6 -> ES5、Sass -> less、Ts->Js等等
          模擬生產(chǎn)環(huán)境web開(kāi)發(fā)服務(wù)器:devServer
          易于調(diào)試sourceMap
          ......

          開(kāi)發(fā)者需要不斷修改代碼查看效果,所以除了滿足功能之外,還需要加快構(gòu)建速度并且自動(dòng)刷新,以保證良好的使用體驗(yàn)。

          優(yōu)化方式實(shí)現(xiàn)方案
          加快構(gòu)建速度devServer熱模塊替換
          自動(dòng)刷新devServer 監(jiān)聽(tīng)源代碼
          ......

          關(guān)于web開(kāi)發(fā)服務(wù)器devServer

          使用web開(kāi)發(fā)服務(wù)器可以模擬像使用nginx、tomcat等服務(wù)器軟件一樣的線上環(huán)境,它在功能以及配置上都與nginx以及tomcat類似, 最簡(jiǎn)單的配置就是指明資源路徑baseUrl以及服務(wù)啟動(dòng)ip和端口port即可。在開(kāi)發(fā)環(huán)境啟動(dòng)本地服務(wù)時(shí),配置代理可以在符合同源策略的情況下解決跨域問(wèn)題

          開(kāi)發(fā)服務(wù)器除了可以模擬線上環(huán)境之外,更加強(qiáng)大的一點(diǎn)是它可以監(jiān)聽(tīng)源代碼,實(shí)現(xiàn)熱部署和自動(dòng)刷新功能

          好的,下面我們?cè)賮?lái)理解一下生產(chǎn)環(huán)境的構(gòu)建任務(wù)流。

          5.理解生產(chǎn)環(huán)境的構(gòu)建任務(wù)流

          生產(chǎn)環(huán)境構(gòu)建任務(wù)流構(gòu)建后的工程是為用戶服務(wù)的。與開(kāi)發(fā)環(huán)境相比,它也需要語(yǔ)法檢查以及編譯功能,但不需要考慮修改以及調(diào)試代碼的問(wèn)題,它關(guān)注的是瀏覽器兼容以及運(yùn)行速度等問(wèn)題。

          功能項(xiàng)包含任務(wù)
          語(yǔ)法檢查Eslint
          語(yǔ)法轉(zhuǎn)換ES6 -> ES5、Sass -> less、Ts->Js等等
          語(yǔ)法兼容不同瀏覽器的js、css語(yǔ)法兼容
          下載速度資源壓縮與合并
          ......

          生產(chǎn)環(huán)境的優(yōu)化除了資源的下載速度之外,還可以從很多方面入手,下面是其中的一些方面以及實(shí)現(xiàn)方案。

          優(yōu)化方面實(shí)現(xiàn)方式
          下載優(yōu)化treeshaking、代碼分割、懶加載
          運(yùn)行優(yōu)化代碼上優(yōu)化性能
          離線訪問(wèn)pwa技術(shù)
          ......

          終于把任務(wù)以及任務(wù)流淺顯粗陋的講完了,接下來(lái)我們先是使用npm scripts來(lái)實(shí)現(xiàn)簡(jiǎn)單項(xiàng)目的自動(dòng)化構(gòu)建,而后學(xué)習(xí)一下Gulp工具如何實(shí)現(xiàn)復(fù)雜項(xiàng)目的自動(dòng)化構(gòu)建

          抱歉,文章那么長(zhǎng),讓你看累了、倦了。但你要知道,我他喵的寫文章更累啊,堅(jiān)持一下,然后點(diǎn)個(gè)贊和關(guān)注吧( ̄▽ ̄)~■干杯□~( ̄▽ ̄)。

          二:實(shí)現(xiàn)前端工程的自動(dòng)化構(gòu)建:npm script方式

          前面說(shuō)到,完成前端工程構(gòu)建也就是完成任務(wù)流。任務(wù)流由任務(wù)組成,而任務(wù)又由腳本代碼實(shí)現(xiàn)。

          對(duì)于任務(wù)的調(diào)用,我們?cè)诙x好任務(wù)腳本或者安裝好所需的cli模塊之后,我們只需在package.json的scripts選項(xiàng)中配置一條script,就可以方便地調(diào)用任務(wù)腳本或者cli模塊。

          cli模塊提供了腳本命令,可以使用npm/npx/yarn運(yùn)行該模塊所提供的腳本。

          這里不得不提一下node_modules/.bin文件夾,我們?cè)陧?xiàng)目中安裝的cli模塊都會(huì)有一個(gè)cmd文件出現(xiàn)在這里。當(dāng)我們?cè)陧?xiàng)目中需要調(diào)用這些cli模塊時(shí),只需yarn/npx cli模塊名的方式就可以很方便的調(diào)用這些cli模塊。

          對(duì)于任務(wù)流的調(diào)用,我們則可以借助一些可以幫助任務(wù)組合(并行和串行)的庫(kù),而后在npm script中配置一條組合任務(wù),調(diào)用它以啟動(dòng)構(gòu)建任務(wù)流。

          好的,通過(guò)上面的分析之后,我們接下來(lái)展開(kāi)講述一下npm scripts如何實(shí)現(xiàn)任務(wù)以及任務(wù)流的構(gòu)建。

          1.單任務(wù)注冊(cè)調(diào)用示例

          下面是sass轉(zhuǎn)換和ES6轉(zhuǎn)換的兩個(gè)單任務(wù)示例:

          • 注冊(cè)任務(wù):package.json中配置script
            "scripts": {
          "sass": "sass scss/main.scss css/style.css",
          "es6": "babel es6 --out-dir es5",
          },
          "devDependencies": {
          "@babel/cli": "^7.12.8",
          "@babel/core": "^7.12.9",
          "sass": "^1.29.0"
          }
          • 調(diào)用任務(wù):
          # sass轉(zhuǎn)換
          yarn sass
          # es6轉(zhuǎn)換
          yarn es6
          • 調(diào)用原理追溯

          最基本的腳本調(diào)用也就是用某個(gè)命令(如node)去執(zhí)行一個(gè)文件,這里直接使用yarn就可以觸發(fā)script中的任務(wù)難免會(huì)讓人有點(diǎn)疑惑。下面是我為您整理的任務(wù)調(diào)用追溯,理解它有時(shí)候能幫助你定位解決一些問(wèn)題。

          • sass轉(zhuǎn)換任務(wù)追溯:yarn sass(觸發(fā)任務(wù)) -> sass scss/main.scss css/style.css -> node_modules/.bin/sass.cmd -> node_modules/sass/sass.js -> ...code

          • es6轉(zhuǎn)換任務(wù)追溯:yarn es6(觸發(fā)任務(wù)) -> babel es6 --out-dir es5 -> node_modules/.bin/babel.cmd -> node_modules/@babel\cli\bin\babel.js -> node_modules/@babel\cli\lib\index.js -> ...code

          2.簡(jiǎn)單任務(wù)流構(gòu)建示例

          這里需要再重申一點(diǎn),任務(wù)流不等同于任務(wù)組合,它與構(gòu)建目的有關(guān)。這里我們假設(shè)我們前端工程的構(gòu)建目的就只是sass轉(zhuǎn)換和es6轉(zhuǎn)換,那么我們以如下形式實(shí)現(xiàn)自動(dòng)化構(gòu)建。

          • 注冊(cè)任務(wù):package.json中配置script

          注意:對(duì)于任務(wù)流中的任務(wù)組合我們這里通過(guò)npm-run-all這個(gè)庫(kù)來(lái)幫助我們實(shí)現(xiàn),這個(gè)庫(kù)提供了兩個(gè)cmd文件,nun-p.cmd實(shí)現(xiàn)任務(wù)的并行,nun-s.cmd實(shí)現(xiàn)任務(wù)的串行。

            "scripts": {
          "sass": "sass scss/main.scss css/style.css",
          "es6": "babel es6 --out-dir es5",
          "build": "run-p sass es6"
          },
          "devDependencies": {
          "npm-run-all": "^4.1.5",
          "@babel/cli": "^7.12.8",
          "@babel/core": "^7.12.9",
          "sass": "^1.29.0"
          }
          • 調(diào)用任務(wù):
          yarn build

          3.具體工程構(gòu)建示例

          下圖即是我們想要構(gòu)建的簡(jiǎn)單前端項(xiàng)目:

          cf18355ea8975a6681d0515aaaa1aa9d.webp

          這個(gè)項(xiàng)目很簡(jiǎn)單,它只包含一個(gè)html文件,一個(gè)使用了ES6語(yǔ)法js文件以及一個(gè)使用了sass語(yǔ)法的樣式文件,接下來(lái)我們就用npm script來(lái)實(shí)現(xiàn)這個(gè)簡(jiǎn)單項(xiàng)目的自動(dòng)化構(gòu)建(也即開(kāi)發(fā)環(huán)境構(gòu)建任務(wù)流和生產(chǎn)環(huán)境構(gòu)建任務(wù)流)。

          簡(jiǎn)單項(xiàng)目的自動(dòng)化構(gòu)建就是npm script實(shí)現(xiàn)自動(dòng)化構(gòu)建的使用場(chǎng)景。

          與日常開(kāi)發(fā)一樣,我們這里也把這個(gè)工程構(gòu)建分為兩個(gè)部分,即開(kāi)發(fā)環(huán)境構(gòu)建和生產(chǎn)環(huán)境構(gòu)建。下面先講講開(kāi)發(fā)環(huán)境構(gòu)建的實(shí)現(xiàn):

          (一):開(kāi)發(fā)環(huán)境構(gòu)建工程

          通過(guò)上面我們對(duì)開(kāi)發(fā)環(huán)境構(gòu)建任務(wù)流的認(rèn)識(shí),我們先理一理在這個(gè)項(xiàng)目中,開(kāi)發(fā)環(huán)境任務(wù)流至少應(yīng)該包含哪些任務(wù):

          • 需要web開(kāi)發(fā)服務(wù)器模擬生產(chǎn)環(huán)境以及實(shí)現(xiàn)源碼監(jiān)聽(tīng)和自動(dòng)刷新。
          • 對(duì)于sass文件,需要實(shí)時(shí)sass轉(zhuǎn)換,并且監(jiān)聽(tīng)源碼變化重啟開(kāi)發(fā)服務(wù)器、刷新瀏覽器。
          • 對(duì)于ES6文件,需要實(shí)時(shí)babel轉(zhuǎn)換,并且監(jiān)聽(tīng)源碼變化重啟開(kāi)發(fā)服務(wù)器、刷新瀏覽器。
          • 對(duì)于html文件,需要監(jiān)聽(tīng)源碼變化重啟開(kāi)發(fā)服務(wù)器、刷新瀏覽器。
          • ...

          對(duì)于sass和ES6修改源代碼后的實(shí)時(shí)轉(zhuǎn)換,我們可以通過(guò)加上一個(gè)watch參數(shù)實(shí)現(xiàn)。而對(duì)于所有這些需要監(jiān)聽(tīng)變化的文件,我們則統(tǒng)一放入temp文件夾下(角色好比如nginx和Tomcat的應(yīng)用存放目錄),而后讓web開(kāi)發(fā)服務(wù)器監(jiān)聽(tīng)這個(gè)temp文件夾下所有文件的變化,一旦變化即重啟并刷新瀏覽器。

          好的經(jīng)過(guò)上面任務(wù)分析之后,我們可能會(huì)把package.json的scripts以及devDependencies寫成如下樣子(核心關(guān)注scripts中的start命令):

            "scripts": {
          "sassDev": "sass scss/main.scss temp/css/style.css --watch",
          "babelDev": "babel es6/script.js --out-dir temp/es5/script.js --watch",
          "copyHtmlDev": "copyfiles index.html temp",
          "serve": "browser-sync temp --files \"temp\"",
          "start": "run-p sassDev babelDev copyHtmlDev serve"
          },
          "devDependencies": {
          "@babel/cli": "^7.12.8",
          "@babel/core": "^7.12.9",
          "browser-sync": "^2.26.13",
          "copyfiles": "^2.4.1",
          "npm-run-all": "^4.1.5",
          "sass": "^1.29.0"
          }

          (二):生產(chǎn)環(huán)境構(gòu)建工程

          通過(guò)上面我們對(duì)生產(chǎn)環(huán)境構(gòu)建任務(wù)流的認(rèn)識(shí),我們先理一理在這個(gè)項(xiàng)目中,生產(chǎn)環(huán)境任務(wù)流應(yīng)該包含哪些任務(wù):

          • 刪除上一次構(gòu)建結(jié)果任務(wù)
          • 編譯任務(wù)
          • html模板信息注入任務(wù)
          • 文件壓縮任務(wù)
          • ...

          好的經(jīng)過(guò)上面任務(wù)分析之后,我們可能會(huì)把package.json的scripts以及devDependencies寫成如下樣子(核心關(guān)注scripts中的build命令):

            "scripts": {
          "sass": "sass scss/main.scss dist/css/style.css",
          "babel": "babel es6 --out-dir dist/es5",
          "copyHtml": "copyfiles index.html dist",
          "build": "run-p sass babel copyHtml"
          },
          "devDependencies": {
          "@babel/cli": "^7.12.8",
          "@babel/core": "^7.12.9",
          "browser-sync": "^2.26.13",
          "copyfiles": "^2.4.1",
          "npm-run-all": "^4.1.5",
          "sass": "^1.29.0"
          }

          上述代碼實(shí)現(xiàn)不全,按道理說(shuō),在生產(chǎn)環(huán)境下,至少需要做代碼的兼容以及壓縮。這時(shí)我們就需要找到對(duì)應(yīng)的工具庫(kù)或者自己實(shí)現(xiàn),另外對(duì)于壓縮而言至少需要在編譯之后完成,所以需要注意多個(gè)任務(wù)間的關(guān)系。思路很簡(jiǎn)單,我偷個(gè)懶當(dāng)前就不花時(shí)間去實(shí)踐了,需要時(shí)再實(shí)現(xiàn)就行。

          4.npm script構(gòu)建總結(jié)

          在進(jìn)入Gulp實(shí)現(xiàn)前端工程的自動(dòng)化構(gòu)建之前,我覺(jué)得有必要再重申一點(diǎn):在項(xiàng)目以及構(gòu)建需求不復(fù)雜時(shí),npm scripts就可以滿足我們的構(gòu)建需求了,無(wú)需借助其它工具。

          好的,下面進(jìn)入gulp方式實(shí)現(xiàn)前端工程的自動(dòng)化構(gòu)建。

          文章都寫到這份上了,不值得你點(diǎn)個(gè)贊和關(guān)注鼓勵(lì)一下嗎?

          三:實(shí)現(xiàn)前端工程的自動(dòng)化構(gòu)建:gulp方式

          有些看官可能不太了解gulp,這里我先簡(jiǎn)單介紹一下它吧。

          Gulp是一個(gè)基于流的自動(dòng)化構(gòu)建工具,相比較于Grunt,它的構(gòu)建速度更快,任務(wù)編寫也更加簡(jiǎn)單靈活。

          安裝好gulp之后,使用gulp的流程也就基本是如下這樣:

          • 首先需要在項(xiàng)目根目錄下創(chuàng)建一個(gè)Gulp入口文件gulpfile.js
          • 然后在這個(gè)入口文件中通過(guò)暴露函數(shù)的方式注冊(cè)任務(wù)
          • 最后以根目錄作為工作目錄執(zhí)行g(shù)ulp命令即可執(zhí)行注冊(cè)任務(wù)。

          有了以上了解之后,下面我們就探討如何借助gulp這個(gè)工具來(lái)實(shí)現(xiàn)自動(dòng)化構(gòu)建吧。

          1.gulp完成各種任務(wù)調(diào)度

          (1):實(shí)現(xiàn)同步任務(wù)和異步任務(wù)

          對(duì)于新版本的Gulp來(lái)說(shuō),所有任務(wù)都是異步任務(wù),所以任務(wù)需要告訴Gulp什么時(shí)候執(zhí)行結(jié)束。以下是gulp異步任務(wù)實(shí)現(xiàn)的幾種方式(關(guān)注它們是如何通知Gulp異步任務(wù)結(jié)束的)。

          //?方式1:調(diào)用done方法主動(dòng)通知任務(wù)結(jié)束
          exports.foo?=?done?=>?{
          ??console.log('foo?task?working~')
          ??done()?//?標(biāo)識(shí)任務(wù)執(zhí)行完成
          }
          //?方式2:返回Promise,通過(guò)它的resolve/reject方法通知任務(wù)結(jié)束
          const?timeout?=?time?=>?{
          ??return?new?Promise(resolve?=>?{
          ????setTimeout(resolve,?time)
          ??})
          }
          //?方式3:返回讀取流對(duì)象,流完即自動(dòng)通知任務(wù)結(jié)束
          exports.stream?=?()?=>?{
          ??const?read?=?fs.createReadStream('yarn.lock')
          ??const?write?=?fs.createWriteStream('a.txt')
          ??read.pipe(write)
          ??return?read
          }
          //?更多方式

          (2):實(shí)現(xiàn)并行任務(wù)和串行任務(wù)

          并行任務(wù)和串行任務(wù)可以通過(guò)gulp提供的series(串行), parallel(并行)實(shí)現(xiàn)。

          const?{?series,?parallel?}?=?require('gulp');

          const?task1?=?done?=>?{
          ??setTimeout(()?=>?{
          ????console.log('task1?working~');
          ????done();
          ??},?1000)
          }

          const?task2?=?done?=>?{
          ??setTimeout(()?=>?{
          ????console.log('task2?working~');
          ????done();
          ??},?1000)??
          }

          exports.bar?=?parallel(task1,?task2);?//?并行任務(wù)bar

          exports.foo?=?series(task1,?task2);?//?串行任務(wù)foo

          (3): gulp插件任務(wù)

          Gulp生態(tài)中有很多成熟的gulp任務(wù)插件,使用它們可以很好地提高效率,如以下示例:

          const?{?src,?dest?}?=?require('gulp');
          const?cleanCSS?=?require('gulp-clean-css');
          const?rename?=?require('gulp-rename');

          exports.default?=?()?=>?{
          ??return?src('src/*.css')
          ????.pipe(cleanCSS())
          ????.pipe(rename({?extname:?'.min.css'?}))
          ????.pipe(dest('dist'))
          }

          (4): 自定義gulp任務(wù)

          如果需要定制任務(wù),或者對(duì)于我們的需求沒(méi)有較好的gulp插件,那么我們就需要自定義任務(wù),如下示例:

          const?fs?=?require('fs')
          const?{?Transform?}?=?require('stream')

          exports.default?=?()?=>?{
          ??const?readStream?=?fs.createReadStream('normalize.css');
          ??const?writeStream?=?fs.createWriteStream('normalize.min.css');
          ??//?文件轉(zhuǎn)換流
          ??const?transformStream?=?new?Transform({
          ????//?核心轉(zhuǎn)換過(guò)程
          ????transform:?(chunk,?encoding,?callback)?=>?{
          ??????const?input?=?chunk.toString();
          ??????const?output?=?input.replace(/\s+/g,?'').replace(/\/\*.+?\*\//g,?'');
          ??????callback(null,?output);
          ????}
          ??})

          ??return?readStream
          ????.pipe(transformStream)?//?轉(zhuǎn)換
          ????.pipe(writeStream)?//?寫入
          }

          2.gulp完成構(gòu)建任務(wù)流

          如下gulpfile文件,分為開(kāi)發(fā)環(huán)境構(gòu)建(develop任務(wù))和生產(chǎn)環(huán)境構(gòu)建(build任務(wù))。相對(duì)于理論認(rèn)識(shí),工具的使用只是不同實(shí)現(xiàn)方式,這里就不多贅述了,具體的邏輯可以看下面代碼中的注釋,我為您寫的很清楚了哦。

          const?{
          ??src,
          ??dest,
          ??parallel,
          ??series,
          ??watch
          }?=?require('gulp')

          const?del?=?require('del')
          const?browserSync?=?require('browser-sync')

          const?loadPlugins?=?require('gulp-load-plugins')

          const?plugins?=?loadPlugins()
          const?bs?=?browserSync.create()

          const?data?=?{
          ??menus:?[{
          ??????name:?'Home',
          ??????icon:?'aperture',
          ??????link:?'index.html'
          ????},
          ????{
          ??????name:?'Features',
          ??????link:?'features.html'
          ????},
          ????{
          ??????name:?'About',
          ??????link:?'about.html'
          ????},
          ????{
          ??????name:?'Contact',
          ??????link:?'#',
          ??????children:?[{
          ??????????name:?'Twitter',
          ??????????link:?'https://twitter.com/w_zce'
          ????????},
          ????????{
          ??????????name:?'About',
          ??????????link:?'https://weibo.com/zceme'
          ????????},
          ????????{
          ??????????name:?'divider'
          ????????},
          ????????{
          ??????????name:?'About',
          ??????????link:?'https://github.com/zce'
          ????????}
          ??????]
          ????}
          ??],
          ??pkg:?require('./package.json'),
          ??date:?new?Date()
          }
          //?css編譯??src?=>?temp
          const?style?=?()?=>?{
          ??return?src('src/assets/styles/*.scss',?{
          ??????base:?'src'
          ????})
          ????.pipe(plugins.sass({
          ??????outputStyle:?'expanded'
          ????}))
          ????.pipe(dest('temp'))
          ????.pipe(bs.reload({
          ??????stream:?true
          ????}))
          }
          //?js編譯???src?=>?temp
          const?script?=?()?=>?{
          ??return?src('src/assets/scripts/*.js',?{
          ??????base:?'src'
          ????})
          ????.pipe(plugins.babel({
          ??????presets:?['@babel/preset-env']
          ????}))
          ????.pipe(dest('temp'))
          ????.pipe(bs.reload({
          ??????stream:?true
          ????}))
          }

          //?html模板解析?????src?=>?temp
          const?page?=?()?=>?{
          ??return?src('src/*.html',?{
          ??????base:?'src'
          ????})
          ????.pipe(plugins.swig({
          ??????data,
          ??????defaults:?{
          ????????cache:?false
          ??????}
          ????}))?//?防止模板緩存導(dǎo)致頁(yè)面不能及時(shí)更新
          ????.pipe(dest('temp'))
          ????.pipe(bs.reload({
          ??????stream:?true
          ????}))
          }

          //?串行編譯、模板解析
          const?compile?=?parallel(style,?script,?page)

          //?開(kāi)發(fā)環(huán)境開(kāi)發(fā)服務(wù)器
          const?serve?=?()?=>?{
          ??watch('src/assets/styles/*.scss',?style)
          ??watch('src/assets/scripts/*.js',?script)
          ??watch('src/*.html',?page)
          ??//?watch('src/assets/images/**',?image)
          ??//?watch('src/assets/fonts/**',?font)
          ??//?watch('public/**',?extra)
          ??watch([
          ????'src/assets/images/**',
          ????'src/assets/fonts/**',
          ????'public/**'
          ??],?bs.reload)

          ??bs.init({
          ????notify:?false,
          ????port:?2080,
          ????//?open:?false,
          ????//?files:?'dist/**',
          ????server:?{
          ??????baseDir:?['temp',?'src',?'public'],
          ??????routes:?{
          ????????'/node_modules':?'node_modules'
          ??????}
          ????}
          ??})
          }

          //?開(kāi)發(fā)環(huán)境構(gòu)建流:編譯?+?啟動(dòng)開(kāi)發(fā)服務(wù)器??? src => temp
          const?develop?=?series(compile,?serve)



          //?生產(chǎn)環(huán)境下清空文件夾
          const?clean?=?()?=>?{
          ??return?del(['dist',?'temp'])
          }
          //?生產(chǎn)環(huán)境js、css、html壓縮后構(gòu)建??temp?=>?dist
          const?useref?=?()?=>?{
          ??return?src('temp/*.html',?{
          ??????base:?'temp'
          ????})
          ????.pipe(plugins.useref({
          ??????searchPath:?['temp',?'.']
          ????}))
          ????//?html?js?css
          ????.pipe(plugins.if(/\.js$/,?plugins.uglify()))
          ????.pipe(plugins.if(/\.css$/,?plugins.cleanCss()))
          ????.pipe(plugins.if(/\.html$/,?plugins.htmlmin({
          ??????collapseWhitespace:?true,
          ??????minifyCSS:?true,
          ??????minifyJS:?true
          ????})))
          ????.pipe(dest('dist'))
          }
          //?生產(chǎn)環(huán)境圖片壓縮后構(gòu)建???src?=>?dist
          const?image?=?()?=>?{
          ??return?src('src/assets/images/**',?{
          ??????base:?'src'
          ????})
          ????.pipe(plugins.imagemin())
          ????.pipe(dest('dist'))
          }
          //?生產(chǎn)環(huán)境字體壓縮后構(gòu)建???src?=>?dist
          const?font?=?()?=>?{
          ??return?src('src/assets/fonts/**',?{
          ??????base:?'src'
          ????})
          ????.pipe(plugins.imagemin())
          ????.pipe(dest('dist'))
          }
          //?生產(chǎn)環(huán)境靜態(tài)資源構(gòu)建
          const?extra?=?()?=>?{
          ??return?src('public/**',?{
          ??????base:?'public'
          ????})
          ????.pipe(dest('dist'))
          }

          //?上線之前執(zhí)行的任務(wù)???src?=>?(temp?=>)?=>?dist?
          const?build?=?series(
          ??clean,
          ??parallel(
          ????series(compile,?useref),
          ????image,
          ????font,
          ????extra
          ??)
          )

          module.exports?=?{
          ??clean,
          ??build,
          ??develop
          }

          四:其它方式實(shí)現(xiàn)前端工程的自動(dòng)化構(gòu)建

          在前端工程的自動(dòng)化構(gòu)建發(fā)展歷程中,出現(xiàn)了很多的自動(dòng)化工具。如grunt、gulp、webpack,包括現(xiàn)在正處在風(fēng)口浪尖的vite等等。webpack的相關(guān)內(nèi)容會(huì)在另外一篇文章中探討,對(duì)于其它的構(gòu)建工具以及過(guò)時(shí)的工具我覺(jué)得沒(méi)有現(xiàn)在學(xué)習(xí)的必要,具體生產(chǎn)需要使用時(shí)學(xué)習(xí)即可。

          總的來(lái)說(shuō),工具很多,但是最重要的其實(shí)是對(duì)于自動(dòng)化構(gòu)建本身的理論認(rèn)識(shí),而對(duì)于自動(dòng)化構(gòu)建的實(shí)現(xiàn)及其工具而言,我覺(jué)得,包括比較需要掌握的gulp和webpack,完全理解npm script方式是更重要的。

          最后





          如果你覺(jué)得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我三個(gè)小忙:

          1. 點(diǎn)個(gè)「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)

          2. 歡迎加我微信「huab119」拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...

          3. 關(guān)注公眾號(hào)「前端勸退師」,持續(xù)為你推送精選好文,也可以加我為好友,隨時(shí)聊騷。




          396257607bb052a8cd82f38d514219f9.webp點(diǎn)個(gè)在看支持我吧,轉(zhuǎn)發(fā)就更好了


          瀏覽 47
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧洲三级片在线 | 情趣在线视频 | 国产成人综合欧美精品久久 | 农村黄色录像一级天天干 | 亚洲免费无码视频 |