大家都能看得懂的原始碼(一)ahooks 整體架構篇

Gopal發表於2022-06-27

本文是深入淺出 ahooks 原始碼系列文章的第一篇,這個系列的目標主要有以下幾點:

  • 加深對 React hooks 的理解。
  • 學習如何抽象自定義 hooks。構建屬於自己的 React hooks 工具庫。
  • 培養閱讀學習原始碼的習慣,工具庫是一個對原始碼閱讀不錯的選擇。

注:本系列對 ahooks 的原始碼解析是基於 v3.3.13。自己 folk 了一份原始碼,主要是對原始碼做了一些解讀,可見 詳情

第一篇主要介紹 ahooks 的背景以及整體架構。

React hooks utils 庫

自從 React 16.8 版本推出 React hooks,越來越多的專案使用 Function Component。React hooks utils 庫隨即誕生,它主要解決的兩個問題如下:

  • 公共邏輯的抽象。
  • 解決 React hooks 存在的弊端,比如閉包等。

那現在社群有哪些比較優秀的 React Hooks utils 庫呢?

react-use 是社群比較活躍的 React hooks utils 庫,它的 star 數達到了 29.6k。它的功能非常強大,擁有的 hooks 已經 100+。假如你需要功能比較齊全,可以考慮選擇 react-use。

如果不需要非常齊全的功能,只需要一些常見的功能,react-use 可能會稍微冗餘了,可以考慮我們今天的主角——ahooks,目前它的 star 數為 9.2k,也算是社群比較活躍。

ahooks

簡介

官方介紹如下:

ahooks,發音 [eɪ hʊks],是一套高質量可靠的 React Hooks 庫。在當前 React 專案研發過程中,一套好用的 React Hooks 庫是必不可少的,希望 ahooks 能成為您的選擇。

特點

它具有如下特點:

  • 易學易用。
  • 支援 SSR。

    • 將訪問 DOM/BOM 的方法放在 useEffect 中(服務端不會執行),避免服務端執行時報錯。
    • 原始碼中可以看到很多 isBrowser 的判斷,主要是區分開瀏覽器環境和伺服器環境。
  • 對輸入輸出函式做了特殊處理,且避免閉包問題。

    • 輸入的函式,永遠都是使用最新的一份。這個是通過 useRef 進行實現。
    • 輸出函式,地址都是不會變化的,這個是通過 useMemoizedFn(ahooks 封裝的)實現的,其實現也是通過 useRef 實現。後面我們會提到。
  • 包含大量提煉自業務的高階 Hooks。
  • 包含豐富的基礎 Hooks。
  • 使用 TypeScript 構建,提供完整的型別定義檔案。可以學習一些 TypeScript 的技巧。

hooks 種類

ahooks 基於 UI、SideEffect、LifeCycle、State、DOM 等分類提供了常用的 Hooks。如下所示:

圖來自網路,侵刪

ahooks 整體架構

專案啟動

我們先從 ahooks 中 folk 一份,clone 下來。

yarn run init
yarn start

如果你能成功跑起服務,就會看到和官方文件一模一樣的頁面。

整體結構

從倉庫的根目錄的 package.json 中可以得到以下資訊。

  • 文件是使用 dumi。是一款為元件開發場景而生的文件工具。
  • 該專案是一個 monoRepo。它的專案管理是通過 lerna 進行管理的。
  • 單元測試是通過 jest 實現。

它的目錄結構中,可以看到 docs 中存放倉庫公共文件。packages 中存放兩個包,hooks 和 use-url-state。整體的結構跟 dumi 中給出的 lerna 專案的結構相似。其中每個包下面的每個元件都可以書寫對應的文件,不一樣的是,hooks 中每個元件多了 __tests__ 資料夾,這個是用來寫單元測試的。

跟 hooks 相似的組織形式

可以用以下一張圖,大致總結一下 ahooks 的工程架構:

hooks

剛剛也提到,ahooks 是採用了 monoRepo 的方式,我們的原始碼都是在 packages 中,我們來看下 hooks。先看 packages/hooks/package.json。另外要使用 useUrlState 這個 hook,需要獨立安裝 @ahooksjs/use-url-state,其原始碼在 packages/use-url-state 中。我理解官方的用意應該是這個庫依賴於 react-router,可能有一些專案不需要用到,把它提出來有助於減少包的大小。

npm i @ahooksjs/use-url-state -S

回到 packages/hooks。重點關注一下 dependencies 和 peerDependencies。可以看到其實它內部還是使用了一些其他的工具庫的,比如 lodash(估計是避免重複造輪子,但感覺這樣會導致包會變大)。後面我們也會對這些工具庫做一個探索。

"dependencies": {
  "@types/js-cookie": "^2.x.x",
  "ahooks-v3-count": "^1.0.0",
  "dayjs": "^1.9.1",
  "intersection-observer": "^0.12.0",
  "js-cookie": "^2.x.x",
  "lodash": "^4.17.21",
  "resize-observer-polyfill": "^1.5.1",
  "screenfull": "^5.0.0"
},
"peerDependencies": {
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
},

另外解釋下 peerDependencies。

peerDependencies 的目的是提示宿主環境去安裝滿足外掛 peerDependencies 所指定依賴的包,然後在外掛 import 或者 require 所依賴的包的時候,永遠都是引用宿主環境統一安裝的 npm 包,最終解決外掛與所依賴包不一致的問題。這裡的宿主環境一般指的就是我們自己的專案本身了。

這對於封裝 npm 包非常重要。當你寫的包 a 裡面依賴另一個包 b,而這個包 b 是引用這個包 a 的業務的常用的包的時候,建議寫在 peerDependencies 裡,避免重複下載/多個版本共存。

總結

作為系列的第一篇,介紹了 React hooks utils 庫的背景以及 ahooks 的特點簡介和整體架構,接下來會探索各個常見的 hooks 方法實現,敬請期待。

參考

本文由mdnice多平臺釋出

相關文章