本系列一共七章,Github 地址請查閱這裡。原文地址請查閱這裡。
雖然有一些通用的專案結構指南,但是沒有適合所有專案的結構。有興趣的童鞋可以看看來自Node Hero系列的 Nodejs 專案結構指南。
NX JavaScript 框架概覽
NX 旨在成為一個開源社群驅動的工程,易於擴充套件和可伸縮性強。
- 它擁有現代客戶端框架所期望的所有功能。
- 除了墊片,它沒有外部依賴項。
- 它總共有將近 3000 行程式碼。
- 每個模組不超過 300 行。
- 每個功能模組不超過 3 個依賴。
最終的依賴關係圖如下:
這個結構為一些經典的框架的相關痛點提供了一個解決方案。
- 可擴充套件性
- 依賴注入
- 私有變數
實現可擴充套件性
易擴充套件性是開源驅動工程的一個必備條件。為了實現它,工程必須具備小型的核心和預定義的依賴處理系統。前者保證工程是易讀的,而後者則確保它將保持這種狀態。
在本節中,我將專注於設計一個小型核心。
現代框架的主要特性是能夠建立自定義元件並在 DOM 中使用它們。NX 核心內建了 component
函式具備這一功能。它允許使用者配置和註冊一個新的元件型別。
component(config)
.register('comp-name')
複製程式碼
註冊的 compname
是一個空白的元件型別,可以按預期在 DOM 中例項化。
<comp-name></compname>
下一步是要保證元件可以用新的功能進行擴充套件。為了保持簡單性和可擴充套件性,這些新功能不應該汙染核心。這就是依賴注入的方便之處。
用中介軟體進行依賴注入(DI)
如果你不熟悉依賴注入,我建議您閱讀這篇文章。
依賴注入是一種設計模式,其中一個或者多個依賴(或服務)被注入或者通過引用傳遞到依賴物件中。
DI 刪除了硬編碼引入依賴,但卻產生了一個新的問題。使用者不得不去了解如何配置和注入所有的依賴。大多數的客戶端框架都會有 DI 容器代替使用者來做這件事。
一個依賴注入容器是一個知道如何例項化和配置物件的物件。
另一項技術是中介軟體依賴注入模式(middleware DI pattern),它被廣泛應用於後端(Express, Koa)。這裡的竅門在於所有的可注入依賴(中介軟體)擁有一致的介面,並且可以以同樣的方式被注入。在這種情況下,是不需要 DI 容器的。
我採用這個解決方案是為了保持簡單。如果你用過 Express,下面的程式碼會非常熟悉。
component()
.use(paint) // 注入畫圖中介軟體
.use(resize) // 注入重調大小中介軟體
.register('comp-name')
function paint(elem, state, next) {
elem.style.color = 'red'
next()
}
function resize(elem, state, next) {
elem.style.width = '100px'
next()
}
複製程式碼
當新的元件例項掛載到 DOM 的時候,執行中介軟體然後為元件例項擴充套件新的功能。用其它不同的庫來擴充套件相同的物件會導致命名衝突。私有變數的暴露會讓這個問題複雜化,並且可能會導致被其它人的不經意間所引用而導致事故。
解決這個問題的辦法是利用一個公共的 API 來暴露公共變數然後隱藏掉其它的變數是一個好的實踐。
處理私有變數
在 JavaScript 中以函式作用域來處理私有變數的。當引入跨函式作用域的私有變數的時候,人們會試圖為私有變數新增 _
字首以表示它們的私有性,然後暴露為公有變數。這個可以防止意外的引用,但是仍然無法避免命名衝突。一個更好的替代方案是使用 ES6 的 Symbol
資料型別。
Symbol 是指的一個唯一和固化的資料型別,可以被用來作為物件的屬性。
以下為一個 symbol
示例
const color = Symbol()
function colorize(elem, state, next) {
elem[color] = 'red'
next()
}
複製程式碼
現在 red
只能被 color
標記的引用來讀取。'red'
的私有性可以通過暴露 color
標記為不同的值來控制。當有了一定量的私有變數的時候,使用一個集中標記儲存系統是一種優雅的解決方案。
exports.private = {
color: Symbol('color from colorize')
}
exports.public = {}
複製程式碼
新增 index.js
,內容如下:
const symbols = require('./symbols')
exports.symbols = symbols.public
複製程式碼
儲存系統可以被工程內部的所有模組所訪問但是私有的部分不會暴露出去。公有部分可以被用來暴露底層的功能給外部開發人員。這樣可以防止意外引用錯誤,因為開發人員不得不通過顯式地引用相應的標記來使用變數。另外,標記引用不會像字串那樣產生衝突,所以就可以避免命名衝突。
以下總結了不同場景下的模式的使用。
1.公有變數
正常使用。
function (elem, state, next) {
elem.publicText = 'Hello World'
next()
}
複製程式碼
2.私有變數
跨作用域變數,即專案的私有變數,應該有一個被新增到私有標記庫的標記鍵值。
exports.private = {
text: Symbol('private text')
}
exports.pubic = {}
複製程式碼
當需要的時候引用。
const private = require('symbols').private
function (elem, state, next) {
elem[private.text] = 'Hello World'
next()
}
複製程式碼
半私有化變數
須在公共標記表中新增底層 API 的變數的標記鍵值。
exports.private = {
text: Symbol('private text')
}
exports.public = {
text: Symbol('exposed text')
}
複製程式碼
需要的時候載入。
const exposed = require('symbols').public
function (elem, state, next) {
elem[exposed.text] = 'Hello World'
next()
}
複製程式碼