在建置網路應用程式時,管理網路請求可能是一項艱鉅的任務。確保數據最新和處理同時請求的挑戰,常常導致應用程式中出現複雜的邏輯來處理中斷和競爭條件。Remix 透過自動化網路管理,鏡像並擴展網頁瀏覽器的直覺行為,簡化了這個流程。
為了幫助理解 Remix 的運作方式,請回想一下完整堆疊資料流,在 form
提交之後,Remix 將從載入器中提取新的資料。這稱為重新驗證。
Remix 對網路並發的處理方式,很大程度上受到網頁瀏覽器處理文件時的預設行為的啟發。
瀏覽器連結導覽:當您在瀏覽器中點擊一個連結,然後在頁面轉換完成之前點擊另一個連結時,瀏覽器會優先處理最新的 action
。它會取消初始請求,僅專注於點擊的最新連結。
瀏覽器表單提交:如果您在瀏覽器中啟動表單提交,然後迅速再次提交另一個表單,瀏覽器會忽略第一個提交,僅處理最新的提交。
雖然標準瀏覽器在導覽和表單提交時,一次只能處理一個請求,但 Remix 提升了這種行為。與導覽不同,使用 useFetcher
,可以同時處理多個請求。
Remix 的設計旨在有效地處理對伺服器 action
的多個表單提交和並行的重新驗證請求。它確保一旦有新數據可用,狀態就會立即更新。但是,當其他 action
引入競爭條件時,Remix 也會避免提交過時的數據,從而防範潛在的陷阱。
例如,如果有三個表單提交正在進行中,其中一個完成,Remix 會立即使用該數據更新 UI,而無需等待其他兩個表單提交,以確保 UI 保持響應性和動態。隨著其餘提交完成,Remix 會繼續更新 UI,確保顯示最新的數據。
為了幫助理解一些視覺化效果,以下是圖表中使用的符號的說明
|
: 提交開始submission 1: |----✓-----✅
submission 2: |-----✓-----✅
submission 3: |-----✓-----✅
但是,如果後續提交的重新驗證比較早的提交完成得更快,Remix 會捨棄較早的資料,確保 UI 中僅反映最新的資訊。
submission 1: |----✓---------❌
submission 2: |-----✓-----✅
submission 3: |-----✓-----✅
由於來自提交 (2) 的重新驗證開始較晚,但比提交 (1) 更早完成,因此提交 (1) 的請求被取消,並且只有來自提交 (2) 的資料被提交至 UI。它的請求時間較晚,因此更有可能包含來自 (1) 和 (2) 的更新值。
您的使用者不太可能遇到這種情況,但在極少數情況下,當基礎設施不一致時,使用者仍有可能看到過時的資料。即使 Remix 會取消對過時資料的請求,這些請求最終仍然會傳送到伺服器。在瀏覽器中取消請求只會釋放該請求的瀏覽器資源,它無法「趕上」並阻止請求傳送到伺服器。在極其罕見的情況下,一個已取消的請求可能會在導致中斷的 action
重新驗證完成後更改資料。請考慮以下圖表
👇 interruption with new submission
|----❌----------------------✓
|-------✓-----✅
👆
initial request reaches the server
after the interrupting submission
has completed revalidation
使用者現在看到的資料與伺服器上的資料不同。請注意,這個問題既極為罕見,也存在於預設的瀏覽器行為中。在任何網路和伺服器基礎架構上,初始請求比提交和第二次重新驗證更晚到達伺服器的機率是意料之外的。如果您擔心您的基礎設施出現這種情況,您可以將時間戳記與表單提交一起發送,並編寫伺服器邏輯來忽略過時的提交。
在像下拉式選單這樣的 UI 元件中,每次按鍵都可能觸發網路請求。管理如此快速連續的請求可能很棘手,尤其是在確保顯示的結果與最新的查詢匹配時。然而,使用 Remix,這個挑戰會自動處理,確保使用者看到正確的結果,而開發人員無需微觀管理網路。
import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno
export async function loader({
request,
}: LoaderFunctionArgs) {
const { searchParams } = new URL(request.url);
const cities = await searchCities(searchParams.get("q"));
return json(cities);
}
export function CitySearchCombobox() {
const fetcher = useFetcher<typeof loader>();
return (
<fetcher.Form action="/city-search">
<Combobox aria-label="Cities">
<ComboboxInput
name="q"
onChange={(event) =>
// submit the form onChange to get the list of cities
fetcher.submit(event.target.form)
}
/>
{/* render with the loader's data */}
{fetcher.data ? (
<ComboboxPopover className="shadow-popup">
{fetcher.data.length > 0 ? (
<ComboboxList>
{fetcher.data.map((city) => (
<ComboboxOption
key={city.id}
value={city.name}
/>
))}
</ComboboxList>
) : (
<span>No results found</span>
)}
</ComboboxPopover>
) : null}
</Combobox>
</fetcher.Form>
);
}
應用程式需要知道的只是如何查詢資料以及如何呈現它,Remix 會處理網路。
Remix 為開發人員提供了一種直觀的、基於瀏覽器的方法來管理網路請求。透過鏡像瀏覽器行為並在需要時增強它們,它簡化了並行性、重新驗證和潛在競爭條件的複雜性。無論您是建構一個簡單的網頁還是複雜的 Web 應用程式,Remix 都能確保您的使用者互動順暢、可靠且始終保持最新。