稍微談一下現在的前端構建學到的一些東西.

打包器

這個東西本身的功能是比較單純的, 就是將工程的整個依賴圖畫出來, 然後打包到單個文件裡面去.

比較主流的就是 Webpack, 其他還有 sonwnpack, rollup(這個稍微有點特別), Parcel, 最後還有最近新出現的 vite.

優化: 例如壓縮 Js, CSS, HTML, 搖樹, 調整圖像大小, 以及分包等機制.所有的優化基本都是為了能在打開網站時更快的載入頁面.

擴展: 前端的技術魚龍混雜, 光開發語言的衍生亞種就非常多, 打包器就需要能支持這些語言的編譯器. 部分代碼優化也在這裡完成. 擴展性基本決定了一個打包器的命運和社區發展.

這兩點基本所有的打包器都支持. 接下來說一些有差異化的地方.

Webpack

究極老牌的打包器了, Webpack 在 5 版本之前是出了名的重和慢還有繁, 擴展性和社區都極佳, 基本所有的前端技術你都能在 wp 上找到對應的 loader 或者 plugin. 也是目前最流行的打包器了. Webpack5 出現了之後速度提升了很多. webpack-chainwebpack-merge使用這兩個第三方包配合可以做到大部分的配置複用. 但是配置依然很重和繁瑣這一點, 一直都沒有很好的方案.

 1var webpack = require("webpack");
 2module.exports = {
 3  entry: "./entry.js", //入口文件
 4  output: {
 5    //node.js中__dirname變量獲取當前模塊文件所在目錄的完整絕對路徑
 6    path: "dist", //輸出位置
 7    filename: "bundle.js", //輸入文件
 8  },
 9  module: {
10    loaders: [
11      {
12        test: /\.css$/, //支持正則
13        loader: "style-loader!css-loader",
14      },
15
16      {
17        test: /\.vue$/,
18        loader: "vue-loader",
19      },
20    ],
21  },
22  //其他解決方案配置
23  resolve: {
24    extensions: ["", ".js", ".json", ".css", "vue"], //添加在此的後綴所對應的文件可以省略後綴
25  },
26  //插件
27  plugins: [new webpack.BannerPlugin("This file is created by ly")],
28};

Parcel

繼 Webpack 之後新出現的打包器, 解決了 Webpack 的一個非常大的痛點, Parcel 可以做到零配置啟動. 自身就集成了 parcel 所有的東西, 你只需呀一個小小的 index.html 作為入口, 其他的全部解析都交給 Parcel, Parcel 內部還集成了一個 Js 編譯器, 並且由於該編譯器使用的是 Rust 語言編寫它的執行速度非常快. 理論上應該比 esbuild 還要快. Parcel 剛出現的時候風評並不好, 因為不支持 source-map, 並且搖樹還有 bug..

Sonwnpack

時代在發展, 漸漸的瀏覽器也開始支持 ESM 了, Sonwnpack 就是坐上了這個風口的打包器. 有了 ESM, 你所有的 JS 模塊都可以懶加載的方式載入到頁面中. 並且你的第一次開發服務器啟動也不需要編譯所有依賴的 JS 代碼. 順勢的, Sonwnpack 中就出現了一個HMR(熱模塊替換)的概念

熱模塊替換 (HMR) 是一種在不觸發整個頁面刷新的情況下將文件更新推送到瀏覽器的能力。想象一下更改一些 CSS,點擊保存,然後立即看到您的更改反映在頁面上而無需刷新。那是 HMR。然而,Snowpack 利用 ESM 進行非捆綁開發的能力引入了近乎即時的單個文件構建,只需 10-25 毫秒即可在瀏覽器中加載和更新。

 1/** @type {import("snowpack").SnowpackUserConfig } */
 2module.exports = {
 3  mount: {
 4    test: { url: "/", static: true },
 5    public: { url: "/", static: true },
 6    src: { url: "/_dist_" },
 7  },
 8  plugins: [
 9    "@snowpack/plugin-webpack",
10    "@snowpack/plugin-vue",
11    "@snowpack/plugin-sass",
12    "@snowpack/plugin-dotenv",
13    // ["@snowpack/plugin-build-script", { "cmd": "postcss", "input": [".css"], "output": [".css"] }]//,
14  ],
15};

Vite

vite 的起步還比較晚, 它的部分開發靈感來自 Sonwnpack, 同時 vite 也支持 HMR. 比較特別的是 Vite 自帶的編譯器是 ESBuild, 在官方 banner 上速度極其誇張的一個編譯器. 配置文件時 vite 的優點,大部分的插件即插即用, 以及配置文件的寫法修復了很多以前打包器的缺點, 可以非常靈活. 目前 Vite 的擴展現在還比較少, 以後可以期待一下.

ESBuild 的官方宣稱: 基準測試通過將 three.js 庫複製 10 次並從頭開始構建單個包來近似大型 JavaScript 代碼庫,而無需任何緩存. Webpack5 = 41.53s, rollup + terser = 34.95s, parcel2 = 32.48s, esbuild = 0.33s. 這個速度是傳統打包器的 100 倍.

然而在實測中, 我在當前的 Webpack 中引入了 esbuild, 來代替以前的 babel 和 tsc, 時間也只省下了 5-10s, 並沒有那麼誇張, 可能是 Webpack 的 loader 調用導致的瓶頸.

1// vite.config.js
2import { defineConfig } from "vite";
3import react from "@vitejs/plugin-react";
4
5export default defineConfig({
6  plugins: [react()],
7});

Rollup

Rollup 是個非傳統意義上的打包器, 它主要面向的是類庫的前端開發者. Rollup 的配置文件的繁瑣程度比較折中. Rollup 似乎沒有對分包的支持, 理念就是將小塊代碼打包成大塊的複雜代碼,放在一個文件中. Rollup 自帶的功能也非常少, 甚至不支持 node 模塊解析. 大部分都需要依賴插件完成.

 1// rollup.config.js
 2import json from "rollup-plugin-json";
 3
 4export default {
 5  input: "src/main.js",
 6  output: {
 7    file: "bundle.js",
 8    format: "cjs",
 9  },
10  plugins: [json()],
11};

關於編譯器

目前大部分前端的編譯器非常混雜, 基本是各做各的, JS 的編譯器的 Babel 是不二之選, 所有 ES 標準以及 Typescript 甚至 Flow 和 coffeescript,Babel 都能編譯. 也支持一些async do {}這種奇怪的語法糖. ESBuild 和興起的 SWC 是針對速度進行優化. 這裡也沒什麼好說的.