中了原始碼的毒,給你一副良藥

阿寶哥發表於2020-10-30

近期阿寶哥在團隊內搞了一個 如何讀原始碼 的專題,主要目的是讓團隊的小夥伴們瞭解讀原始碼的思路與技巧。在此期間,阿寶哥也寫了 77.9K 的 Axios 專案有哪些值得借鑑的地方從 12.9K 的前端開源專案我學到了啥如何讓你的 Express 飛起來 三篇原始碼解析的文章。其中前兩篇在 掘金社群 獲得不錯的評價,平均 705+ 個 ?,所以阿寶哥就想寫一篇文章來分享一下本人讀原始碼的思路、技巧與工具。

好的,讓我們開始出發吧!在進入正題之前,我們先來個讀原始碼前的 靈魂四連問 熱熱身。

一、靈魂四連問

1.1 為什麼要讀原始碼

1.2 如何選擇專案

1.3 如何閱讀原始碼

1.4 有實際的案例麼

既然前兩篇文章比較受大家喜歡,接下來阿寶哥就以最受歡迎的 Axios 為例,來分享一下讀原始碼的思路與技巧。

二、如何品讀 Axios?

2.1 走進 Axios

Axios 是一個基於 Promise 的 HTTP 客戶端,同時支援瀏覽器和 Node.js 環境。它是一個優秀的 HTTP 客戶端,被廣泛地應用在大量的 Web 專案中。

由上圖可知,Axios 專案的 Star 數為 78.1K,Fork 數也高達 7.3K,是一個很優秀的開源專案,所以值得大家細細品讀。

2.2 發現 Axios 的美

在確認 Axios 為 “追求目標” 之後,下一步我們就需要來發現它身上的優點(特性):

每個人對 “美” 都有不同的看法,對於阿寶哥來說,我看中了圖中已選中的三點。因此,它們也很光榮地成為讀原始碼的三個切入點。當然切入點也不是越多越好,可以先找自己最感興趣的地方作為切入點。需要注意的是,如果切入點之間有關聯關係的話,建議做個簡單的排序。

2.3 感受 Axios 的美

選擇切入點之後,我們就可以開始逐一感受 Axios 的設計之美。以 能夠攔截請求與響應 這個切入點為例,首先我們就會接觸到 攔截器 的概念。所以我們需要先了解攔截器是什麼、攔截器有什麼作用以及如何使用攔截器,這裡我們可以從專案的 官方文件 或者專案中的 README.md 文件入手。

2.3.1 攔截器的作用

Axios 提供了請求攔截器和響應攔截器來分別處理請求和響應,它們的作用如下:

  • 請求攔截器:該類攔截器的作用是在請求傳送前統一執行某些操作,比如在請求頭中新增 token 欄位。
  • 響應攔截器:該類攔截器的作用是在接收到伺服器響應後統一執行某些操作,比如發現響應狀態碼為 401 時,自動跳轉到登入頁。
2.3.2 攔截器的使用
// 新增請求攔截器 —— 處理請求配置物件
axios.interceptors.request.use(function (config) {
  config.headers.token = 'added by interceptor';
  return config;
});

// 新增響應攔截器 —— 處理響應物件
axios.interceptors.response.use(function (data) {
  data.data = data.data + ' - modified by interceptor';
  return data;
});

axios({
  url: '/hello',
  method: 'get',
}).then(res =>{
  console.log('axios res.data: ', res.data)
});

在瞭解完攔截器的作用和用法之後,我們就會把焦點聚焦到 axios 物件,因為註冊攔截器和傳送請求都與它有緊密的聯絡。不過在看具體原始碼之前,阿寶哥建議先對功能點做一下梳理。以下是阿寶哥的分析思路:

Axios 的作用是用於傳送 HTTP 請求,請求攔截器和響應攔截器分別對應於 HTTP 請求的不同階段,它們的本質是一個實現特定功能的函式。這時我們就可以按照功能把傳送 HTTP 請求拆解成不同型別的子任務,比如有 用於處理請求配置物件的子任務用於傳送 HTTP 請求的子任務用於處理響應物件的子任務。當我們按照指定的順序來執行這些子任務時,就可以完成一次完整的 HTTP 請求。

既然已經提到了任務,我們就會聯想到任務管理系統的基本功能:任務註冊、任務編排(優先順序排序)和任務排程等。因此我們就可以考慮從 任務註冊、任務編排和任務排程 三個方面來分析 Axios 攔截器的實現。

2.3.3 任務註冊
// 新增請求攔截器 —— 處理請求配置物件
axios.interceptors.request.use(function (config) {
  config.headers.token = 'added by interceptor';
  return config;
});

// 新增響應攔截器 —— 處理響應物件
axios.interceptors.response.use(function (data) {
  data.data = data.data + ' - modified by interceptor';
  return data;
});

lib/axios.js 路徑下,我們可以找到 axios 物件的定義。為了能直觀地瞭解物件之間的關係,阿寶哥建議大家在讀原始碼的過程中,多動手畫畫圖。比如阿寶哥使用下圖來總結一下 Axios 物件與 InterceptorManager 物件的內部結構與關係:

2.3.4 任務編排

現在我們已經知道如何註冊攔截器任務,但僅僅註冊任務是不夠,我們還需要對已註冊的任務進行編排,這樣才能確保任務的執行順序。

同樣對於任務編排,也可以使用圖的形式來展現任務編排後的結果。 這裡有一個小技巧,就是可以採用對比的形式來展示任務編排後的結果,這樣子會更加清楚任務編排的處理邏輯。

2.3.5 任務排程

任務編排完成後,要發起 HTTP 請求,我們還需要按編排後的順序執行任務排程。

需要注意的是:在閱讀原始碼過程中,不要太在意細節。比如在研究 Axios 攔截器原理時,不需要再深入瞭解 dispatchRequest 背後的具體實現,只需知道該方法用於實現傳送 HTTP 請求即可,這樣才不會把整個線路拉得太長。

在分析完特定的功能點之後,也許你已經讀懂的具體的原始碼。但阿寶哥覺得這並不是最重要的,更重要的是思考它的設計思想,這樣設計有什麼好處,對於我們有沒有什麼值得借鑑和學習的地方。比如參考 Axios 攔截器的設計模型,我們就可以抽出以下通用的任務處理模型:

上面阿寶哥以 Axios 的攔截器為例,分享了讀 Axios 原始碼的思路與技巧。接下來阿寶哥來分享一些讀原始碼的建議和輔助工具。

三、讀原始碼的建議

四、讀原始碼輔助工具

如果你對下列輔助工具感興趣的話,可以通過以下圖片來源的連結,來直接開啟每個工具的線上地址。

(圖片來源:https://www.processon.com/vie...

五、總結

其實除了上面的內容之外,讀優秀開源專案還有挺多值得關注的地方。阿寶哥在學習 BetterScroll 專案原始碼時,總結了一張思維導圖

(圖片來源:https://www.processon.com/vie...

下面阿寶哥用一張圖來總結一下 axiosbetter-scroll 這兩個開源專案的學習路線:

1、Axios 專案的切入點是從 Github 中的功能特性中篩選出來的;

2、BetterScroll 的切入點是從掘金上 BetterScroll 2.0 釋出:精益求精,與你同行 這篇文章中介紹的功能亮點中找到的。

除此之外,阿寶哥也來簡單總結一下本文介紹的讀原始碼的思路與技巧:

  • 站在巨人的肩膀,提前閱讀一些專案相關的優質文章;
  • 彙總學習或工作中遇到的問題,帶著問題進行原始碼學習;
  • 明確閱讀原始碼的主線或切入點;
  • 儘可能從簡單的示例出發來分析每個功能點;
  • 先梳理清楚主要流程,不要太在意細節,避免把整個線路拉得太長;
  • 在閱讀原始碼過程中,要多多畫圖,這樣理解起來會更加直觀。

本文阿寶哥分享了個人讀原始碼的思路、技巧與工具,希望閱讀完本文能對你有所啟發或幫助。如果你有讀原始碼更好的思路與技巧,歡迎隨時跟阿寶哥交流哈。有寫得不好的地方,也請各位見諒哈。

六、參考資源

相關文章