前言
看到英文技術文件、快哭了。含著淚也要把他讀完。
原文How I Structure My JavaScript File
內容
很多人都在問我怎麼寫我的JavaScript——好吧,這是一個謊言,沒人問我,但是如果他們這麼做了,我想指出這篇文章。在使用了多年的PHP之後,在閱讀了乾淨程式碼(和其他書籍)之後,我在多年中採用了我的程式碼風格。是的,PHP,不要敲它,它有一個偉大的社群和偉大的編碼標準。當然,多年來和別人一起寫作,跟隨不同公司的風格。
結構並不依賴於JS模組,但我現在傾向於只編寫JS模組,所以我將使用這些模組。
結構,總結如下:
入口
在檔案的頂部是匯入。這是有道理的,他們比其他任何東西都要高。進口的順序無關緊要,除非你使用一些掛鉤(比如babel hook),所以我傾向於選擇:
-
本地模組-節點本地的東西。
-
庫模組- lodash, knex,等等。
-
本地庫——比如../db。
-
本地檔案- like ./helper或類似的。
讓我的模組保持有序,讓我更容易看到我在匯入什麼,以及我實際上在使用什麼。當我開始編寫程式碼時,我也傾向於以這種方式編寫依賴關係。
我傾向於不關心字母排序(除了破壞匯入之外),我也沒有看到它的一個點。
本地模組
我傾向於將本地模組放在最上面,並以這樣的主題保持一個清晰的組織:
如果我在瀏覽器中,顯然跳過這一步。
庫模組
我儘可能只從庫中匯入我需要的東西,但是,我又一次將它們分組到某個主題中。
我還注意到,如果我正在執行一個預設的匯入(例如。我傾向於把它放在我的庫模組的頂部,並將被破壞的匯入降低。沒有必要,但我喜歡它的視覺效果。
Local/Internal libraries
在本地庫中,我指的是本地共享的模組,比如db。設定與書架連線的js檔案。或者,在我的工作中,我們有幾個庫處理我們產品中使用的數字和計算。
Local files
最後,我匯入本地檔案,這些檔案通常與我正在處理的檔案或一個目錄(最多)在同一個資料夾中。例如,我為Redux寫了一個減速器,並把它放在一個單獨的資料夾中。在該資料夾中,我還保留了一個助手檔案,通常命名為[reducer name]Helpers.js:
Constants
在匯入所有依賴項之後,我通常會做一些前期工作,這些工作將在模組的其餘部分中使用。例如,我從書架例項中提取了knex。或者我可以設定值常數。
使用非常量通常表示我依賴於某種型別的單例。我儘量避免使用它們,但有時它是必要的,因為沒有簡單的其他方法來完成它,或者它不重要(比如一次性命令列指令碼)。
Exports
在我基本設定了所有模組級別的依賴項之後:無論它們是常量值還是匯入庫,我都試著將我的匯出分組到檔案的頂部。基本上,這就是我把功能作為模組的粘合劑,實現模組的最終目的。
在Redux的情況下,我可能匯出一個單一的減速器,然後將工作分解並呼叫相關的邏輯。在ExpressJS的情況下,我可能在這裡匯出所有的路由,而實際的路由邏輯在下面。
我想說的是,這並不是我出口功能的唯一部分。
我感覺模組系統的工作方式使得在暴露最窄的API和匯出函式在測試中使用它們之間的界限變得有點困難。
例如,在上面的例子中,我從來不想在模組之外使用calculateSomething。我不完全確定OOP語言是如何處理測試私有函式的,但這是一個類似的問題。
Core Logic
這看起來很奇怪,但核心邏輯對我來說是最重要的。我完全理解當人們翻轉匯出和核心邏輯時,但這對我來說很有效,原因有很多。
當我開啟一個檔案時,頂層函式會告訴我抽象步驟中將會發生什麼。我很喜歡這樣。我一眼就能看出檔案的作用。我做了大量的CSV操作,並將其插入到DB中,而頂級函式始終是一個容易理解的流程,它有一個類似的流程:fetchCSV aggregateData insertData terminate script。
核心邏輯總是包括從上到下的出口。在內聯的例子中,我們有這樣的東西:
注意,readCSV沒有。這聽起來很普通,我應該把它放到一個helper檔案中,然後將它匯入上面。除此之外,你可以看到我的出口,而不是進退兩難。我不希望在模組外部提供聚合資料,但我仍然希望測試它。
在此之外,我傾向於把“meatier”函式放在上面,下面的函式更小。如果我有一個特定於模組的實用函式,一個我在多個地方使用的函式,但只在模組中使用,我將把它們放在底部。基本上,我的命令是:複雜性+使用。
所以順序是:
-
核心邏輯函式——由頂級匯出使用的函式。
-
更簡單/更小的函式——核心邏輯函式使用的函式。
-
實用函式——模組周圍多個地方使用的小函式(但不匯出)
Core-logic functions
核心邏輯函式就像我匯出的函式的“sub-glue”。根據模組的複雜性,這些可能存在,也可能不存在。功能的分解不是必需的,但是如果一個模組足夠大,核心邏輯函式就像主函式中的步驟。
如果你寫的是反應或角度,這些你的元件將是我上面提到的出口函式。但是,核心邏輯函式將是各種偵聽器或資料處理器的實現。用Express,這些將是您的特定路線。在一個Redux減速器中,這些將是在鏈條上足夠遠的單獨的減速器,沒有一個開關/case語句。
如果您處於角度,那麼在類中組織這些函式而不是在整個檔案的範圍內是完全公平的。
Simpler/Smaller functions
這些函式通常是核心邏輯和純實用程式之間的中間步驟。你可能會用到它們,或者它們可能只是一個比效用函式更復雜的tad。我可能會刪除這個類別,並說“寫你的功能,以減少複雜性或工作量”。
這裡沒有提及。也許onHandleInput事件監聽器需要一些邏輯來處理$event資料,所以如果它是純的,您可以將它從類中刪除,如果不是,您可以在類中保留它:
Utility functions
最後,效用函式。我傾向於組織最接近我使用它們的工具。在相同的檔案中,或者相同的資料夾(必要時),相同的模組,等等。每次使用從檔案中擴充套件到專案的根或它自己的NPM模組時,我都將函式移出一個級別。
在我看來,效用函式應該始終是一種純方法,這意味著它不應該在它的範圍之外訪問變數,而且應該只依賴傳遞到它的資料,而且不需要任何形式的副作用。除非使用實用程式函式來訪問API或訪問資料庫。因為這些被認為是副作用,所以我認為它們是唯一的例外。
Anything else?
當然!我認為每個人都有自己獨特的書寫方式。上面所描述的結構在我多年來每天編寫大量程式碼的過程中非常有效。最終,許多細微的差別開始出現,我發現自己編寫程式碼更快,更喜歡它,並且更容易除錯和測試。
在我完成這篇文章之前,我想和大家分享一些我已經習慣了的編碼,它們與文件結構的關係更少,更多的是在編寫實際程式碼時更傾向於使用小的偏好。
早期的回報
當我發現早期的回報時,那是一瞬間。當您可以提前返回時,為什麼要在else語句中封裝大量程式碼?
我的經驗法則是,如果早期的返回條件小於其餘的程式碼,那麼我將寫早期的返回,但是如果不是,我將顛倒程式碼,這樣較小的程式碼塊總是提前返回。
早期的回報在交換機上也很出色,我是Redux的超級粉絲。
分號塊
雖然我不再使用它(沒有更漂亮的支援),但我總是用分號來終止函式連結,在一個單獨的行上,一個縮排到鏈的縮排的左邊。這就建立了一個整潔的程式碼塊,程式碼不只是掛在那裡。
當然,這意味著我也喜歡用分號來代替。
Or better written, it might look like this:
總結
使用的是有道線上翻譯,如有不正確之處還望大佬指出。