首頁 >web前端 >js教程 >複製品大圖

複製品大圖

Patricia Arquette
Patricia Arquette原創
2025-01-02 20:23:39909瀏覽

複製快取

這是一個幫助實現本地優先軟體的框架。 Git 同樣可以透過推和拉來幫助組織同步任務。

Replicache 在後台非同步同步伺服器數據,消除伺服器往返並實現即時 UI 變更。

複製零件

Replicache 由多個元素組成。

複製快取

Replicache 可以被視為瀏覽器內的鍵值存儲,其中包含內部類似 git 的操作。先寫入內存,後同步。

您的申請

這是我們創建的應用程序,就像網頁應用程式一樣。它是 Replicache 中儲存狀態的實體。變異器和訂閱的實作是為了改變和回應狀態

你的伺服器

它的存在是為了儲存最可靠的資料。連接到伺服器的資料庫中儲存的狀態優先於應用程式中的狀態。

伺服器必須實作push(上游)和pull(下游)才能與客戶端的Replicache通訊。

  • push(上游):Replicache 將變更傳送到推播端點。變異器在伺服器和應用程式上實現,並且此推送端點執行此變異器來更改資料庫的狀態。

  • pull(下游):當定期或明確要求時,Replicache 會向伺服器發送拉取請求。伺服器傳回客戶端所需的更改,以使其與伺服器的狀態相同。

  • poke:雖然客戶端會週期性的發送一個pull request,但是為了更即時的展示,當服務端有變化時,就是服務端給客戶端一個提示的信號提出拉取請求。它不攜帶任何數據。

同步

應用程式和伺服器已同步到最新狀態。下圖清楚地展示了這個過程。它展示了定期從伺服器拉取狀態變更並更新 UI 的過程,以及客戶端上的狀態變更如何先更新 UI,然後推送到伺服器

Replicache Big Picture
來源

客戶端、客戶端群組、快取

記憶體中的副本稱為客戶端。

import {Replicache} from "replicache";

const rep = new Replicache({
  name: userID,
  ...
});

console.log(rep.clientID);

每次點擊通常都有一個客戶端。客戶端是不穩定的並且遵循選項卡的生命週期。擁有唯一的 clientID。

客戶端群組是一組共享本機資料的客戶端。即使離線時,該客戶端群組內的客戶端也會共用狀態。

Client Group 使用磁碟上的持久緩存,該緩存透過 Replicache 建構函數的 name 參數進行區分。屬於具有相同名稱的客戶端群組的所有客戶端共享相同的快取。

客戶視角

客戶端在持久化快取中有一個鍵值對的有序映射,稱為客戶端視圖。客戶端視圖是應用程式數據,與伺服器數據同步。之所以稱為客戶端視圖,是因為不同的客戶端可能在不同的客戶端視圖中擁有伺服器資料。這意味著每個客戶端看到的伺服器狀態都不同。

存取客戶端視圖非常快速。讀取延遲小於 1 毫秒,大多數裝置的吞吐量為 500MB/s。

建議您直接從 Client View 讀取並使用它,而不是使用 useState 從 React 等地方單獨複製 Client View 並將其上傳到記憶體。當 mutator 更改 Client View 時,會觸發訂閱,從而更新 UI

訂閱

Subscribe 函數接收 ReadTransaction 參數並實作從 Replicache 讀取。每當此訂閱因副本資料變更而過期時,就會再次執行訂閱功能。如果此結果發生變化,則值會更新,UI 也會更新。

如果您透過訂閱配置 UI,則可以始終保持最新狀態。

import {Replicache} from "replicache";

const rep = new Replicache({
  name: userID,
  ...
});

console.log(rep.clientID);

突變

Mutation 是指改變 Replicache 資料的任務。接收突變並實際更改資料的主體稱為突變者。

啟動時,Replicache 中會註冊幾個 Mutators,但它們實際上只是命名函數。下面的createTodo和markTodoComplete都是透過WriteTransaction改變Replicache資料的變異器。

const todos = useSubscribe(rep, async tx => {
  return await tx.scan({prefix: 'todo/'}).toArray();
});
return (
  <ul>
    {todos.map(todo => (
      <li key={todo.id}>{todo.text}</li>
    ))}
  </ul>
);

Mutator 的工作原理如下。當mutator運行時,資料發生變化,與之相關的訂閱被觸發,UI也發生變化。

const rep = new Replicache({
  ...
  mutators: {
    createTodo,
    markTodoComplete,
  },
});

async function createTodo(tx: WriteTransaction, todo: Todo) {
  await tx.set(`/todo/${todo.id}`, todo);
}

async function markTodoComplete(tx: WriteTransaction,
    {id, complete}: {id: string, complete: boolean}) {
  const key = `/todo/${id}`;
  const todo = await tx.get(key);
  if (!todo) {
    return;
  }
  todo.complete = complete;
  await tx.set(key, todo);
}

在內部,Mutator 創造了一種稱為突變的東西。它就像一個執行記錄,但 Replicache 創建了以下突變。

await rep.mutate.createTodo({id: nanoid(), text: "take out the trash"});

這些突變被標記為待處理,直到它們被推送到伺服器並完全同步。

同步詳情

現在,這裡詳細介紹Sync,它可以說是Replicache的核心。同步已在伺服器上完成。

副本同步模型

(從現在開始,「狀態」一詞是指由多個鍵值對組成的資料(鍵值空間)的狀態。)

Replicache 試圖解決的同步問題是在多個客戶端同時更改相同狀態並且存在以下情況時發生的。

  1. 伺服器的狀態是事實的來源。它被表示為規範。
  2. 客戶端本地狀態的變化會立即反映出來。這就是所謂的投機。
  3. 伺服器必須僅套用一次更改,且結果必須是可預測的。應用於伺服器的變更必須能夠合理地與客戶端上的本機變更合併。

其中的最後一項是將伺服器更改與本地狀態“合理合併”,這是一個有趣的主題。為了‘合理合併’,必須考慮以下情況。

  • 如果本地變更尚未套用到伺服器。在這種情況下,即使從伺服器檢索到新狀態,您也需要確保本機變更不會從應用程式的 UI 中消失。從伺服器接收到新狀態後,任何現有的本機變更都必須在伺服器狀態之上重新執行。

  • 當客戶端上所做的本機變更已傳送至伺服器並反映在伺服器狀態中。在這種情況下,請注意不要兩次應用本地更改。不應重新套用本地變更

  • 如果有其他客戶端已將伺服器狀態變更為相同狀態。在這種情況下,與第一種情況一樣,必須根據從伺服器接收的狀態重做本機變更。但是,由於相同資源可能會發生衝突,因此必須仔細規劃合併邏輯。將此邏輯寫入 Mutator 中。

我們來看看Mutator的操作流程。

本地執行

變異器在本地運行,副本的值根據變異器邏輯變化。同時,該客戶端建立一個mutation,其mutationId依序增加。突變會作為待處理的突變進行排隊

待處理的突變被傳送到伺服器上實作的推播端點(replicache-push)。

變異透過執行伺服器上實現的變異器來改變規範狀態。在應用突變時,該客戶端的最後一個突變 ID 會被更新,並成為一個值,讓您知道當該客戶端進行下一次拉取時要重新應用哪個突變

本地應用的待定突變會產生推測結果,而應用於伺服器的突變會產生規範結果。應用於伺服器的變異已被確認,不會再次在本地執行。即使相同的突變會傳回不同的結果,伺服器的規範結果也會優先,因此客戶端的結果會改變

Replicache 定期向拉取端點 (replicache-pull) 發送請求,以檢索最新狀態並更新 UI。

拉取要求包含cookie和clientGroupId,並傳回新的cookie、修補程式和lastMutationIDChanges。

Cookie用來區分客戶端持有的伺服器狀態。任何可以追蹤伺服器和客戶端狀態差異程度的值就足夠了。您可以將其視為一個全域“版本”,只要資料庫狀態發生變化,該版本就會發生變化。或者,您可以使用 cookie 策略來追蹤更具體的資料範圍。

lastMutationIdChanges 是一個值,表示伺服器最後為每個客戶端套用的突變 ID。 mutationID 小於該值的所有突變不應再被視為待處理而是已確認。

變基

當客戶端收到拉取時,補丁必須套用於本機狀態。然而,由於待處理的突變會影響目前的本地狀態,因此補丁不能直接應用於本地狀態。相反,恢復本地掛起的突變,首先應用作為拉取接收的補丁,然後再次應用本地掛起的突變。

為了實現這種撤銷和重新應用,Replicache 的設計與 Git 類似。您可以將伺服器的狀態視為主分支,將由本機掛起的突變變更的狀態視為開發分支,接收從伺服器到主分支的拉取,並將開發變基到主分支。

rebase過程中可能出現的衝突將在下面單獨討論。

Poke,如上所述,是伺服器告訴客戶端拉取的提示訊息。

衝突解決

在 Replicache 這樣的分散式系統中,合併衝突是不可避免的。拉推過程中需要合併。合併的方式應該使合併結果可預測並符合應用程式的目的。

如果是會議室預訂應用,發生衝突時只能批准一個請求。因此,你必須採用合併方式,只批准先預約的客戶。

另一方面,如果它是一個Todo應用程序,Todo列表的目的是即使添加同時發生,兩個更改也會被批准。

以下兩種情況會發生合併衝突。

  1. 本地變更何時應用到伺服器。這是因為本地應用時的狀態和伺服器上應用時的狀態可能不同。

  2. 變基時。這是因為申請時的狀態可能會有所不同。

Replicache 認識到合併方法必須根據應用程式的目的以不同的方式實現,因此它允許開發人員實現它。開發者可以透過 Mutator 來實現這個邏輯。

以上是複製品大圖的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn