精讀《webpack4.0 升級指南》

本週精讀的是 webpack4.0 一些變化,以及 typescript 該怎麼作才能最大化利用 webpack4.0 的全部特性。html

1 引言

前段時間嘗試了 parcel 做爲構建工具,就像農村人享受了都市的生活,就再也回不去了同樣,發現無配置真是前端構建工具的大趨勢,用起來很是方便快捷,不再想碰 webpack 的配置了。前端

但是實踐一段實踐後,發現 parcel 仍是不夠成熟,主要體如今暫時不支持一些 rollup 優秀特性:Tree shaking、Scope Hoist,大型項目打包速度反而比 webpack3.0 慢。因爲筆者徹底零配置,當發現構建速度急速降低時,天然把矛頭指向了 parcel :p.node

就在前幾周,webpack4.0 發佈了,也擁抱了零配置,我想,是時候再回到 webpack 了。但是,文檔好少,怎麼遷移呢?react

就在這幾天,webpack 文檔發佈了 4.0 版本,雖然遺留了大量舊文檔,不過也足夠參考了。webpack

2 精讀

筆者嘗試了 webpack node api,嘗試了好久,發現被坑了。文檔裏隻字未提 mode 模式,4.0 環境下 compiler 老是提示沒有 mode 的 warning。git

讀了一些文檔,發現 webpack4.0 大力度宣傳的是 cli 方式啓動,裏面提到了最重要的 webpack --mode 模式,可見 webpack4.0 更推崇的是讓開發者使用高度封裝的 cli,而不是使用 node 方式開發(那 node 文檔也應該更新呀)。筆者又看了一圈,發現 webpack-dev-server 的 webpack 版本升到了 4.0,ts-loader 也升級到了 4.0,可能生態已經所有準備好了。es6

使用 webpack cli、webpack-dev-server cli

安裝 webpack^4.1.1 webpack-cli^2.0.10 webpack-dev-server^3.1.0,以及建立一個公共配置文件 webpack.config.ts:github

export default {
  entry,

  output,

  module: {
    rules
  },

  resolve,

  resolveLoader,

  devServer: {
    https: true,
    open: true,
    overlay: {
      warnings: true,
      errors: true
    },
    port
  }
}
複製代碼

記得用 tsc 轉換爲 webpack.config.js 做爲 cli 入口。web

開發模式下使用 webpack-dev-server:typescript

webpack-dev-server --mode development --progress --hot --hotOnly --config ./webpack.config.js
複製代碼

生產環境 build 使用 webpack:

webpack --mode production --progress --config ./webpack.config.js
複製代碼

開發/生產模式,都以 webpack.config.ts 做爲配置,其中 devServer 項僅在開發模式下,對 webpack-dev-server 生效。

一旦開啓了 --mode production,會自動開啓代碼壓縮、scope hoist 等插件,以及自動傳遞環境變量給 lib 包,因此已經不須要 plugins 這個配置項了。同理,開啓了 --mode development 會自動開啓 sourceMap 等開發插件,咱們只要關心更簡單的配置,這就是 4.0 零配置的重要改變。

mode=production, mode=development 具體內置了哪些配置,能夠參考這篇文章:webpack 4 終於知道「約定優於配置」了。偏偏有意思的是,webpack4 這麼作,就是不想咱們浪費時間瞭解這些機制,社區應該會慢慢習慣零配置的開發方式。

固然,雖說零配置,但配置文件基本三板斧仍是很是有必要配置:entry output module

咱們可能還要給配置文件傳一些參數,好比定製多種開發模式的入口,經過 --env 傳遞:

webpack-dev-server --mode development --env.entry ./src/main.tsx
複製代碼

webpack.config.ts 接收:

const entry = yargs.argv.env.entry
複製代碼

使用 typescript + webpack

簡單來講,只須要 ts-loader 就夠了。在 webpack.config.ts 中增長新的 rules:

{
  module: {
    rules: [{
      test: /\.(tsx|ts)?$/,
      use: ["ts-loader"]
    }]
  }
}
複製代碼

注意 tsconfig.json 中模塊解析策略使用: "module": "esnext"

緣由是 webpack 須要 es6 import 語句,才能進行 tree shaking 或者動態 import 優化,咱們再也不讓 ts-loader 包辦模塊設置,換句話說,咱們採用白名單方式看待 typescript 以及 babel,只讓他作咱們須要的工做,剩下的丟給 webpack 處理,能夠得到最大程度性能優化。

若是僅使用 webpack + typescript,建議將 ts 編譯輸出模式調整爲 es3,由於 webpack 自帶的壓縮工具對 es6 語法還存在報錯,並且也不會作兼容處理。

使用 typescript + babel + webpcak

注意處理順序,ts -> babel -> webpack。

由於多出了 babel,咱們將 ts 編譯兼容模式關閉:"target": "esnext",模塊也不要解析:"module": "esnext"ts-loader 僅僅將 typescript 代碼轉換成 js,其餘一切優化都不要作,將 esnext 原生代碼直接傳給 babel 處理。

babel 這一層的職責是對代碼進行兼容處理,不要壓縮,也不要把 import 轉成 require。筆者發現 babel 直接解析 import 代碼會沒法處理,所以須要 stage-2 preset:

{
  presets: [
    ["env", {
      modules: false,
    }],
    ["stage-2"]
  ],
  plugins: [
    ["transform-runtime"]
  ],
  comments: true
}
複製代碼

從上面配置能夠看到,babel 這層對 esnext 的代碼進行了瀏覽器兼容處理(env 插件),直接透傳 import(stage-2 插件讓 babel 識別 esModule),以及支持 async await(transform-runtime) 插件。

原本想用 env 替代 transform-runtime 的功能,筆者暫時沒有查詢到可行方式,歡迎讀者補充。

另外要容許 babel 保留註釋(comments: true),由於 webpack import 支持自定義 chunkName 是經過註釋的方式:

import(/* webpackChunkName: "src" */ "./src")
複製代碼

配合 react-loadable 使用更佳:

Loadable({
  loader: () => import(/* webpackChunkName: "src" */ "./src"),
  loading: (): any => null
})
複製代碼

由於 react-loadable 讓頁面按 chunk 方式打包,而 webpack 又會自動 picke shared chunks,配合給每一個 page chunks 經過 webpackChunkName 定義名稱,webpack 能夠給每一個共享 chunks 更加可讀的名字,好比:vendor~src,about,login,你就知道這個是 src about login 三個頁面間公共模塊。

可能已經有人看出瑕疵了,給每一個文件增長 webpackChunkName 註釋既麻煩又不優雅,並且只要有一個開發者沒有加這個註釋,上面說的可讀 chunks 可能就缺乏了某個模塊名。

這就要筆者以前一篇精讀來看了:精讀《Rekit Studio》項目能夠經過約定的方式定義頁面,入口文件經過 cli 自動生成,不就既減小業務代量,又統一加上了 webpackChunkName 嘛?

這裏小小安利下集成了這個思路的項目腳手架 pri,使用了 ts + babel + webpack4.0,上述的小優化也是內置的功能之一。

webpack4 帶來的是適配成本的大幅優化

社區彷佛有部分聲音在抱怨,webpack 又發新版本,咱們又要適配一輪。其實 webpack 這麼作偏偏沒有帶來適配成本,出問題的在於咱們對 webpack 的使用方式與理念。

若是咱們開始就將 webpack 看成一體化打包方案,開發調試使用 webpack-dev-server cli,開發環境編譯使用 webpack cli,那麼 webpack4 其實只是補充了開發環境這個最重要的配置變量而已。類比 parcel 的兩個命令:

parcel index.html
parcel build index.html
複製代碼

對應:

webpack-dev-server --mode development
webpack --mode production
複製代碼

因此 webpack4 幾乎是有史以來最方便使用與遷移的版本,前提是使用思惟得正確,捨得將編譯環節全權交給兩個官方的 Cli。

3 總結

只要合理的使用 typescript、babel,讓各自只發揮最小功能,將原生的模塊化代碼拋給 webpack,再配合 --mode production 配置,webpack 會自動開啓一切可能的插件優化你的項目,而咱們再不須要閱讀形形色色的 webpack 插件了,更使人激動的是,隨着 webpack 版本升級,優化會不斷升級,而咱們只要留着 --mode 參數,不須要改一行配置。

總結起來,就是不用關心優化相關的配置,咱們只須要配置業務相關的 entry output module,這就是 webpack4.0.

我之前爲了實現第一次編譯完後當即打開瀏覽器的功能,寫了一共 200 行的 customCompiler 以及 format-webpack-message,並且利用 koa 開了一個 server,利用 await 和 flags 等待第一次編譯完的時機,並利用 opn 庫打開網頁。

其實用 cli 只須要 webpack-dev-server --open

隨着新的一波零配置浪潮,真的不該該在編譯配置上花那麼多時間了。

4 番外 - prefetch

讀者自習閱讀就會發現,這不是一篇單純 webpack4 升級指南,仔細閱讀能夠發現文中蘊藏的一些工程優化思路。文章末尾再給一波福利,分析一下 prefetch 優化是什麼,以及怎麼作。

現代瀏覽器支持瞭如下兩種語法:

<link rel="preload" />
<link rel="prefetch" />
複製代碼

兼容性本身查 Caniuse,筆者重點在功能上。preload 收集當前用到的資源,prefetch 收集將來用到的資源。

頁面本質上也是將來一種資源,若是認爲用戶會點擊另外一個頁面(若是對產品沒自信,或者 pv 太低能夠忽略這個功能),就能夠用 prefetch 讓瀏覽器在空閒時間下載下一個頁面的 chunk 文件。

前端包體積優化效率通常和用戶體驗是違背的,既然下一個頁面在另外一個 chunk 中,用戶點擊後必然會產生 loading。但是若是結合了 prefetch,魚和熊掌就兼得了(正經常使用戶不可能頁面還沒加載完就馬上點按鈕跳頁,因此惟一的缺點幾乎不會對正經常使用戶產生影響)。

api 有了,那麼最大的問題就是,當前頁面怎麼知道要加載哪些 chunks?通常兩種作法:

全量模式 使用好比 preload-webpack-plugin 插件,將全部生成的 chunk 都做爲 prefetch 資源,在全部頁面中。幾乎全部規模的項目都不會產生過多的 chunks,因此這個方案理論上不夠優雅,但能解決實際問題。

按需模式,是理論和實踐雙重優雅的方案,是否要這麼作取決於您是否有代碼潔癖。方法是提供一個定製的 Link 標籤,根據 URL 地址按需生成 prefetch 標籤。這種方案最大缺陷是,若是用戶不按照約定使用內置的 Linkprefetch 規則將會無效。

5 更多討論

討論地址是:精讀《webpack4.0 升級指南》 · Issue #66 · dt-fe/weekly

若是你想參與討論,請點擊這裏,每週都有新的主題,每週五發布。

相關文章
相關標籤/搜索