隨著網頁技術不斷翻新,前端需要處理的事也就越來越繁雜。在這樣龐雜豐富且迅速更迭的技術環境之下,將開發流程自動化、工程化就成了必需,開發者也自然會需要一些工具來協助優化這些流程。

今天就來聊聊前端的模組打包工具(Module Bundler)。

前端工程化#

工程化的起點,自然是從 Node.js 問世,套件管理工具 npm 誕生開始。透過 npm,開發者便能快速且輕易的從 NPM Registry 下載到其他開發者提交的套件,並加在自己的專案中。

除此之外,npm 也提供了 script 的功能,將 shell script 封裝成別名快速使用:

{
  "scripts": {
    "dev": "node app.js"
  }
}

例如上例,當開發者輸入 npm run dev,就等同於執行了 node app.js 這行指令。透過這樣簡短的 script 設定,確實可以解決一些基本的需求。

Grunt#

但其實大部分需求在開發過程中,重複性是非常高的。例如將 SASS、SCSS 編譯成 CSS,透過 npm script,就需要修改完、輸入指令進行編譯、Reload,開發者的開發體驗便會大打折扣。要解決這樣的情況,Grunt 是一個不錯的解決方案。

Grunt 提供了更大的程式化空間,讓開發者能更自由地控制自動化流程。藉由大量的插件(plugin),以及高度客製化的設定,開發者可以輕易地完成一些常見的任務。

Gulp#

但 Grunt 的設定大多是每個插件個別設定,一旦專案中的插件數量增加,設定跳來跳去,同一支原始檔案的去向很容易就變得難以追蹤。Gulp 透過管道(Pipes)及串流(Streams)的概念,便能有效避免前述的情況發生。

例如想要將 LESS 編譯成 CSS,再將檔案進行最小化(minify)處理,Gulp 中的設定可能會這樣寫:

function css() {
  return src("assist/style/*.less")
    .pipe(less())
    .pipe(minifyCSS())
    .pipe(dest("dist/css"));
}

藉由 pipe 函式,將任務結果依序往後傳遞,同檔案類型會經過什麼處理,也就變得一目了然。除此之外,Gulp 相較於 Grunt 還多了檔案監看、檔案讀寫等功能,將許多繁雜的基礎功能都得以自動化。

Webpack#

但隨著前端的演進,開發流程又更加複雜,針對檔案類型做處理已經不能滿足開發者的需求。加上資源量越來越多,在 HTTP/1.1 有單一網域請求數上限的情況下,勢必需要將前端的程式分開包裝,才能夠將資源傳遞給使用者。

熱門套件 Webpack 則給出了一個全新的思考模式:藉由定義專案的 entryoutput,Webpack 便會從 entry 開始解析專案,最後將相依的檔案打包成指定的 output。除此之外,也可以在過程中間加上 loaderplugins,對需要的檔案做進一步的處理。

同樣拿 CSS 來看看,這次將 SCSS 依序處理成 CSS:

rules: [
  {
    test: /\.(scss)$/,
    include: [path.resolve(__dirname, "app")],
    loader: ExtractTextPlugin.extract({
      fallback: "style-loader",
      use: [
        {
          loader: "css-loader",
          options: {
            modules: false,
            minimize: true,
            sourceMap: true,
            importLoaders: 3,
          },
        },
        {
          loader: "postcss-loader",
          options: {
            sourceMap: true,
            config: {
              path: path.resolve(__dirname, "postcss.config.js"),
            },
          },
        },
        {
          loader: "resolve-url-loader",
          options: {
            sourceMap: true,
          },
        },
        {
          loader: "sass-loader",
          options: {
            sourceMap: true,
          },
        },
      ],
    }),
  },
  //...
];

透過上述設定,將會依序透過 sass-loaderresolve-url-loaderpostcss-loadercss-loaderstyle-loader 對副檔名為 .scss 的檔案做處理,其餘檔案類型也能夠設定、搭配相對應的 loader,將前端的內容封裝、打包。

更多打包工具#

看到一堆設定,這也是 Webpack 最讓人詬病的問題:Webpack 提供開發者更高的自由度,同時卻也帶來更多的複雜度。但在一般的中小型專案,可能根本不需要那些繁雜的設定,這時候用 Webpack 就有點殺雞用牛刀。

開源社群中有一些新的打包工具,都能讓開發者可以減少撰寫龐大設定的痛苦,並享用自動化流程的便利:

  • Backpack:專注在 Node.js 後端專案
  • Rollup:號稱是新一代打包工具
  • Parcel:完全無設定便能直接使用

打包工具的用途#

說了那麼多打包工具,用途到底是什麼?不外乎以下幾點。

轉譯#

前端三大框架是不少開發者接觸打包工具的起點。由於前端框架提供了許多額外的語法,但瀏覽器只能解讀 JavaScript,中間必須要經過一個轉換的步驟,將開發者撰寫的程式,轉譯成瀏覽器引擎(Browser Engine)能解讀的內容。

例如 LESSSASS 會透過 postcss 做相對應的處理;React 用的 JSX 語法、Vue 的 .vue 檔、ES6+ 的語法等等,則會透過 Babel,做程式碼的轉譯。

優化程式#

針對程式進行效能優化是非常重要的一環,也因為其繁瑣的流程設定,打包工具在優化程式這塊能充分的給予協助。

打包工具可以幫忙完成的事情包含:

  • minify
  • uglify
  • 切分 chunk
  • 樹搖(Tree Shaking)

加速開發#

例如為了減少前端請求數目的 CSS Sprite,比起用 Photoshop 慢慢對齊、計算 CSS 屬性,還不如透過打包工具的套件例如 Webpack Spritesmith,來完成圖檔合併的工作。其他像是 Webpack 的 Dev Server 內建的 Hot Reload 功能,網頁開發者就不用一直手動重新整理頁面。

加上前面的轉譯及優化程式,將原本需要手動處理的事情全部交給打包工具處理,再透過設定妥當的自動化流程,讓開發者寶貴的時間不用再去做週期性的繁瑣雜事。

結語#

在網站內容不斷增加,開發流程逐漸趨近工程化的現在,嘗試去使用、理解這些能協助建置自動化開發流程的套件,可以讓開發者有效地省下大量的時間。

原文出處#

  • 原書/iThome:https://ithelp.ithome.com.tw/articles/10225915