JavaScript 專案最佳實踐指南

發表於2017-07-20

在開發一個新專案時,就像在一個綠色場地上來回翻滾,維護它對其他人來說可能是一個潛在的黑暗而扭曲的惡夢。以下是我們發現、編寫和收集的指南列表(我們認為)在 hive 上的大多數 JavaScript 專案都能很好地工作。如果你想分享最佳做法,或者認為應該刪除這些指南之一,請隨時與我們分享。

  • Git
  • 文件
  • 環境
  • 依賴
  • 測試
  • 結構和命名
  • 編碼風格
  • 日誌
  • API 設計
  • 協議

1. Git

1.1 一些 Git 相關的規則

有這樣一些規則需要牢記。

  • 開一個獨立的 feature 分支寫程式碼為什麼:

    因為這樣就可以在一個專用的分支上把所有的開發工作都做完,而不會動主分支。你就能提交多次推送請求而不致把事情搞亂,不會因為寫了潛在的不穩定、未完成的程式碼而搞壞了 master 分支。瞭解更多…

  • 分支要同 develop 分支獨立出來為什麼:

    這樣你就可以確保 master 分支裡面的程式碼總是可以無誤的通過構建, 並且總是可以直接用來做釋出 (這一點對於有些專案而言可能有點矯枉過正了)。

  • 永遠不要向 develop 或者 master 分支推送,而是要發起 Pull Request。為什麼:

    這樣做會通知團隊成員說他們已經完成了一項功能的開發。同時也能很容易就進入同行程式碼評審的流程,並且能有一個把擬提交功能拿到論壇進行充分討論的過程。

  • 在推送你開發的功能併發起一次 Pull Request 之前,要更新你的 develop 分支然後做一次程式碼庫重設。為什麼:

    重設會在所請求的分支( master 或者 develop )中進行程式碼合併,並將你在本地所做的提交應用到頂層的歷史記錄之上,而無需再去建立一次合併提交(假設寫的程式碼沒衝突)。這樣做能維持一個整潔乾淨的歷史記錄。瞭解更多 …

  • 在進行重設併發起 Pull Request 之前先解決潛在的程式碼衝突。
  • 在合併之後要刪掉本地和遠端的 feature 分支。為什麼:

    已經沒用的分支會把你的分支列表搞得亂七八雜。這樣做可以確保你只會講分支合併到 master 或者 develop 中一次。Feature 分支應該只存在於開發工作還在進行的那段時間。

  • 在發起一次 Pull Request 之前,要確保 feature 分支可以成功構建並且可以通過所有的測試(包括程式碼風格的檢查)。為什麼:

    現在是你要把程式碼新增到一個穩定的分支上。如果你的 feature 分支測試失敗了,就有極高的風險導致目標分支也構建失敗。此外你還需要在發起一次 Pull Request 之前進行一下程式碼風格檢查。這樣做有助於提高程式碼可讀性,並減少在實際的修改中所進行的格式化修復工作變亂。

  • 使用這個 .gitignore 檔案為什麼:

    這個檔案裡面已經有了一份列表,列出來那些不應該同你的程式碼一起被髮送到遠端程式碼庫的系統檔案。此外,它還排除了哪些最常被使用的編輯器的設定目錄以及檔案,還有最常用的依賴目錄。

  • 保護你的 develop 和 master 分支。為什麼:

    這樣做可以讓你的準生產分支不受不可預期且不可回退的修改的破壞。從 Github 和 Bitbucket 瞭解更多。

1.2 Git 工作流程

大多是由於上述的原因,我們使用帶有主動重設功能特性分支開發工作流(Feature-branch-workflow還有 Gitflow (命名並擁有一個 develop 分支)的一些元素。主要的步驟如下:

  • 檢出一個新的 feature/bug-fix 分支
  • 修改程式碼

    為什麼:

    git commit -a 會啟動一個編輯器,讓你能將程式碼的修改的主題從程式碼中分離出來。在章節 1.3 中可以瞭解到更多

  • 與遠端進行同步,獲取本地錯過的那些修改。

    為什麼:

    這樣做讓你可以在(稍後)進行重設的時候有機會在自己的機器上處理程式碼衝突,而不是建立出一個包含了衝突的 Pull Request。

  • 通過主動進行重設來從 develop 分支拿到最新的修改更新 feature 分支。

    為什麼:

    你可以使用 –autosquash 來將所有的提交操作捆成一次提交。沒有人會想要在 develop 分支中針對一個功能的開發做多次提交。瞭解更多…

  • 如果你的程式碼裡並沒有衝突,那就跳過這個步驟。如果有衝突,那就先處理掉它們然後再繼續進行重設
  • 推送你的分支。重設會改變歷史記錄,所以你得使用 -f 來將修改推送到遠端分支。如果另外還有一個人正在你的分支上寫程式碼,就使用負面影響較小的 –force-with-lease 吧。

    為什麼:

    當你在進行一次重設的時候,就是在對你的 feature 分支上的歷史記錄做變更。這樣做的結果就是 Git 會拒絕一般的 git 推送,這時候你就需要用到 -f 或者 –force 標識了。瞭解更多…

  • 發起一次 Pull Request。
  • Pull request 將會被審查人接收,合併到主分支以後關閉。
  • 如果上述的工作都做完了,就可以刪除你本地的 feature 分支了。

1.3 書寫良好的 commit 資訊

制定並堅持使用的良好的 commit 指導方針,能讓 Git 與他人協作更容易。這裡有一些經驗原則(來源):

  • 將主題行與正文之間用換行符分開為什麼:

    擁有正文部分可讓您對程式碼審閱者進行有用的解釋說明。如果您可以連結到相關聯的 Jira ticket(bug 管理系統)、GitHub issue 、Basecamp to-do 等。大多數桌面 Git 客戶端在其 GUI 中的主題行和正文之間都有明確的分隔。

  • 將主題行限制為50個字元
  • 對主題行進行大寫
  • 不要用時間來作為主題行的結尾
  • 在主題行中使用必要的祈使語氣(imperative mood
  • 將正文限制在72個字元以內
  • 正文應該是用來解釋是什麼,為什麼,而不是怎麼辦

2. Documentation 文件

  • 使用此模板建立 README.md ,隨意新增未覆蓋的內容。
  • 對於具有多個儲存庫的專案,請在各自的 README.md 檔案中提供它們的連結。
  • 隨著專案的發展,保持 README.md 的更新。
  • 註釋你的程式碼,儘可能使每個主要部分的目標儘可能清晰。
  • 如果在 github 或 stackoverflow 有關於你所使用的程式碼或方法的公開討論,請在你的註釋中包含對應連結。
  • 不要將註釋作為糟糕程式碼的藉口。 保持你的程式碼乾淨。
  • 不要使用純淨的程式碼作為藉口,根本不註釋。
  • 隨著程式碼的發展,保證註釋相關。

3. 環境

  • 根據專案規定,定義單獨的開發、測試和生產環境。為什麼:

    不同的環境可能需要不同的資料、令牌、API、埠等。您可能需要一種獨立的開發模式,可以呼叫返回可預測資料的虛擬 API ,從而使自動化和手動測試更容易。或者您可能只想在生產環境加入 Google Analytics 等等。閱讀了解更多…

  • 從環境變數載入您的特定配置,不要將它們作為常量新增到程式碼中,請檢視此示例為什麼:

    您配置的令牌、密碼和其他有價值的資訊,應該正確地與應用程式分開,就像程式碼可以隨時公開一樣。

    怎麼做:

    使用 .env 檔案來儲存變數,並將它們新增到 .gitignore 中以便被 git 排除。提交一個 .env.example 作為開發人員的指南。對於生產環境,您仍然應該以標準方式設定環境變數。閱讀了解更多

  • 建議您在應用程式啟動之前驗證環境變數。使用 joi 檢視這個簡單驗證提供的值 示例為什麼:

      總有一天它會拯救某人的故障排查。

3.1 開發環境一致性:

  • 在 package.json 中設定 node 版本為什麼:

    它讓別人知道專案工程的 node 版本。 閱讀更多…

  • 另外,使用 nvm 並在您的專案根目錄中建立一個 .nvmrc 。 不要忘了在文件中提及它為什麼:

    任何使用 nvm 的人都可以使用 nvm 來切換到合適的 node 版本。 閱讀更多…

  • 您還可以使用 preinstall 的指令碼來檢查 node 和 npm 版本為什麼:

    某些依賴項可能會在較新版本的 node 中使用失敗。

  • 使用 Docker 映象,只要它不會使事情更復雜為什麼:

    它可以在整個工作流程中為您提供一致的環境。不需要操心 libs 、依賴或配置。 閱讀更多…

  • 使用本地模組安裝,而不是使用全域性安裝的模組為什麼:

    讓你與你的同事分享你的工具,而不是期望在他們的系統上安裝它。

4.依賴項

在使用包之前,請檢查它的 GitHub 。 檢查問題的數量,每日下載次數和貢獻者數量以及上次更新軟體包的日期。

  • 如果需要了解依賴項,請在使用之前與團隊進行討論。
  • 跟蹤您當前可用的軟體包:例如,npm ls –depth = 0。 閱讀更多…
  • 檢視您的任何包是否已被使用或不相關:depcheck。 閱讀更多…
  • 檢查下載統計資訊以檢視依賴項是否被社群大量使用:npm-stat。 閱讀更多…
  • 檢查依賴項是否具有良好的成熟版本釋出頻率與大量維護者:例如,npm view async。 閱讀更多…
  • 始終確保您的應用程式適用於最新版本的依賴項,而不會被破壞:npm outdated。 閱讀更多…
  • 檢查軟體包是否存在已知安全漏洞,例如 Snyk 。

4.1 一致性依賴:

  • 在 npm@5 或更高版本上使用 package-lock.json
  • 對於舊版本的 npm,在安裝新的依賴關係時使用 –save -save-exact,並在釋出之前建立 npm-shrinkwrap.json 。
  • 或者,你可以使用 Yarn ,並確保在 README.md 中提及它。你的 lock 檔案和 package.js 在每次依賴關係更新後都應具有同樣的版本。
  • 在這裡閱讀更多:package-locks | npm Documentation

5. Testing 測試

  • 如果可能,請使用測試模式的環境。
  • 將測試檔案放在測試模組旁邊,使用 * .test.js 或 * .spec.js 命名約定,如 module_name.spec.js
  • 將其他測試檔案放入一個單獨的測試資料夾中以避免混淆。
  • 編寫可測試程式碼,避免副作用,提取副作用,編寫純函式
  • 不要寫太多的測試來檢查型別,而是使用靜態型別檢查器
  • 在進行任何 pull 請求開發之前,請先在本地執行測試。
  • 記錄你的測試,並附上說明。

6. 目錄結構與命名

  • 圍繞產品特性/頁面/元件來組織您的檔案,而不是圍繞角色

壞的做法

壞的做法

  • 將測試檔案放在他們的實現旁邊。
  • 將其他測試檔案放在單獨的測試資料夾中以避免混淆。
  • 使用 ./config 資料夾。使用的環境變數由提供配置檔案。
  • 將指令碼放在 ./scripts 資料夾中。這包括用於資料庫同步,構建和捆綁等的 bash 和 node 指令碼。
  • 將構建輸出放在 ./build 資料夾中。並將 build/ 新增到 .gitignore 中。
  • 使用 PascalCase’ ‘camelCase 作為檔名和目錄名。僅對元件使用 PascalCase。
  • CheckBox / index.js應該具有CheckBox元件,這是也是可以作為 CheckBox.js 的。但是CheckCountBox / CheckBox.js 或 checkbox / CheckBox.js ,這些都是冗餘的。
  • 理想情況下,目錄名稱應與 index.js 預設匯出的名稱相匹配。

7. 程式碼風格

  • 為新專案使用 stage-1 和更高版本的 JavaScript(現代)語法。對於舊專案,與現有語法保持一致,除非您打算使專案升級。
  • 在構建過程之前包括程式碼風格檢查。
  • 使用 ESLint – 可插入 JavaScript linter 來強制執行程式碼風格檢查。
  • 使用 Airbnb JavaScript 風格指南閱讀更多。使用 JavaScript 風格指南是您的專案和您的團隊必須做的事情。
  • 使用 ESLint 的 Flow Type 風格檢查規則。當使用 FlowType
  • 使用 .eslintignore 從程式碼樣式檢查中排除檔案或資料夾。
  • 刪除所有 eslint 禁用註釋,然後再執行 Pull 請求。
  • 始終使用 // TODO:註釋來提醒自己和他人關於未完成的工作。
  • 始終註釋並保持與程式碼更改相關。
  • 儘可能刪除註釋的程式碼塊。
  • 避免生產中的 js 警報。
  • 避免不相關或有趣的註釋,日誌或命名(原始碼可能會交給另一家公司/客戶,他們可能不會分享同樣的幽默)。
  • 編寫可測試程式碼,避免副作用,寫純函式。
  • 函式命名儘可能容易搜尋到且有意義的,避免縮短名。對於函式使用長的描述性名稱。函式名稱應該是一個動詞或動詞短語,需要傳達其意圖。
  • 按照 step-down 規則在檔案中組織您的函式。較高階別的函式應該在較低階別的上面。這樣讀取原始碼就更為自然了。

8. Logging 日誌

  • 避免在產品中輸出客戶端控制檯日誌
  • 生成可讀的產品級日誌。推薦在產品模式下使用日誌庫,例如 winston 或 node-bunyan 。

9 API 設計

遵循面向資源的設計。這有三個主要因素:資源、資料集合和 URL 。

  • 資源擁有資料,與其他資源的關係以及對其進行操作的方法
  • 一組資源稱為集合。
  • URL 標識資源的線上位置。

9.1.1 URLs 的命名

  • /users 使用者的集合(複數名詞)。
  • /users/id 具有特定使用者資訊的資源。
  • 資源在 URL 中始終應該是複數。 將動詞從資源網址中刪除。
  • 對於非資源使用動詞。在這種情況下,你的 API不 會返回任何資源。而是執行一個操作並將結果返回給客戶端。 因此,你應該在 URL 中使用動詞而不是名詞來清楚地區分與資源相關的響應中的非資源響應。

9.1.2 欄位的命名

  • 請求體或響應型別是 JSON 時,請遵循 camelCase 來保持一致性。
  • 公開資源,而不是資料庫模式詳細資訊。你不必使用 table_name 作為資源名稱。與資源屬性相同,它們不應與你的列名稱相同。
  • 僅在 URL 命名中使用名詞,不要嘗試解釋其功能,只解釋資源(優雅)。

9.2 資源上的操作

9.2.1 使用 HTTP 方法

僅在資源 URL 中使用名詞,避免像 /addNewUser 或 /updateUser 這樣的終端。同時避免傳送資源操作作為引數。而是使用 HTTP 方法來實現相關功能:

  • GET 用於檢索資源的內容。
  • POST 用於建立新的資源和子資源
  • PUT 用於更新已經存在的資源
  • PATCH 用於更新已經存在的資源。PATCH 僅更新所提供的欄位,而不改動其他的欄位。
  • DELETE 用於建立已經存在的資源

9.3 使用子資源

子資源用於將一個資源與另一個資源連結,因此使用子資源來表示關係。 API 應該是為開發人員提供的介面,那麼使得資源可檢視是自然的需求。如果存在類似僱員與公司聯絡的資源之間的關係,請在 URL 中使用 id :

  • GET /schools/2/students 應該從學校2獲得所有學生的列表。
  • GET /schools/2/students/31 應該獲得在學校2中就讀的學生31的詳細資訊
  • DELETE /schools/2/students/31 應該刪除在學校2就讀的學生31的資訊
  • PUT /schools/2/students/31 應該更新學生31的資訊,僅在資源URL上使用PUT,而不是整個資料集合。
  • POST /schools 應該建立一個新的學校,並返回所建立學校的全部資訊。請在集合URL中使用POST

9.4 API 版本化

當你的 API 對其他第三方公開時,升級 API 會出現一些破壞性的變化,也會導致破壞那些使用你的 API 的現有產品或服務。在URL中的使用版本化可以防止這種情況發生: http://api.domain.com/v1/schools/3/students

9.5 傳送反饋

9.5.1 錯誤

響應訊息必須是自描述的。好的錯誤訊息響應可能看起來是這樣的:

或用於校驗錯誤的:

注意:儘可能保持安全異常訊息的通用性。例如,你可以回覆說“無效的使用者名稱或密碼”,並在不知不覺中通知使用者使用者名稱確實是正確的,只有密碼不正確,而不是回覆“錯誤的密碼”。

9.5.2 使用 HTTP 訊息碼對齊你的反饋

客戶端和 API 工作正常(成功 – 2xx響應碼)

  • 200 OK 當前 HTTP 響應表示成功執行 GET, PUT 或 POST 請求
  • 201 Created 該狀態碼必須在一個新例項建立時返回。例如:在建立一個新例項時,使用 POST 方法,應該總是返回 201 狀態碼。
  • 204 No Content 表示請求已經被處理,但是沒有返回任何內容。DELETE 就是對此的一個好示例。如果發生任何錯誤,響應碼可能就不在2xx成功範圍而是4xx客戶端錯誤範圍內。

客戶端應用程式表現異常(客戶端錯誤 – 4xx 響應碼)

  • 400 Bad Request 表示客戶端的請求並沒有被處理,因為伺服器無法理解客戶端的請求。
  • 401 Unauthorized 表示請求缺乏必要的證照無法訪問需要的資源,客戶端需要重新使用證照傳送請求。
  • 403 Forbidden 表示請求是合法的,客戶端已經獲得授權,但是該客戶端因某種原因被禁止訪問該頁面和資源。
  • 404 Not Found 表示未找到被請求的資源。
  • 406 Not Acceptable 和定義在 Accept-Charset 和 Accept-Language 頭中的可接受值列表匹配的響應無法提供。
  • 410 Gone 表示所請求的資源已經無法訪問,並且被特意、永久移除。

API 處理異常 (伺服器錯誤—— 5xx響應碼)

  • 500 Internal Server Error 表示請求是有效的,但伺服器因為某種未知原因無法完成該請求。
  • 503 Service Unavailable 表示伺服器掛掉或者無法接收和處理該請求。多數情況下如果伺服器處理維護或者短時過載可能會遇到該情況。

9.6 資源引數和後設資料

  • 在響應中提供資源的總數
  • 還應考慮公開資源的資料量。API 使用者並不總是需要資源的完整表示。使用欄位查詢引數,該引數採用逗號分隔的欄位列表來,包括:
  • 分頁,過濾和排序不需要預設為所有資源提供支援。文件化那些提供了過濾和排序的資源。

9.7 API 安全性

9.7.1 TLS

為了保護你 Web API 身份驗證,所有身份驗證都應使用 SSL 。OAuth2 需要授權伺服器和訪問令牌的證照才能使用 TLS 。在 HTTP 和 HTTPS 之間切換會引入安全漏洞,最佳實踐是預設使用 TLS 進行所有通訊。 丟擲非安全訪問 API 網址的錯誤。

9.7.2 速率限制

如果你的 API 是公開的或擁有大量的使用者,任何客戶端都可以每小時呼叫你的 API 數千次。你應該儘早考慮實施限速。

9.7.3 輸入校驗

如果允許的值有限,很難實施大多數攻擊。

  • 驗證必填欄位、欄位型別(例如字串,整數,布林型別等)和格式要求。 返回 400 錯誤請求,其中包含有關錯誤或丟失資料錯誤的詳細資訊。
  • 轉移那些可能成為 SQL 語句一部分的引數,以防止 SQL 注入攻擊
  • 如前所述,在命名資源和定義響應時,不要暴露你的資料庫方案

9.7.4 URL 校驗

攻擊者可以篡改 HTTP 請求的任何部分,包括 URL、查詢字串。

9.7.5 校驗收到的 content-types

伺服器不應該假定 Content-Type 。 缺少 Content-Type 頭或意外的 Content-Type 頭應導致伺服器拒絕該內容,並返回 406 Not Acceptable 響應。

9.7.6 JSON 編碼

JSON 編碼器的一個關鍵問題是阻止在瀏覽器或伺服器上的 node.js 中執行任意的 JavaScript 遠端程式碼。在輸入資料上使用 JSON 序列化器,以防止在瀏覽器/伺服器上執行使用者輸入。

9.8 API 文件

  • 參考 README.md template 一節為 API 填寫參考部分。
  • 用程式碼示例描述 API 驗證方法
  • 解釋 URL 結構(僅路徑,無根 URL ),包括請求型別(方法)

對每個端點說明:

  • URL Params:如果 URL 引數存在,請根據 URL 部分中提到的名稱來指定它們
  • 如果請求型別為 POST ,請提供一個可工作的示例程式碼。URL Params 規則也適用於此。將該部分分為“可選的”和“必需的”。
  • 成功的響應,狀態碼應該是什麼,是否有返回資料?當人們需要知道他們的回撥應該等待什麼時,這是有用的!
  • 錯誤響應,大多數端點有許多方法可以導致失敗。從未經授權的訪問,到不正確的引數等。所有這些應該列在這裡。 它看起來似乎是重複的,但它有助於防止預設假定。 例如

9.8.1 API 設計工具

有許多開源工具可用於好的文件設計,例如 API Blueprint 和 Swagger 。

10. 許可證(協議)

確保你有權使用該資源。如果你使用庫,請記住使用 MIT、Apache 或 BSD ,但如果你修改了它們,請檢視許可證中的詳細資訊。 有版權的影象和視訊可能會導致法律問題。

相關文章