本文適合許多發現 Ghost (https://ghost.org/docs/themes/helpers/) 提供的標準助手不夠的開發人員和主題創作者。尋找方法來擴展使用 Ghost 提供的 Handlebars 的主題的功能是完全正常的。在發表這篇文章並找到適合我的主題的解決方案之前,我搜尋了整個網路並親自對 Ghost 的原始程式碼進行了分析。
我發現可以使用額外的幫助程式來擴充 Ghost 的原始碼。我透過在 current/core/frontend/apps 中新增目錄來實現這一點。我使用了一個名為 amp 的現有「應用程式」的範例,其程式碼非常簡單,開始建立主題中可用的新助理。在這些現有的應用程式中,結構很簡單,因為助手在 lib/helpers 中註冊。在此過程的最後,您需要將 apps 中的目錄名稱新增至 apps.internal JSON 部分的 current/core/shared/config/overrides.json 中。
我們應用程式中的index.js 檔案的範例內容如下:
const path = require('path'); module.exports = { activate: function activate(ghost) { ghost.helperService.registerDir(path.resolve(__dirname, './lib/helpers')); } };
接下來,在此應用程式的 lib 目錄中,我們建立一個名為 helpers 的資料夾。在裡面,我們建立一個新文件,它將是要在 Handlebars 範本中呼叫的助手的名稱。例如,我們將其命名為 uppercase.js。
下面是此類助理程式碼的範例,它只是將助手參數中給定文字的字母轉換為大寫:
const {SafeString, escapeExpression} = require('../../../../services/handlebars'); module.exports = function uppercase(text) { return `${text.toUpperCase()}`; };
不要忘記將應用程式目錄的名稱加入 current/core/shared/config/overrides.json 中。重新啟動 Ghost 後,一切都應該準備就緒。
我最近開發了這個方法,您不僅可以將其套用到自架 Ghost,還可以套用到主機供應商提供的 Ghost 執行個體。在後一種情況下,需要適當的架構規劃並購買一台小型伺服器來充當最終 Ghost 實例的代理。
我們將在此方法中使用的架構:
Nginx 伺服器 ← Node.js 中間件 ← Ghost 實例
使用者的瀏覽器向Nginx伺服器發送請求,Nginx伺服器包含中間件的上游。所有請求,無論位於何處,都將被代理到中間件。
中間件是一個在 Node.js 中運行的 Express 伺服器,增加了express-http-proxy (https://github.com/villadora/express-http-proxy) 庫,這顯著簡化了工作。我們配置代理來與 Ghost 實例通訊。 express-http-proxy 庫有一個 userResDecorator 屬性,我們可以使用它來「裝飾代理伺服器的回應」。簡單來說,我們可以在將 Ghost 的回應傳送到使用者的瀏覽器之前對其進行修改。
我們的 userResDecorator 將是非同步的,以免阻塞主執行緒。創建助手時我們將回到非同步處理的主題。目前,您需要知道並非使用者瀏覽器要求的所有內容都需要進行修飾。因此,第一步是檢查 Ghost 回應的內容類型標頭。您可以如下進行操作,然後比較是否為 text/html,僅裝飾傳回給使用者的 HTML 文件:
const path = require('path'); module.exports = { activate: function activate(ghost) { ghost.helperService.registerDir(path.resolve(__dirname, './lib/helpers')); } };
在這個條件語句中,我們可以開始修改htmlContent,但是為什麼我們需要它呢?讓我們先為 Ghost 主題中的自訂助手建立基礎!
在本文中,我將在主題的 index.hbs 檔案(首頁)中建立一個自訂助手。在 Handlebars 模板的可見位置,我新增了一個範例自訂助手,將其命名為 {{hello_world}}。
⚠️ 然後,我將其放在主頁上的可見位置 - 但請注意當我刷新 Ghost 頁面時會發生什麼!
const {SafeString, escapeExpression} = require('../../../../services/handlebars'); module.exports = function uppercase(text) { return `${text.toUpperCase()}`; };
在此變數中,我們將 Ghost 實例的回應作為頁面的完整 HTML。假設此回應是您的 Ghost 實例的主頁。 HTML 內容還將包括我們的純文字 {{hello_world}},它顯示為純文字。如果我們的自訂助手採用這種形式,我們可以在中間件中使用 Handlebars.js (https://handlebarsjs.com/) 來編譯它。請記住首先透過套件管理器安裝該庫,例如 npm:npm install handbars 並將其添加到您的程式碼中:const handbars = require("handlebars");。
// Where 'proxyRes' is your proxy response inside 'userResDecorator' const contentType = proxyRes.headers['content-type'] || ''; if (!contentType.includes('text/html')) { // Return original content if response is not 'text/html' return proxyResData; } let htmlContent = proxyResData.toString('utf8'); // Do something with 'htmlContent' and return return htmlContent;
哇!我們現在已經使用 Handlebars.js 編譯並渲染了 HTML——但我們還沒有完成。我們仍然需要註冊我們的自訂助手 {{hello_world}}。加入以下程式碼,最好在初始化 Handlebars.js 之後:
{{!< default}} <div> <p>After refreshing, I get an error message from Ghost because the {{hello_world}} helper doesn’t exist in Ghost's default helpers. For our logic to work, we must escape this helper so that it’s not treated as a helper by Ghost’s built-in Handlebars.</p> <p>The correct way is to write this helper as \{{hello_world}}. This way, Ghost treats it as plain text. After refreshing the Ghost homepage, you should see the plain text {{hello_world}}. If this happens, you are on the right track. Let’s now return to the middleware server file, where we will use the response decorator.</p> <p>⚠️ Remember to escape custom helpers in your theme! Don’t forget to add the \ character.<br> </p> <pre class="brush:php;toolbar:false">let htmlContent = proxyResData.toString('utf8');
重新啟動中間件伺服器並註冊上述助手後,您應該在瀏覽器中看到渲染的助手,其中包含我們的助手返回的文字以及當前日期和時間。
在此階段,您可以使用其他自訂幫助程式來擴充 Ghost 主題,並將其新增至中間件伺服器程式碼。
在某些時候,您可能想與助手一起歸還各種東西。預設情況下,該程式庫可防止 XSS 攻擊,但當您使用 SafeString 方法時,此保護將停止運作。盡可能避免使用它。
還有一件事!想像一下,用戶在貼文下的評論部分添加了這樣的助手,並在參數中添加了惡意內容。注意安全。例如,如果您完全渲染每個 HTML,則可能容易受到 XSS 攻擊。建議在特定的封閉區域編譯和渲染 Handlebars.js。您可以使用 Cheerio (https://cheerio.js.org/) 函式庫來解析 HTML 並在必要時渲染 Handlebars。以下是如何透過修改先前的渲染程式碼來保護自己的範例:
const path = require('path'); module.exports = { activate: function activate(ghost) { ghost.helperService.registerDir(path.resolve(__dirname, './lib/helpers')); } };
請記得在腳本開頭加入函式庫初始化:const asyncHelpers = require('handlebars-async-helpers');。如果您因為handlebars-async-helpers 和handlebars 之間的版本衝突而遇到安裝問題,只需將handlebars 降級到^4.7.6。不幸的是,非同步幫助器庫已經有一段時間沒有維護了,但它在實踐中仍然有效。
如果你想在 Ghost 中進行資料庫查詢來獲取,例如當前的帖子,這是可能的,而且並不困難。您可以使用像 knex (https://knexjs.org/) 這樣的函式庫,它是一個清晰且快速的 SQL 查詢產生器。請記住,為此您需要handlebars-async-helpers。正確配置 knex 以連接到 Ghost 的資料庫。
將 knex 初始化為 db 變數並嘗試以下程式碼:
const {SafeString, escapeExpression} = require('../../../../services/handlebars'); module.exports = function uppercase(text) { return `${text.toUpperCase()}`; };
然後,在 Ghost 主題的 post.hbs 範本中,加入下列幫助器:{{post_title uuid="{{uuid}}"}}。在此範例中,{{uuid}} 將被檢索並作為 Ghost 中可用的幫助程式傳遞,填入我們的幫助程式的 uuid 欄位並使自訂幫助程式顯示貼文標題。
您也可以使用 axios 向 Ghost Content API 發出 HTTP 請求,但這比直接資料庫通訊慢得多。
我知道基於中間件的解決方案在速度方面可能不是最好的,但我個人使用這個解決方案並且沒有註意到頁面載入時間顯著下降。單一請求的平均回應時間低於 100 毫秒(根據express-status-monitor),並且我使用自訂助理從每個頁面的資料庫中檢索一些值。
當然,您可以添加快取機制來提高中間件效能或使用替代解決方案來取代express-http-proxy。
使用 Docker 或其他容器化機制。我在我的專案中使用過它,效果很好。為 Ghost、Nginx 和 Node.js 映像新增 Ghost 和資料庫映像。將它們連接到共用網路(驅動程式:bridge),相應地配置 Nginx 和 Node.js 伺服器 - 這一切都非常簡單!
以上是在 Ghost 中製作自訂車把助手!的詳細內容。更多資訊請關注PHP中文網其他相關文章!