本系列一共七章,Github 地址請查閱這裏。原文地址請查閱這裏。javascript
雖然有一些通用的項目結構指南,可是沒有適合全部項目的結構。有興趣的童鞋能夠看看來自Node Hero系列的 Nodejs 項目結構指南。java
NX 旨在成爲一個開源社區驅動的工程,易於擴展和可伸縮性強。node
最終的依賴關係圖以下:git
這個結構爲一些經典的框架的相關痛點提供了一個解決方案。github
易擴展性是開源驅動工程的一個必備條件。爲了實現它,工程必須具有小型的內核和預約義的依賴處理系統。前者保證工程是易讀的,然後者則確保它將保持這種狀態。後端
在本節中,我將專一於設計一個小型內核。設計模式
現代框架的主要特性是可以建立自定義組件並在 DOM 中使用它們。NX 核心內置了 component
函數具有這一功能。它容許用戶配置和註冊一個新的組件類型。bash
component(config)
.register('comp-name')
複製代碼
註冊的 compname
是一個空白的組件類型,能夠按預期在 DOM 中實例化。框架
<comp-name></compname>
函數
下一步是要保證組件能夠用新的功能進行擴展。爲了保持簡單性和可擴展性,這些新功能不該該污染內核。這就是依賴注入的方便之處。
若是你不熟悉依賴注入,我建議您閱讀這篇文章。
依賴注入是一種設計模式,其中一個或者多個依賴(或服務)被注入或者經過引用傳遞到依賴對象中。
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
複製代碼
存儲系統能夠被工程內部的全部模塊所訪問可是私有的部分不會暴露出去。公有部分能夠被用來暴露底層的功能給外部開發人員。這樣能夠防止意外引用錯誤,由於開發人員不得不經過顯式地引用相應的標記來使用變量。另外,標記引用不會像字符串那樣產生衝突,因此就能夠避免命名衝突。
如下總結了不一樣場景下的模式的使用。
正常使用。
function (elem, state, next) {
elem.publicText = 'Hello World'
next()
}
複製代碼
跨做用域變量,即項目的私有變量,應該有一個被添加到私有標記庫的標記鍵值。
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()
}
複製代碼