靜態第一:帶有無服務器渲染作為後備的預先生成的jamstack網站
JAMstack 架構正日益受到關注,它提供了一種高效的網站構建方法。
JAMstack 的核心原則之一是預渲染。這意味著提前將網站生成一系列靜態資源,以便能夠以最快的速度、最低的開銷從CDN 或其他優化的靜態託管環境中為訪問者提供服務。
但是,如果我們要提前預生成網站,如何讓它們看起來更動態呢?如何構建需要頻繁更改的網站?如何處理用戶生成的內容?
實際上,這正是無服務器函數的理想應用場景。 JAMstack 和無服務器是最佳拍檔,它們完美地互補。
本文將探討一種模式:在幾乎完全由用戶生成內容組成的網站中,使用無服務器函數作為預生成頁面的後備方案。我們將採用一種樂觀URL 路由技術,其中404 頁面是一個無服務器函數,以便動態添加無服務器渲染。
聽起來很複雜?也許吧。但它有效嗎?絕對有效!
您可以試用演示網站來了解這個用例。但是,請您看完本文後再試用。
您回來了?太好了,讓我們深入探討。
這個示例網站的理念是:讓您創建一條溫馨的留言和虛擬鼓勵信息發送給朋友。您可以撰寫留言,自定義棒棒糖(或冰棒,為我的美國朋友們準備的)並獲得一個URL 與您的收件人分享。就這樣,您就照亮了他們的一天。還有什麼比這更好的呢?
傳統上,我們會使用一些服務器端腳本處理表單提交,將新的棒棒糖(我們的用戶生成內容)添加到數據庫中並生成唯一的URL。然後,我們會使用更多服務器端邏輯來解析對這些頁面的請求,查詢數據庫以獲取填充頁面視圖所需的數據,使用合適的模板進行渲染,並將其返回給用戶。
這看起來合情合理。
但擴展的成本是多少?
技術架構師和技術主管在評估項目範圍時經常會遇到這個問題。他們需要規劃、支付和配置足夠的資源以應對成功的情況。
這個虛擬棒棒糖網站並非普通的裝飾品。由於我們都想互相發送積極的信息,這個網站將讓我成為億萬富翁!隨著消息的傳播,流量水平將會飆升。我最好有一個好的策略來確保服務器能夠處理繁重的負載。我可能會添加一些緩存層、一些負載均衡器,並且我會設計我的數據庫和數據庫服務器,以便能夠分擔負載,而不會因為創建和提供所有這些棒棒糖的需求而不堪重負。
但是……我不知道如何做這些事情。
而且我不知道添加這些基礎設施並保持其正常運行的成本是多少。這很複雜。
這就是我喜歡通過預渲染盡可能簡化我的託管的原因。
提供靜態頁面比從需要執行一些邏輯以按需為每個訪問者生成視圖的Web 服務器動態提供頁面要簡單得多且成本更低。
由於我們正在處理大量用戶生成的內容,使用數據庫仍然是有意義的,但我不會自己管理它。相反,我將選擇許多可用作服務的數據庫選項之一。我將通過其API 與它進行交互。
我可能會選擇Firebase、MongoDB 或其他任何數量的數據庫。 Chris 在一個關於無服務器資源的優秀網站上編譯了一些這樣的資源,非常值得探索。
在本例中,我選擇Fauna 作為我的數據存儲。 Fauna 提供了一個很好的API 用於存儲和查詢數據。它是一個非SQL 風格的數據存儲,它正是我所需要的。
至關重要的是,Fauna 已將提供數據庫服務作為一項完整的業務。他們擁有我永遠不會擁有的深厚領域知識。通過使用像這樣的數據庫即服務提供商,我為我的項目繼承了一個專家數據服務團隊,包括高可用性基礎設施、容量和合規性安心、熟練的支持工程師和豐富的文檔。
與其自己動手,不如使用這樣的第三方服務,這就是它的優勢所在。
架構TL;DR
在處理概念驗證時,我經常發現自己會塗鴉一些邏輯流程。這是我為這個網站做的塗鴉:
以及一些解釋:
- 用戶通過填寫普通的HTML 表單創建一個新的棒棒糖。
- 新內容保存在數據庫中,並且它的提交會觸發新的站點生成和部署。
- 站點部署完成後,新的棒棒糖將可通過唯一的URL 訪問。它將是一個靜態頁面,從CDN 快速提供服務,不依賴於數據庫查詢或服務器。
- 在站點生成完成之前,任何新的棒棒糖都將無法作為靜態頁面訪問。對棒棒糖頁面的不成功請求將回退到一個頁面,該頁面通過動態查詢數據庫API 來動態生成棒棒糖頁面。
這種方法首先假設靜態/預生成的資源,然後在靜態視圖不可用時才回退到動態渲染,正如聯合利華的Markus Schork 所描述的那樣,這被稱為“靜態優先”,我很喜歡這個說法。
更詳細的說明
您可以直接深入研究該網站的代碼(它是開源的,您可以隨意探索),或者我們可以進一步討論。
您想更深入地挖掘並探索此示例的實現?好的,我將更詳細地解釋:
- 從數據庫獲取數據以生成每個頁面
- 使用無服務器函數將數據發佈到數據庫API
- 觸發完整的站點重新生成
- 在尚未生成頁面時按需渲染
從數據庫生成頁面
稍後,我們將討論如何將數據發佈到數據庫,但首先,讓我們假設數據庫中已經有一些條目。我們將要生成一個包含每個條目的頁面的網站。
靜態網站生成器非常擅長這一點。它們會處理數據,將其應用於模板,並輸出準備提供服務的HTML 文件。我們可以為此示例使用任何生成器。我選擇Eleventy 是因為它相對簡單且站點生成速度快。
為了向Eleventy 提供一些數據,我們有很多選擇。一種方法是提供一些返回結構化數據的JavaScript。這非常適合查詢數據庫API。
我們的Eleventy 數據文件將如下所示:
<code>// 设置与Fauna 数据库的连接。 // 使用环境变量进行身份验证// 并访问数据库。 const faunadb = require('faunadb'); const q = faunadb.query; const client = new faunadb.Client({ secret: process.env.FAUNADB_SERVER_SECRET }); module.exports = () => { return new Promise((resolve, reject) => { // 获取最新的100,000 个条目(为了我们的示例) client.query( q.Paginate(q.Match(q.Ref("indexes/all_lollies")),{size:100000}) ).then((response) => { // 获取每个条目的所有数据const lollies = response.data; const getAllDataQuery = lollies.map((ref) => { return q.Get(ref); }); return client.query(getAllDataQuery).then((ret) => { // 将数据发送回Eleventy 以用于站点构建resolve(ret); }); }).catch((error) => { console.log("error", error); reject(error); }); }) }</code>
我將此文件命名為lollies.js,它將使其返回的所有數據在名為lollies 的集合中可用於Eleventy。
我們現在可以在我們的模板中使用該數據。如果您想查看獲取該數據並為每個項目生成頁面的代碼,您可以在代碼存儲庫中查看。
在沒有服務器的情況下提交和存儲數據
當我們創建一個新的棒棒糖頁面時,我們需要將用戶內容捕獲到數據庫中,以便將來可以將其用於填充給定URL 的頁面。為此,我們使用傳統的HTML 表單將數據發佈到合適的表單處理程序。
表單看起來像這樣(或在存儲庫中查看完整代碼):
<code></code>
在我們的託管方案中沒有Web 服務器,因此我們需要設計一個地方來處理從此表單提交的HTTP 發布請求。這是無服務器函數的完美用例。我正在為此使用Netlify Functions。如果您願意,可以使用AWS Lambda、Google Cloud 或Azure Functions,但我喜歡Netlify Functions 工作流程的簡單性,以及它將我的無服務器API 和我的UI 保留在同一個代碼存儲庫中的事實。
避免將後端實現細節洩露到前端是一個好習慣。清晰的分離有助於使事情更易於移植和整潔。查看上面表單元素的action 屬性。它將數據發佈到我網站上名為/new 的路徑,這並沒有真正暗示它將與哪個服務進行通信。
我們可以使用重定向將它路由到我們喜歡的任何服務。我將它發送到我將在此項目中配置的無服務器函數,但如果我們願意,可以輕鬆地將其自定義為將數據發送到其他地方。 Netlify 為我們提供了一個簡單且高度優化的重定向引擎,該引擎在CDN 級別引導我們的流量,因此用戶可以非常快速地路由到正確的位置。
下面的重定向規則(位於我的項目的netlify.toml 文件中)將/new 的請求代理到由Netlify Functions 託管的名為newLolly.js 的無服務器函數。
<code># 将“new”URL 解析为函数[[redirects]] from = "/new" to = "/.netlify/functions/newLolly" status = 200</code>
讓我們看看那個無服務器函數:
- 將新數據存儲到數據庫中,
- 為新頁面創建一個新的URL,並
- 將用戶重定向到新創建的頁面,以便他們可以看到結果。
首先,我們將需要各種實用程序來解析表單數據、連接到Fauna 數據庫並為新的棒棒糖創建易於閱讀的短唯一ID。
<code>const faunadb = require('faunadb'); // 用于访问FaunaDB const shortid = require('shortid'); // 生成短唯一URL const querystring = require('querystring'); // 帮助我们解析表单数据// 首先,我们使用我们的数据库设置一个新的连接。 // 环境变量帮助我们安全地连接// 到正确的数据库。 const q = faunadb.query const client = new faunadb.Client({ secret: process.env.FAUNADB_SERVER_SECRET })</code>
現在,我們將向處理對無服務器函數的請求添加一些代碼。處理程序函數將解析請求以從表單提交中獲取所需的數據,然後為新的棒棒糖生成一個唯一的ID,然後將其作為新記錄創建到數據庫中。
<code>// 处理对无服务器函数的请求exports.handler = (event, context, callback) => { // 获取表单数据const data = querystring.parse(event.body); // 添加一个唯一的路径ID。并记下它- 我们稍后会将用户发送到它const uniquePath = shortid.generate(); data.lollyPath = uniquePath; // 组装准备发送到数据库的数据const lolly = { data: data }; // 在fauna db 中创建棒棒糖条目client.query(q.Create(q.Ref('classes/lollies'), lolly)) .then((response) => { // 成功!将用户重定向到此新棒棒糖页面的唯一URL return callback(null, { statusCode: 302, headers: { Location: `/lolly/${uniquePath}`, } }); }).catch((error) => { console.log('error', error); // 错误!返回带有statusCode 400 的错误return callback(null, { statusCode: 400, body: JSON.stringify(error) }); }); }</code>
讓我們檢查一下我們的進度。我們有辦法在數據庫中創建新的棒棒糖頁面。我們還有一個自動構建,它會為我們的每個棒棒糖生成一個頁面。
為了確保為每個棒棒糖都有一套完整的預生成頁面,我們應該在每次成功將新條目添加到數據庫時觸發重建。這非常容易做到。由於我們的靜態網站生成器,我們的構建已經自動化了。我們只需要一種觸發它的方法。使用Netlify,我們可以定義任意數量的構建鉤子。它們是Webhook,如果它們收到HTTP POST 請求,它們將重建和部署我們的站點。這是我在Netlify 的站點管理控制台中創建的一個:
為了重新生成站點,包括數據庫中記錄的每個棒棒糖的頁面,我們可以在將新數據保存到數據庫後立即向此構建鉤子發出HTTP POST 請求。
這是執行此操作的代碼:
<code>const axios = require('axios'); // 简化发出HTTP POST 请求// 触发新的构建以永久冻结此棒棒糖axios.post('https://api.netlify.com/build_hooks/5d46fa20da4a1b70XXXXXXXXX') .then(function (response) { // 在无服务器函数的日志中报告console.log(response); }) .catch(function (error) { // 描述无服务器函数日志中的任何错误console.log(error); });</code>
您可以在完整的代碼中看到它,它已添加到數據庫插入的成功處理程序中。
如果我們在與收件人分享新棒棒糖的URL 之前,願意等待構建和部署完成,那麼這一切都很好。但是我們並不耐心,當我們獲得剛剛創建的棒棒糖的新URL 時,我們會立即想要分享它。
遺憾的是,如果我們在站點完成重新生成以包含新頁面之前訪問該URL,我們將得到404。但令人高興的是,我們可以利用這個404。
樂觀URL 路由和無服務器後備方案
使用自定義404 路由,我們可以選擇將每個對棒棒糖頁面的失敗請求發送到一個頁面,該頁面可以直接在數據庫中查找棒棒糖數據。如果我們願意,我們可以在客戶端JavaScript 中執行此操作,但更好的方法是從無服務器函數動態生成一個準備查看的頁面。
方法如下:
首先,我們需要告訴所有希望訪問棒棒糖頁面的請求(這些請求返回為空)改為轉到我們的無服務器函數。我們通過在Netlify 重定向配置中添加另一個規則來實現:
<code># 未找到的棒棒糖应该直接代理到API [[redirects]] from = "/lolly/*" to = "/.netlify/functions/showLolly?id=:splat" status = 302</code>
只有在對棒棒糖頁面的請求沒有找到準備提供服務的靜態頁面時,才會應用此規則。它會創建一個臨時重定向(HTTP 302) 到我們的無服務器函數,該函數看起來像這樣:
<code>const faunadb = require('faunadb'); // 用于访问FaunaDB const pageTemplate = require('./lollyTemplate.js'); // JS 模板文字// 设置和授权Fauna DB 客户端const q = faunadb.query; const client = new faunadb.Client({ secret: process.env.FAUNADB_SERVER_SECRET }); exports.handler = (event, context, callback) => { // 从请求中获取棒棒糖ID const path = event.queryStringParameters.id.replace("/", ""); // 在DB 中查找棒棒糖数据client.query( q.Get(q.Match(q.Index("lolly_by_path"), path)) ).then((response) => { // 如果找到,则返回视图return callback(null, { statusCode: 200, body: pageTemplate(response.data) }); }).catch((error) => { // 未找到或发生错误,将悲伤的用户发送到通用错误页面console.log('Error:', error); return callback(null, { body: JSON.stringify(error), statusCode: 301, headers: { Location: `/melted/index.html`, } }); }); }</code>
如果對任何其他頁面(不在站點的/lolly/ 路徑內)的請求返回404,我們不會將該請求發送到我們的無服務器函數以檢查棒棒糖。我們可以直接將用戶發送到404 頁面。我們的netlify.toml 配置允許我們通過在文件中添加更多回退規則來定義任意數量的404 路由級別。文件中第一個成功的匹配將被採用。
<code># 未找到的棒棒糖应该直接代理到API [[redirects]] from = "/lolly/*" to = "/.netlify/functions/showLolly?id=:splat" status = 302 # 真正的404 可以直接转到这里: [[redirects]] from = "/*" to = "/melted/index.html" status = 404</code>
我們完成了!我們現在擁有一個“靜態優先”的站點,如果尚未將URL 生成靜態文件,它將嘗試使用無服務器函數動態渲染內容。
非常快速!
支持更大規模
每次創建新條目時都觸發構建以重新生成棒棒糖頁面的技術可能並非永遠都是最佳的。雖然構建的自動化意味著重新部署站點非常簡單,但當我們開始變得非常受歡迎時,我們可能希望開始限制和優化事情。 (這只是時間問題,對吧?)
沒關係。當我們要創建非常多的頁面並且數據庫中更頻繁地添加內容時,以下是一些需要考慮的事情:
- 我們可以不為每個新條目觸發重建,而是可以將站點重建為計劃作業。也許這可以每小時或每天發生一次。
- 如果每天構建一次,我們可能決定只為過去一天提交的新棒棒糖生成頁面,並將每天生成的頁面緩存以供將來使用。構建中的這種邏輯將幫助我們支持大量棒棒糖頁面,而不會使構建時間過長。但我不會在這裡討論構建內緩存。如果您好奇,您可以在Netlify 社區論壇中詢問。
通過將靜態預生成資源與提供動態渲染的無服務器後備方案相結合,我們可以滿足令人驚訝的廣泛用例——同時避免需要配置和維護大量動態基礎設施。
您還可以使用這種“靜態優先”方法滿足哪些其他用例?
以上是靜態第一:帶有無服務器渲染作為後備的預先生成的jamstack網站的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

您是否曾經在項目上需要一個倒計時計時器?對於這樣的東西,可以自然訪問插件,但實際上更多

在元素個數不固定的情況下如何通過CSS選擇第一個指定類名的子元素在處理HTML結構時,常常會遇到元素個數不�...

關於Flex佈局中紫色斜線區域的疑問在使用Flex佈局時,你可能會遇到一些令人困惑的現象,比如在開發者工具(d...

格子呢是一塊圖案布,通常與蘇格蘭有關,尤其是他們時尚的蘇格蘭語。在Tartanify.com上,我們收集了5,000多個格子呢
