使用定制的靜態站點生成器簡化您的堆棧

已發表: 2022-03-10
快速總結 ↬在現代開發中,有很多很棒的網站開發工具,但它們往往超出了給定項目所必需的工具。 在本文中,我們將探討如何在沒有框架和客戶端 JavaScript 的 CMS 中製作一個不起眼的 HTML 頁面並使其內容可編輯。

隨著 Jamstack 運動的出現,靜態服務站點再次風靡一時。 大多數提供靜態 HTML 的開發人員並未創作原生 HTML。 為了獲得可靠的開發人員體驗,我們經常求助於稱為靜態站點生成器 (SSG) 的工具。

這些工具具有許多使創作大型靜態網站變得愉快的功能。 無論是像 Gatsby 的數據源那樣提供簡單的第三方 API 掛鉤,還是像 11ty 的大量模板引擎那樣提供深入的配置,靜態站點生成總能滿足每個人的需求。

因為這些工具是為不同的用例而構建的,所以它們必須具有很多功能。 這些功能使它們功能強大。 對於新開發人員來說,它們也使它們變得非常複雜和不透明。 在本文中,我們將介紹 SSG 的基本組件並創建我們自己的組件。

什麼是靜態站點生成器?

靜態站點生成器的核心是一個程序,它對一組文件執行一系列轉換,以將它們轉換為靜態資產,例如 HTML。 它可以接受什麼類型的文件,如何轉換它們,以及輸出什麼類型的文件來區分 SSG。

Jekyll 是一個早期且仍然很流行的 SSG,它使用 Ruby 將 Liquid 模板和 Markdown 內容文件處理成 HTML。

Gatsby 使用 React 和 JSX 將組件和內容轉換為 HTML。 然後它更進一步,創建一個可以靜態服務的單頁應用程序。

11ty 從 Liquid、Handlebars、Nunjucks 或 JavaScript 模板文字等模板引擎渲染 HTML。

這些平台中的每一個都具有使我們的生活更輕鬆的附加功能。 它們提供主題、構建管道、插件架構等。 每增加一個特性都會帶來更多的複雜性、更多的魔力和更多的依賴。 可以肯定,它們是重要的功能,但並非每個項目都需要它們。

在這三個不同的 SSG 之間,我們可以看到另一個共同的主題:數據 + 模板 = 最終站點。 這似乎是生成器靜態站點的核心功能。 這是我們將基於我們的 SSG 的功能。

靜態站點生成器的核心是一個程序,它對一組文件執行一系列轉換,以將它們轉換為靜態資產,例如 HTML。

我們新的靜態站點生成器的技術堆棧:Handlebars、Sanity.io 和 Netlify

要構建我們的 SSG,我們需要一個模板引擎、一個數據源和一個可以運行我們的 SSG 並構建我們的站點的主機。 許多生成器使用 Markdown 作為數據源,但如果我們更進一步,將 SSG 本地連接到 CMS 會怎樣?

  • 數據來源:Sanity.io
  • 數據獲取和模板:節點和把手
  • 主機和部署:Netlify。

先決條件

  • 安裝了 NodeJS
  • Sanity.io 帳戶
  • Git的知識
  • 命令行基礎知識
  • 部署到 Netlify 等服務的基本知識。

注意要繼續學習,您可以在 GitHub 上的此存儲庫中找到代碼。

跳躍後更多! 繼續往下看↓

在 HTML 中設置我們的文檔結構

為了開始我們的文檔結構,我們將編寫純 HTML。 還沒有必要把事情複雜化。

在我們的項目結構中,我們需要為源文件創建一個存放位置。 在這種情況下,我們將創建一個src目錄並將我們的index.html放入其中。

index.html中,我們將概述我們想要的內容。 這將是一個相對簡單的關於頁面。

 <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Title of the page!</title> </head> <body> <h1>The personal homepage of Bryan Robinson</h1> <p>Some pagraph and rich text content next</p> <h2>Bryan is on the internet</h2> <ul> <li><a href="linkURL">List of links</a></li> </ul> </body> </html>

讓我們保持簡單。 我們將從頁面的h1開始。 我們將在此之後添加幾段傳記信息,並使用鏈接列表錨定頁面以查看更多信息。

將我們的 HTML 轉換為接受數據的模板

在我們有了基本結構之後,我們需要建立一個流程來將其與一些數據結合起來。 為此,我們將使用 Handlebars 模板引擎。

Handlebars 的核心是一個類似 HTML 的字符串,通過文檔中定義的規則插入數據,然後輸出一個編譯的 HTML 字符串。

要使用 Handlebars,我們需要初始化一個 package.json 並安裝該包。

運行npm init -y以創建帶有一些默認內容的 package.json 文件的結構。 一旦我們有了這個,我們就可以安裝 Handlebars。

 npm install handlebars

我們的構建腳本將是一個 Node 腳本。 這是我們將在本地用於構建的腳本,也是我們的部署供應商和主機將用於為實時站點構建 HTML 的腳本。

要啟動我們的腳本,我們將創建一個index.js文件並在頂部需要兩個包。 第一個是 Handlebars,第二個是 Node 中用於訪問當前文件系統的默認模塊。

 const fs = require('fs'); const Handlebars = require('handlebars');

我們將使用fs模塊來訪問我們的源文件,以及寫入分發文件。 為了開始我們的構建,我們將為我們的文件創建一個在調用時運行的main函數,以及一個用於組合我們的數據和標記的buildHTML函數。

 function buildHTML(filename, data) { const source = fs.readFileSync(filename,'utf8').toString(); const template = Handlebars.compile(source); const output = template(data); return output } async function main(src, dist) { const html = buildHTML(src, { "variableData": "This is variable data"}); fs.writeFile(destination, html, function (err) { if (err) return console.log(err); console.log('index.html created'); }); } main('./src/index.html', './dist/index.html');

main()函數接受兩個參數:HTML 模板的路徑和我們希望構建的文件存在的路徑。 在我們的主函數中,我們使用一些數據在模板源路徑上運行buildHTML

build 函數將源文檔轉換為字符串並將該字符串傳遞給 Handlebars。 Handlebars 使用該字符串編譯模板。 然後我們將數據傳遞到編譯後的模板中,Handlebars 呈現一個新的 HTML 字符串,用數據輸出替換任何變量或模板邏輯。

我們將該字符串返回到我們的main函數中,並使用 Node 的文件系統模塊提供的writeFile方法將新文件寫入我們指定的位置(如果目錄存在)。

為防止出現錯誤,請將dist目錄添加到您的項目中,其中包含.gitkeep文件。 我們不想提交我們構建的文件(我們的構建過程會這樣做),但我們要確保我們的腳本有這個目錄。

在我們創建 CMS 來管理此頁面之前,讓我們確認它是否正常工作。 為了測試,我們將修改我們的 HTML 文檔以使用我們剛剛傳遞給它的數據。 我們將使用 Handlebars 變量語法來包含variableData內容。

 <h1>{{ variableData }}</h1>

既然我們的 HTML 有一個變量,我們就可以運行我們的節點腳本了。

 node index.js

腳本完成後,我們應該在/dist/index.html有一個文件。 如果我們在瀏覽器中閱讀 open this,我們將看到我們的標記呈現,還有我們的“這是可變數據”字符串。

連接到 CMS

我們有一種將數據與模板放在一起的方法,現在我們需要一個數據源。 此方法適用於任何具有 API 的數據源。 對於這個演示,我們將使用 Sanity.io。

Sanity 是一種 API 優先的數據源,將內容視為結構化數據。 他們有一個開源內容管理系統,可以讓編輯和開發人員更方便地管理和添加數據。 CMS 就是通常所說的“無頭”CMS。 與您的數據與演示文稿緊密耦合的傳統管理系統不同,無頭 CMS 創建了一個數據層,該數據層可以被任何前端或服務(可能同時有很多)使用。

Sanity 是一項付費服務,但他們有一個“標準”計劃,該計劃是免費的,並且具有我們對此類網站所需的所有功能。

建立理智

啟動和運行新的 Sanity 項目的最快方法是使用 Sanity CLI。 我們將從全局安裝開始。

 npm install -g @sanity/cli

CLI 讓我們可以訪問一組用於管理、部署和創建的助手。 為了讓事情開始,我們將運行sanity init 。 這將使我們完成一份問卷調查,以幫助引導我們的工作室(Sanity 稱之為他們的開源 CMS)。

 Select a Project to Use: Create new project HTML CMS Use the default dataset configuration? Y // this creates a "Production" dataset Project output path: studio // or whatever directory you'd like this to live in Select project template Clean project with no predefined schemas

此步驟將在您的 Sanity 帳戶中創建一個新項目和數據集,創建一個本地版本的 Studio,並為您將數據和 CMS 綁定在一起。 默認情況下, studio目錄將創建在我們項目的根目錄中。 在大型項目中,您可能希望將其設置為單獨的存儲庫。 對於這個項目,將其捆綁在一起很好。

為了在本地運行我們的 Studio,我們將目錄更改為studio目錄並運行sanity start 。 這將在localhost:3333運行 Studio。 當您登錄時,您會看到一個屏幕,讓您知道您有“空架構”。 有了這個,是時候添加我們的模式了,這就是我們的數據的結構和編輯方式。

創建健全模式

在 Sanity Studio 中創建文檔和字段的方式是在schemas/schema.js文件中創建模式。

對於我們的網站,我們將創建一個名為“About Details”的模式類型。 我們的模式將來自我們的 HTML。 一般來說,我們可以將大部分網頁設置為單個富文本字段,但以解耦方式構建我們的內容是最佳實踐。 這為我們將來如何使用這些數據提供了更大的靈活性。

對於我們的網頁,我們需要一組數據,包括以下內容:

  • 標題
  • 全名
  • 傳記(帶有富文本編輯)
  • 具有名稱和 URL 的網站列表。

為了在我們的模式中定義它,我們為我們的文檔創建一個對象並定義它的字段。 我們的內容的註釋列表及其字段type

  • 標題 - 字符串
  • 全名——字符串
  • 傳記——“塊”數組
  • 網站列表 — 具有名稱和 URL 字符串字段的對像數組。
 types: schemaTypes.concat([ /* Your types here! */ { title: "About Details", name: "about", type: "document", fields: [ { name: 'title', type: 'string' }, { name: 'fullName', title: 'Full Name', type: 'string' }, { name: 'bio', title: 'Biography', name: 'content', type: 'array', of: [ { type: 'block' } ] }, { name: 'externalLinks', title: 'Social media and external links', type: 'array', of: [ { type: 'object', fields: [ { name: 'text', title: 'Link text', type: 'string' }, { name: 'href', title: 'Link url', type: 'string' } ] } ] } ] } ])

將此添加到您的架構類型,保存,您的 Studio 將重新編譯並向您展示您的第一個文檔。 從這裡開始,我們將通過創建一個新文檔並填寫信息將我們的內容添加到 CMS 中。

以可重用的方式構建您的內容

此時,您可能想知道為什麼我們有“全名”和“頭銜”。 這是因為我們希望我們的內容具有多用途的潛力。 通過包含名稱字段而不是僅在標題中包含名稱,我們可以更多地使用該數據。 然後,我們可以使用此 CMS 中的信息來支持簡歷頁面或 PDF。 傳記字段可以以編程方式用於其他系統或網站。 這使我們能夠對大部分內容擁有單一的事實來源,而不是受此特定站點的直接用例所支配。

將我們的數據提取到我們的項目中

現在我們已經通過 API 提供了我們的數據,讓我們將其拉入我們的項目中。

安裝和配置 Sanity JavaScript 客戶端

首先,我們需要訪問 Node.js 中的數據。 我們可以使用 Sanity JavaScript 客戶端來偽造該連接。

 npm install @sanity/client

這將獲取並安裝 JavaScript SDK。 從這裡開始,我們需要將其配置為從我們之前設置的項目中獲取數據。 為此,我們將在/utils/SanityClient.js中設置一個實用程序腳本。 我們為 SDK 提供了我們的項目 ID 和數據集名稱,並準備在我們的主腳本中使用它。

 const sanityClient = require('@sanity/client'); const client = sanityClient({ projectId: '4fs6x5jg', dataset: 'production', useCdn: true }) module.exports = client;

使用 GROQ 獲取我們的數​​據

回到我們的index.js文件,我們將創建一個新函數來獲取我們的數​​據。 為此,我們將使用 Sanity 的原生查詢語言,即開源 GROQ。

我們將在變量中構建查詢,然後使用我們配置的客戶端根據查詢獲取數據。 在這種情況下,我們構建了一個具有名為about的屬性的對象。 在此對像中,我們希望返回特定文檔的數據。 為此,我們根據創建文檔時自動生成的文檔_id進行查詢。

為了找到文檔的_id ,我們導航到 Studio 中的文檔,然後從 URL 複製它或進入“檢查”模式以查看文檔上的所有數據。 要進入 Inspect,請單擊右上角的“kabob”菜單或使用快捷鍵Ctrl + Alt + I 。 該視圖將列出該文檔上的所有數據,包括我們的_id 。 Sanity 將返回一個文檔對像數組,因此為簡單起見,我們將返回第0th個條目。

然後我們將查詢傳遞給 Sanity 客戶端的fetch方法,它將返回我們文檔中所有數據的 JSON 對象。 在這個演示中,返回所有數據並不是什麼大問題。 對於更大的實現,GROQ 允許一個可選的“投影”只返回你想要的顯式字段。

 const client = require('./utils/SanityClient') // at the top of the file // ... async function getSanityData() { const query = `{ "about": *[_id == 'YOUR-ID-HERE'][0] }` let data = await client.fetch(query); }

將富文本字段轉換為 HTML

在返回數據之前,我們需要對富文本字段進行轉換。 雖然許多 CMS 使用直接返回 HTML 的富文本編輯器,但 Sanity 使用稱為 Portable Text 的開源規範。 Portable Text 返回一個對像數組(將富文本視為段落列表和其他媒體塊),其中包含有關富文本樣式和屬性(如鍊接、腳註和其他註釋)的所有數據。 這允許您在不支持 HTML 的系統中移動和使用您的文本,例如語音助手和本機應用程序。

對於我們的用例,這意味著我們需要將對象轉換為 HTML。 有 NPM 模塊可用於將可移植文本轉換為各種用途。 在我們的例子中,我們將使用一個名為 block-content-to-html 的包。

 npm install @sanity/block-content-to-html

此包將呈現富文本編輯器中的所有默認標記。 可以覆蓋每種類型的樣式以符合您的用例所需的任何標記。 在這種情況下,我們將讓包為我們完成工作。

 const blocksToHtml = require('@sanity/block-content-to-html'); // Added to the top async function getSanityData() { const query = `{ "about": *[_type == 'about'][0] }` let data = await client.fetch(query); data.about.content = blocksToHtml({ blocks: data.about.content }) return await data }

在 Handlebars 中使用 Sanity.io 的內容

現在數據已經形成了我們可以使用的形狀,我們將把它作為數據參數傳遞給我們的buildHTML函數。

 async function main(src, dist) { const data = await getSanityData(); const html = buildHTML(src, data) fs.writeFile(dist, html, function (err) { if (err) return console.log(err); console.log('index.html created'); }); }

現在,我們可以更改 HTML 以使用新數據。 我們將在模板中使用更多變量調用來提取大部分數據。

為了呈現我們的富文本content變量,我們需要在我們的變量中添加一個額外的大括號層。 這將告訴 Handlebars 呈現 HTML 而不是將 HTML 顯示為字符串。

對於我們的externalLinks數組,我們需要使用 Handlebars 的內置循環功能來顯示我們添加到 Studio 的所有鏈接。

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{ about.title }}</title> </head> <body> <h1>The personal homepage of {{ about.fullName }}</h1> {{{ about.content }}} <h2>Bryan is on the internet</h2> <ul> {{#each about.externalLinks }} <li><a href="{{ this.href }}">{{ this.text }}</a></li> {{/each}} </ul> </body> </html>

設置部署

讓我們直播吧。 我們需要兩個組件來完成這項工作。 首先,我們需要一個靜態主機來為我們構建文件。 接下來,當我們的 CMS 中的內容髮生更改時,我們需要觸發我們網站的新構建。

部署到 Netlify

對於託管,我們將使用 Netlify。 Netlify 是一個靜態站點主機。 它提供靜態資產,但具有使我們的網站順利運行的附加功能。 他們有一個內置的部署基礎設施,可以運行我們的節點腳本、用於觸發構建的 webhook,以及一個全球分佈的 CDN,以確保我們的 HTML 頁面得到快速服務。

Netlify 可以在 GitHub 上查看我們的存儲庫,並根據我們可以添加到其儀表板中的命令創建構建。

首先,我們需要將此代碼推送到 GitHub。 然後,在 Netlify 的 Dashboard 中,我們需要將新存儲庫連接到 Netlify 中的新站點。

一旦連接好,我們需要告訴 Netlify 如何構建我們的項目。 在儀表板中,我們將前往 Settings > Build & Deploy > Build Settings。 在這方面,我們需要將“構建命令”更改為“node index.js”,將“發布目錄”更改為“./dist”。

當 Netlify 構建我們的站點時,它將運行我們的命令,然後檢查我們列出的文件夾中的內容並在其中發佈內容。

設置 Webhook

當有人更新內容時,我們還需要告訴 Netlify 發布新版本。 為此,我們將設置一個 Webhook 來通知 Netlify 我們需要重建站點。 Webhook 是一個 URL,可以由不同的服務(例如 Sanity)以編程方式訪問以在源服務(在本例中為 Netlify)中創建操作。

我們可以在 Netlify 儀表板的 Settings > Build & Deploy > Build hooks 中設置一個特定的“Build hook”。 添加一個鉤子,給它一個名字並保存。 這將提供一個 URL,可用於遠程觸發 Netlify 中的構建。

接下來,我們需要告訴 Sanity 在您發布更改時向此 URL 發出請求。

我們可以使用 Sanity CLI 來完成此操作。 在我們的/studio目錄中,我們可以運行sanity hook create來連接。 該命令將要求輸入名稱、數據集和 URL。 名稱可以是您喜歡的任何名稱,數據集應該是我們產品的production ,URL 應該是 Netlify 提供的 URL。

現在,每當我們在 Studio 中發佈內容時,我們的網站都會自動更新。 不需要框架。

  • 代碼可以在這個 GitHub 存儲庫中找到 →

下一步

這是您創建自己的工具時可以執行的操作的一個非常小的示例。 雖然大多數項目可能需要功能更全面的 SSG,但創建自己的迷你 SSG 可以幫助您更多地了解您選擇的生成器中發生的情況。

  • 這個站點只發布一個頁面,但是在我們的構建腳本中添加一些額外的內容,我們可以讓它發布更多頁面。 它甚至可以發布博客文章。
  • 存儲庫中有點缺乏“開發人員體驗”。 我們可以通過實現像 Nodemon 這樣的包或使用 BrowserSync 之類的東西添加“熱重載”來在任何文件保存上運行我們的 Node 腳本。
  • 存在於 Sanity 中的數據可以為多個站點和服務提供支持。 您可以創建一個使用它並發布 PDF 而不是網頁的簡歷站點。
  • 您可以添加 CSS 並使其看起來像一個真實的站點。