從 v1.0 開始,所有 Sails 應用程式都內建支援helpers,這是一種簡單的工具,可讓您在多個地方共享 Node.js 程式碼。這有助於避免重複程式碼,並透過減少錯誤和最小化重寫來提高開發效率。與 actions2 一樣,這也讓為您的應用程式建立文件變得更加容易。
在 Sails 中,helpers 是建議的方法,用於將重複的程式碼提取到單獨的檔案中,然後在各種actions、自訂回應、命令列腳本、單元測試,甚至其他 helpers 中重複使用該程式碼。您不一定要使用 helpers,事實上您可能一開始甚至不需要它們。但是隨著您的程式碼庫增長,helpers 對於您應用程式的可維護性將變得越來越重要(而且,它們真的非常方便)。
例如,在建立 Node.js/Sails 應用程式用來回應用戶端請求的 actions 過程中,您有時會發現自己在多個地方重複程式碼。當然,這很容易出錯,更不用說很煩人。幸運的是,有一個簡潔的解決方案:用對自訂 helper 的呼叫來取代重複的程式碼
const greeting = await sails.helpers.formatWelcomeMessage('Bubba');
sails.log(greeting);
// => "Hello, Bubba!"
Helpers 可以從幾乎程式碼中的任何地方呼叫,只要該位置可以存取
sails
應用程式實例。
以下是一個簡單、定義完善的 helper 範例
// api/helpers/format-welcome-message.js
module.exports = {
friendlyName: 'Format welcome message',
description: 'Return a personalized greeting based on the provided name.',
inputs: {
name: {
type: 'string',
example: 'Ami',
description: 'The name of the person to greet.',
required: true
}
},
fn: async function (inputs, exits) {
const result = `Hello, ${inputs.name}!`;
return exits.success(result);
}
};
雖然簡單,但這個檔案展示了一個良好 helper 的幾個特徵:它以友善的名稱和描述開頭,立即清楚地說明了該工具的作用,它描述了其輸入,因此很容易看出該工具的使用方式,並且它以最簡單的方式完成了離散的任務。
fn
函數helper 的核心是 fn
函數,它包含 helper 將執行的實際程式碼。該函數接受兩個參數:inputs
(輸入值的字典,或稱為「argins」)和 exits
(回呼函數的字典)。fn
的工作是利用和處理 argins,然後觸發提供的 exits 之一,將控制權返回給呼叫 helper 的程式碼。請注意,與使用 return
向呼叫者提供輸出的典型 JavaScript 函數相反,helpers 透過將結果值傳遞給 exits.success()
來提供該結果值。
Helper 宣告的輸入類似於典型 JavaScript 函數的參數:它們定義了程式碼必須處理的值。但是,與標準 JavaScript 函數參數不同,輸入會自動驗證。如果 helper 被呼叫時使用的 argins 與其對應輸入的類型錯誤,或缺少必要輸入的值,則會觸發錯誤。因此,helpers 是自我驗證的。
Helper 的輸入在 inputs
字典中定義。每個輸入定義至少由一個 type
屬性組成。Helper 輸入支援以下類型:
string
- 字串值number
- 數值(整數和浮點數皆有效)boolean
- 值 true
或 false
ref
- JavaScript 變數參考(可以是任何值,包括字典、陣列、函數、串流等)這些是您可能已經習慣從定義模型屬性中使用的相同資料類型(和相關語意)。因此,正如您可能預期的那樣,您可以透過設定其 defaultsTo
屬性來為輸入提供預設值。或者您可以透過設定 required: true
使其成為必填項。您甚至可以使用 allowNull
和幾乎任何更高等級的驗證規則,例如 isEmail
。
當呼叫 helper 時,您傳入的參數與該 helper 宣告的 inputs
中的鍵順序相對應。或者,如果您寧願按名稱傳入 argins,請使用 .with()
const greeting = await sails.helpers.formatWelcomeMessage.with({ name: 'Bubba' });
Exits 描述了 helper 可能會有的所有不同結果,無論好壞。每個 helper 都自動支援 error
和 success
exits。當呼叫 helper 時,如果其 fn
觸發 success
,則它將正常返回。但是,如果其 fn
觸發了除了 success
之外的某些 exit,則它將拋出 Error(除非使用了 .tolerate()
)。
在必要時,您也可以公開其他自訂 exits(稱為「例外」),允許呼叫您的 helper 的使用者程式碼處理特定的異常情況。這有助於保證您程式碼的透明度和可維護性,因為它可以輕鬆地宣告和協商錯誤。
Helper 的例外(自訂 exits)在
exits
字典中定義。最佳實務是為所有自訂例外提供明確的description
屬性。
想像一下一個名為“inviteNewUser”的 helper,它公開了一個自訂的 emailAddressInUse
exit。如果提供的電子郵件已存在,則 helper 的 fn
可能會觸發此自訂 exit,讓您的使用者程式碼可以處理此特定情境,而不會混淆您的結果值或求助於額外的 try/catch
區塊。
例如,如果從具有自己“badRequest” exit 的 action 中呼叫此 helper
const newUserId = sails.helpers.inviteNewUser('[email protected]')
.intercept('emailAddressInUse', 'badRequest');
上面花俏的簡寫只是一種更快的寫法
.intercept('emailAddressInUse', (err)=>{ return 'badRequest'; });
至於 .intercept(),它只是另一個捷徑,因此您不必被迫手動編寫自訂的 try/catch 區塊來協商這些錯誤。
在內部,您的 helper 的 fn
負責觸發其 exits 之一,無論是透過拋出特殊的 exit 信號,還是透過調用 exit 回呼(例如 exits.success('foo')
)。如果您的 helper 透過 success exit 發送回結果(例如 'foo'
),那麼這將是 helper 的傳回值。
注意:對於非 success exits,Sails 將使用 exit 的預定義描述自動建立適當的 JavaScript Error 實例(如果需要)。
預設情況下,所有 helpers 都被視為非同步。雖然這是一個安全的預設假設,但並非總是如此。當您確定您的 helper 是同步時,您可以透過使用 sync: true
屬性告訴 Sails 來最佳化效能。這允許使用者程式碼在呼叫 helper 時無需 await
。但是如果您將 sync
設定為 true
,請不要忘記將 fn: async function
更改為 fn: function
!
注意:在沒有
await
的情況下呼叫非同步 helper 將無法運作。
req
如果您正在設計一個 helper,專門用於解析請求標頭以在 actions 中使用,那麼您將需要利用 request 物件的預先存在的方法和/或屬性。允許您的 action 中的程式碼將 req
傳遞給您的 helper 的最簡單方法是定義 type: 'ref'
輸入
inputs: {
req: {
type: 'ref',
description: 'The current incoming request (req).',
required: true
}
}
然後,為了在您的 actions 中使用您的 helper,您可以編寫如下程式碼
const headers = await sails.helpers.parseMyHeaders(req);
Sails 提供了一個內建產生器,您可以使用它來自動建立新的 helper
sails generate helper foo-bar
這將建立一個檔案 api/helpers/foo-bar.js
,可以在您的程式碼中以 sails.helpers.fooBar
的方式存取。最初建立的檔案將是一個通用的 helper,沒有任何輸入,只有預設的 exits(success
和 error
),它在執行時會立即觸發其 success
exit。
每當 Sails 應用程式載入時,它都會找到 api/helpers/
中的所有檔案,將它們編譯成函數,並使用檔案名稱的駝峰式版本將它們儲存在 sails.helpers
字典中。然後可以從您的程式碼中調用任何 helper,只需使用 await
呼叫它,並提供一些 argin 值
const result = await sails.helpers.formatWelcomeMessage('Dolly');
sails.log('Ok it worked! The result is:', result);
這與您可能已經熟悉的 模型方法(如
.create()
)的使用方式大致相同。
如果 helper 宣告了 sync
屬性,您也可以在沒有 await
的情況下呼叫它
const greeting = sails.helpers.formatWelcomeMessage('Timothy');
但是在您移除 await
之前,請確保 helper 實際上是同步的。沒有 await
非同步 helper 將永遠不會執行!
如果您的應用程式使用許多 helpers,您可能會發現將相關的 helpers 分組到子目錄中很有幫助。例如,假設您有許多 user
helpers 和幾個 item
helpers,它們組織在以下目錄結構中
api/
helpers/
user/
find-by-username.js
toggle-admin-role.js
validate-username.js
item/
set-price.js
apply-coupon.js
當呼叫這些 helpers 時,每個子資料夾名稱(例如 user
和 item
)都會成為 sails.helpers
物件中的額外屬性層,因此您可以使用 sails.helpers.user.findByUsername()
呼叫 find-by-username.js
,並且可以使用 sails.helpers.item.setPrice()
呼叫 set-price.js
。
如需更多資訊,您可以閱讀 Ryan Emberling 和 Mike McNeil 之間的對話,其中更詳細地介紹了這個用例,包括一些使用自訂 helpers 和 organics 的一般技巧和訣竅。
為了更精細的錯誤處理(甚至對於那些不太算是錯誤的例外情況),您可能習慣於設定某種錯誤代碼,然後嗅探出錯誤。這種方法運作良好,但可能很耗時且難以追蹤。
幸運的是,在 Sails helpers 中有幾種不同的方法可以方便地處理錯誤。請參閱關於 .tolerate()、.intercept() 和 特殊 exit 信號的頁面以獲取更多資訊。
雖然此範例中的用法過於繁瑣,但很容易想像在某些情況下,依賴諸如 notUnique
之類的自訂 exits 會很有幫助。儘管如此,您也不希望每次都必須處理每個自訂 exit。理想情況下,您只需在必要時在使用者程式碼中處理自訂 exit:無論是為了實作某種功能,甚至是為了改善使用者體驗或提供更好的內部錯誤訊息。
幸運的是,Sails helpers 支援「自動 exit 轉發」。這表示使用者程式碼可以選擇在個別情況下與盡可能少或盡可能多的自訂 exits 整合。換句話說,當呼叫 helper 時,如果您不需要,完全可以忽略其自訂的 notUnique
exit。這樣,您的程式碼將盡可能保持簡潔和直觀。如果情況發生變化,您可以隨時修改您的程式碼以稍後處理自訂 exit。
sails-hook-organics
(捆綁在「Web App」範本中)附帶了幾個免費、開放原始碼且經 MIT 許可的 helpers,適用於許多常見的用例。看看吧!