伺服器操作已修復
伺服器操作的出現是為了減少客戶端程式碼和簡化需要與伺服器通訊的互動。這是一個優秀的解決方案,可以讓開發人員編寫更少的程式碼。然而,在其他框架中實施它存在一些不容忽視的挑戰。
在本文中,我們將討論這些問題以及如何在 Brisa 中找到解決方案。
為什麼需要伺服器操作?
要了解伺服器操作提供的內容,回顧過去與伺服器的通訊方式很有用。您可能習慣於每次與伺服器互動時執行以下操作:
- 捕捉瀏覽器事件(客戶端)
- 標準化與序列化資料(客戶端)
- 向伺服器發出請求(客戶端)
- 在端點 API 中處理請求 (伺服器)
- 回覆必要的資料(伺服器)
- 等待伺服器回應並處理(客戶端)
- 更新客戶端上的資料並呈現變更(客戶端)
這七個動作每次互動都會重複。例如,如果您的頁面有 10 個不同的交互,您將重複非常相似的代碼 10 次,僅更改請求類型、URL、發送的資料和客戶狀態等詳細資訊。
一個熟悉的例子是
一:
<input onInput={(e) => { // debounce if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { fetch("/api/search", { method: "POST", body: JSON.stringify({ query: e.target.value }), }) .then((res) => res.json()) .then((data) => { setState({ data }); }); }, 300); }} />
在伺服器中:
app.post("/api/search", async (req, res) => { const { query } = req.body; const data = await search(query); res.json(data); });
增加客戶端包大小......以及開發人員的挫折感。
伺服器操作如何運作
伺服器操作將這些操作封裝在遠端過程調用(RPC)中,它管理客戶端-伺服器通信,減少客戶端上的程式碼並將邏輯集中在伺服器上:
- 捕捉瀏覽器事件(RPC 用戶端)
- 標準化與序列化資料(RPC 用戶端)
- 向 RPC 伺服器發出請求 (RPC 用戶端)
- 使用資料在伺服器上執行操作(RPC 伺服器)
- 選項 1:
- 從伺服器渲染並將串流傳送到客戶端(RPC 伺服器)
- 處理流的區塊,以便更改可見(RPC 客戶端)
- 選項 2:
- 回覆必要的資料並將屬性從伺服器儲存傳送到客戶端儲存(RPC 伺服器)
- 使正在監聽更改的訊號對儲存中的變更做出反應(RPC 用戶端)
這裡的一切都是由 Brisa RPC 為您完成的。
這將是來自伺服器元件的程式碼:
<input onInput={(e) => { // debounce if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { fetch("/api/search", { method: "POST", body: JSON.stringify({ query: e.target.value }), }) .then((res) => res.json()) .then((data) => { setState({ data }); }); }, 300); }} />
在這裡,開發人員不編寫客戶端程式碼,因為它是伺服器元件。 onInput 事件在反跳後接收,由客戶端 RPC 處理,而伺服器 RPC 使用「操作訊號」來觸發已向該儲存屬性註冊訊號的 Web 元件。
如您所見,這顯著減少了伺服器程式碼,最重要的是,客戶端上的程式碼大小不會隨著每次互動而增加。 RPC 用戶端程式碼佔用固定的 2 KB,無論您有 10 個還是 1000 個這樣的互動。這意味著客戶端包大小增加 0 位元組,換句話說,不會增加。
此外,在需要重新渲染的情況下,這是在伺服器上完成的,並以HTML 流的形式返回,使用戶比傳統方式更早地看到更改,在傳統方式中,您必須在客戶端上完成此工作伺服器回應。
這樣:
- 改善使用者體驗(使用者體驗)
- 改善開發體驗(DX)
Brisa Server Actions 與其他框架之間的差異
1. 要捕捉的事件數量
在 React 等其他框架中,他們只關注 操作作為 form onSubmit 的一部分,而不是任何事件。
這是一個問題,因為有許多非表單事件也應該從伺服器元件處理,而無需添加客戶端程式碼。例如,輸入的onInput 用於執行自動建議,onScroll 用於載入無限滾動,onMouseOver 進行,onMouseOver
進行,onMouseOver
懸停
3. 關注點分離
當伺服器操作在 React 中引入時,出現了新的範式轉變,許多開發人員在使用它們時必須更改心理晶片。
我們希望使其
盡可能熟悉Web平台
<input onInput={(e) => { // debounce if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { fetch("/api/search", { method: "POST", body: JSON.stringify({ query: e.target.value }), }) .then((res) => res.json()) .then((data) => { setState({ data }); }); }, 300); }} />
都是可互動的。這是一個範例重設表單: 在此範例中,根本沒有客戶端程式碼,在伺服器操作期間,您可以使用CSS
使用指示器停用提交按鈕,以便表單無法提交兩次,並且在伺服器上執行操作後的同一時間,使用e.formData 存取表單資料,然後使用事件的相同API 重設表單。
在精神上,這與使用網路平台非常相似。唯一的區別是所有伺服器元件的所有事件都是伺服器操作。 這樣,就實現了真正的關注點分離,沒有必要將
「用戶伺服器」或「使用客戶端」放入您的元件不再。請記住一切都只在伺服器上運行。唯一的例外是在客戶端上運行的src/web-components資料夾,並且事件正常。
4. 事件傳播
在 Brisa 中,伺服器操作在伺服器元件之間傳播,就好像它們是 DOM 事件一樣。也就是說,從一個Server Action中,你可以呼叫一個Server Component的prop的事件,然後執行父Server Component的Server Action,等等
<input onInput={(e) => { // debounce if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { fetch("/api/search", { method: "POST", body: JSON.stringify({ query: e.target.value }), }) .then((res) => res.json()) .then((data) => { setState({ data }); }); }, 300); }} />
在這種情況下,onAfterMyAction 事件在父元件上執行,並且可以在伺服器上執行操作。這對於在伺服器上執行影響多個伺服器元件的操作非常有用。
4. 兩個世界之間的溝通
特別是在過去的幾周里,在 X(以前的 Twitter)上進行了多次討論之後,Web 元件變得有點不受歡迎。然而,作為HTML的一部分,它是與伺服器操作互動的最佳方式,原因如下:
- 您可以從伺服器擷取任何Web 元件事件並產生客戶端-伺服器通訊。範例
。這非常強大,因為 Web 元件內的所有事件都只是客戶端邏輯,而沒有放置任何伺服器邏輯,只需在使用 Web 元件時從伺服器進行伺服器操作即可。 - HTTP 協定 可用於其設計目的,在串流媒體中傳輸超文本 (HTML),因此如果在重新從伺服器操作渲染WebWeb 元件的任何屬性都會更新,RPC 用戶端的差異演算法使Web 元件無需太多努力即可更新。 Brisa 中的 Web 元件 屬性 是訊號,它們使 Web 元件的內部部分做出反應而無需重新渲染。這個過程在其他框架中變得非常複雜,使得 RPC 伺服器必須透過網路處理 JSON 或 JS,而不是 HTML,這使得串流的實作更加複雜。
在 Web Components 中使用屬性需要序列化,就像不使用 Web Components 將資料從伺服器傳輸到客戶端一樣,因此,使用兩者,無需管理額外的序列化。
注意:如果您有興趣,我會在另一篇文章中解釋流式 HTML 並使用比較演算法對其進行處理。
5.新概念:行動訊號
在 Brisa 中,我們加入了一個新概念,為伺服器操作提供更多功能,這個概念稱為 「操作訊號」。 「行動訊號」的想法是,您有2 個商店,一個位於伺服器,一個位於客戶端。
為什麼是2家店?
預設伺服器儲存 僅在請求等級存在。且您可以共享對客戶端不可見的資料。例如,您可以讓中間件設定使用者並有權存取任何伺服器元件中的敏感使用者資料。透過生活在請求級別,不同請求之間不可能發生衝突,因為每個請求都有其自己的存儲並且不存儲在任何數據庫中,當請求完成,預設消亡。
另一方面,在客戶端商店中,它是一個商店,每個屬性在消費時都是一個信號,也就是說,如果它已更新,正在偵聽該訊號的Web 元件會做出反應。
但是,「操作訊號」的新概念是我們可以將伺服器儲存的生命週期延長到請求之外。為此,需要使用以下程式碼:
<input onInput={(e) => { // debounce if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { fetch("/api/search", { method: "POST", body: JSON.stringify({ query: e.target.value }), }) .then((res) => res.json()) .then((data) => { setState({ data }); }); }, 300); }} />
此transferToClient方法,將伺服器資料共用到客戶端儲存並轉換為訊號。透過這種方式,很多時候不需要從伺服器進行任何重新渲染,您可以簡單地透過伺服器操作對正在偵聽該訊號的 Web 元件的訊號做出反應。
這次商店轉移使伺服器商店的生命現在:
渲染初始伺服器元件→客戶端→伺服器操作→客戶端→伺服器操作...
因此它從僅在請求級別生存到永久生存,兼容頁面之間的導航。
範例:
app.post("/api/search", async (req, res) => { const { query } = req.body; const data = await search(query); res.json(data); });
在此範例中,我們延長了錯誤儲存屬性的壽命,不是在客戶端上使用,而是在伺服器操作中重複使用,然後最終在伺服器操作的重新渲染中重複使用。在這種情況下,作為非敏感數據,沒有必要對其進行加密。此範例程式碼全部發生在伺服器上,甚至重新渲染,用戶將在伺服器上渲染後看到錯誤,其中伺服器RPC 將在流中發送HTML 區塊,客戶端RPC 將對其進行處理以進行比較並顯示向用戶反饋錯誤。
6. 僅加密敏感數據
如果在伺服器操作中使用了渲染級別存在的某個變量,則在安全級別,許多框架(如Next.js 14)所做的就是加密此資料以創建在以下位置使用的資料的快照渲染的時間。這或多或少沒問題,但是始終加密資料會產生相關的計算成本並且它並不總是敏感資料。
在 Brisa 中,為了解決這個問題,有不同的請求,在初始渲染中它有一個值,並且在伺服器操作中您可以捕獲它在此請求中具有的值。
<input debounceInput={300} onInput={async (e) => { // All this code only runs on the server const data = await search(e.target.value); store.set("query", data); store.transferToClient(["query"]); }} />
這在某些情況下很有用,但並不總是有用,例如,如果您執行 Math.random,初始渲染和伺服器操作執行之間肯定會有所不同。
<input onInput={(e) => { // debounce if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { fetch("/api/search", { method: "POST", body: JSON.stringify({ query: e.target.value }), }) .then((res) => res.json()) .then((data) => { setState({ data }); }); }, 300); }} />
這就是為什麼我們創建了「行動訊號」概念,以將資料從伺服器儲存傳送到客戶端儲存 ,並且開發者可以隨意決定是否加密它或不。
有時,您可能希望傳輸初始渲染中已存在的數據,而不是從伺服器操作查詢資料庫,即使它需要關聯的加密。為此,您只需使用:
app.post("/api/search", async (req, res) => { const { query } = req.body; const data = await search(query); res.json(data); });
當你這樣做時:
<input debounceInput={300} onInput={async (e) => { // All this code only runs on the server const data = await search(e.target.value); store.set("query", data); store.transferToClient(["query"]); }} />
Web 元件(客戶端)內部總是會加密,但在伺服器上總是會解密。
注意:Brisa 使用 aes-256-cbc 進行加密,這是 OpenSSL 推薦的用於安全加密資訊的加密演算法組合。加密金鑰是在專案建置過程中產生的。
結論
在 Brisa 中,雖然我們喜歡支援輕鬆編寫 Web 元件,但目標是能夠在沒有客戶端程式碼的情況下製作 SPA,並且僅在純客戶端互動或必須觸及 Web API 時才使用 Web 元件。這就是為什麼伺服器操作如此重要,因為它們允許與伺服器互動而無需編寫客戶端程式碼。
我們鼓勵您嘗試 Brisa,您只需在終端機中執行此命令:bun create brisa,或嘗試一些範例來看看它是如何工作的。
參考
- 伺服器操作約定
- 伺服器操作行為
- 有伺服器操作的表單
- 巢狀操作
- 伺服器端驗證與錯誤處理
- 反跳伺服器操作
- 樂觀更新
- 實際重新渲染
- 使用伺服器操作導航到另一個頁面
- 訪問 Cookie
- 伺服器操作的安全性
- 行動訊號
- 傳輸敏感資料
- 伺服器操作中的道具
- 在反向代理程式中使用伺服器操作
以上是伺服器操作已修復的詳細內容。更多資訊請關注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)

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。
