使用 Svelte 儲存

在上一篇文章中,我們完成了應用程式的開發,完成了將其組織成元件的工作,並討論了一些處理響應性、處理 DOM 節點和公開元件功能的高階技術。在本文中,我們將展示另一種在 Svelte 中處理狀態管理的方法:儲存。儲存是儲存值的全域性資料儲存庫。元件可以訂閱儲存,並在其值發生變化時接收通知。

先決條件

至少,建議您熟悉核心 HTMLCSSJavaScript 語言,並瞭解 終端/命令列

您需要一個安裝了 node 和 npm 的終端來編譯和構建您的應用程式。

目標 學習如何使用 Svelte 儲存

使用儲存,我們將建立一個Alert元件,該元件在螢幕上顯示通知,並且可以接收來自任何元件的訊息。在這種情況下,Alert元件獨立於其他元件——它不是任何其他元件的父級或子級——因此訊息不適合元件層次結構。

我們還將瞭解如何開發自己的自定義儲存以將待辦事項資訊持久儲存到 Web 儲存,從而允許我們的待辦事項在頁面重新載入後仍然存在。

與我們一起編寫程式碼

Git

使用以下命令克隆 GitHub 倉庫(如果您尚未執行此操作):

bash
git clone https://github.com/opensas/mdn-svelte-tutorial.git

然後,要進入當前應用程式狀態,請執行:

bash
cd mdn-svelte-tutorial/06-stores

或直接下載資料夾的內容

bash
npx degit opensas/mdn-svelte-tutorial/06-stores

請記住執行npm install && npm run dev以在開發模式下啟動您的應用程式。

REPL

要使用 REPL 與我們一起編碼,請從以下位置開始:

https://svelte.dev/repl/d1fa84a5a4494366b179c87395940039?version=3.23.2

處理應用程式狀態

我們已經瞭解了我們的元件如何使用 props、雙向資料繫結和事件相互通訊。在所有這些情況下,我們都在處理父元件和子元件之間的通訊。

但並非所有應用程式狀態都屬於應用程式的元件層次結構中。例如,有關登入使用者的資訊,或是否選擇了深色主題。

有時,您的應用程式狀態需要由多個與層次結構無關的元件或常規 JavaScript 模組訪問。

此外,當您的應用程式變得複雜且元件層次結構變得複雜時,元件之間可能難以相互傳遞資料。在這種情況下,遷移到全域性資料儲存可能是一個不錯的選擇。如果您已經使用過 ReduxVuex,那麼您將熟悉這種儲存的工作方式。Svelte 儲存為狀態管理提供了類似的功能。

儲存是一個具有subscribe()方法的物件,該方法允許感興趣的方在儲存值發生變化時收到通知,以及一個可選的set()方法,該方法允許您為儲存設定新值。這個最小的 API 稱為 儲存契約

Svelte 提供了用於在svelte/store模組中建立 可讀可寫派生 儲存的函式。

Svelte 還提供了一種非常直觀的方法,可以使用 反應式$store語法 將儲存整合到其反應式系統中。如果您建立自己的符合儲存契約的儲存,則可以免費獲得此反應式語法糖。

建立 Alert 元件

為了展示如何使用儲存,我們將建立一個Alert元件。這些型別的部件也可能被稱為彈出通知、吐司或通知氣泡。

我們的Alert元件將由App元件顯示,但任何元件都可以向其傳送通知。每當收到通知時,Alert元件將負責在螢幕上顯示它。

建立儲存

讓我們從建立一個可寫儲存開始。任何元件都可以寫入此儲存,並且Alert元件將訂閱它,並在修改儲存時顯示訊息。

  1. 在您的src目錄中建立一個新檔案stores.js
  2. 賦予它以下內容
    js
    import { writable } from "svelte/store";
    
    export const alert = writable("Welcome to the to-do list app!");
    

注意:儲存可以在 Svelte 元件外部定義和使用,因此您可以根據需要組織它們。

在上面的程式碼中,我們從svelte/store匯入writable()函式,並使用它建立一個名為alert的新儲存,其初始值為“歡迎使用待辦事項列表應用程式!”。然後我們export儲存。

建立實際的元件

現在讓我們建立我們的Alert元件,並瞭解如何從儲存中讀取值。

  1. 建立另一個名為src/components/Alert.svelte的新檔案。
  2. 賦予它以下內容
    svelte
    <script>
      import { alert } from '../stores.js'
      import { onDestroy } from 'svelte'
    
      let alertContent = ''
    
      const unsubscribe = alert.subscribe((value) => alertContent = value)
    
      onDestroy(unsubscribe)
    </script>
    
    {#if alertContent}
    <div on:click={() => alertContent = ''}>
      <p>{ alertContent }</p>
    </div>
    {/if}
    
    <style>
    div {
      position: fixed;
      cursor: pointer;
      margin-right: 1.5rem;
      margin-left: 1.5rem;
      margin-top: 1rem;
      right: 0;
      display: flex;
      align-items: center;
      border-radius: 0.2rem;
      background-color: #565656;
      color: #fff;
      font-weight: 700;
      padding: 0.5rem 1.4rem;
      font-size: 1.5rem;
      z-index: 100;
      opacity: 95%;
    }
    div p {
      color: #fff;
    }
    div svg {
      height: 1.6rem;
      fill: currentcolor;
      width: 1.4rem;
      margin-right: 0.5rem;
    }
    </style>
    

讓我們詳細瞭解這段程式碼。

  • 在開頭,我們匯入alert儲存。
  • 接下來,我們匯入onDestroy()生命週期函式,它允許我們在元件解除安裝後執行回撥。
  • 然後我們建立一個名為alertContent的區域性變數。請記住,我們可以從標記中訪問頂級變數,並且每當修改它們時,DOM 都會相應地更新。
  • 然後我們呼叫方法alert.subscribe(),將回調函式作為引數傳遞給它。每當儲存的值發生變化時,回撥函式將被呼叫,並將新值作為其引數。在回撥函式中,我們只需將接收到的值賦給區域性變數,這將觸發元件 DOM 的更新。
  • subscribe()方法還返回一個清理函式,該函式負責釋放訂閱。因此,我們在元件初始化時訂閱,並在元件解除安裝時使用onDestroy取消訂閱。
  • 最後,我們在標記中使用alertContent變數,如果使用者單擊警報,我們將清除它。
  • 最後,我們包含幾行 CSS 程式碼來設定我們的Alert元件的樣式。

此設定允許我們以反應式的方式使用儲存。當儲存的值發生變化時,將執行回撥。在那裡,我們將一個新值賦給一個區域性變數,並且由於 Svelte 的反應性,我們所有的標記和反應式依賴項都會相應地更新。

使用元件

現在讓我們使用我們的元件。

  1. App.svelte中,我們將匯入元件。在現有匯入語句下方新增以下匯入語句
    js
    import Alert from "./components/Alert.svelte";
    
  2. 然後在Todos呼叫上方呼叫Alert元件,如下所示
    svelte
    <Alert />
    <Todos {todos} />
    
  3. 現在載入您的測試應用程式,您現在應該在螢幕上看到Alert訊息。您可以單擊它將其關閉。 應用程式右上角顯示一個簡單的通知,內容為歡迎使用待辦事項列表應用程式

使用響應式 $store 語法使儲存變得響應式

這可以工作,但是每次您想要訂閱儲存時都必須複製貼上所有這些程式碼

svelte
<script>
  import myStore from "./stores.js";
  import { onDestroy } from "svelte";

  let myStoreContent = "";

  const unsubscribe = myStore.subscribe((value) => (myStoreContent = value));

  onDestroy(unsubscribe);
</script>

{myStoreContent}

對於 Svelte 來說,這太多了!作為編譯器,Svelte 有更多資源來讓我們的生活更輕鬆。在這種情況下,Svelte 提供了反應式$store語法,也稱為自動訂閱。簡單來說,您只需在儲存前新增$符號,Svelte 將生成程式碼以使其自動具有反應性。因此,我們之前的程式碼塊可以替換為以下內容

svelte
<script>
  import myStore from "./stores.js";
</script>

{$myStore}

並且$myStore將完全具有反應性。這也適用於您自己的自定義儲存。如果您實現了subscribe()set()方法,就像我們稍後將要做的,反應式$store語法也將應用於您的儲存。

  1. 讓我們將其應用於我們的Alert元件。更新Alert.svelte<script>和標記部分,如下所示
    svelte
    <script>
      import { alert } from '../stores.js'
    </script>
    
    {#if $alert}
    <div on:click={() => $alert = ''}>
      <p>{ $alert }</p>
    </div>
    {/if}
    
  2. 再次檢查您的應用程式,您會發現它與之前一樣工作。這樣好多了!

在幕後,Svelte 生成了程式碼來宣告區域性變數$alert、訂閱alert儲存、在儲存內容修改時更新$alert以及在元件解除安裝時取消訂閱。它還將在我們為$alert分配值時生成alert.set()語句。

此巧妙技巧的最終結果是,您可以像使用反應式區域性變數一樣輕鬆地訪問全域性儲存。

這是一個完美的例子,說明了 Svelte 如何讓編譯器負責更好的開發者體驗,不僅可以節省我們鍵入樣板程式碼的時間,還可以生成更不易出錯的程式碼。

寫入我們的儲存

寫入我們的儲存只是匯入它並執行$store = 'new value'的問題。讓我們在我們的Todos元件中使用它。

  1. 在現有的匯入語句下方新增以下import語句
    js
    import { alert } from "../stores.js";
    
  2. 像這樣更新您的addTodo()函式
    js
    function addTodo(name) {
      todos = [...todos, { id: newTodoId, name, completed: false }];
      $alert = `Todo '${name}' has been added`;
    }
    
  3. 像這樣更新removeTodo()
    js
    function removeTodo(todo) {
      todos = todos.filter((t) => t.id !== todo.id);
      todosStatus.focus(); // give focus to status heading
      $alert = `Todo '${todo.name}' has been deleted`;
    }
    
  4. updateTodo()函式更新為以下內容
    js
    function updateTodo(todo) {
      const i = todos.findIndex((t) => t.id === todo.id);
      if (todos[i].name !== todo.name)
        $alert = `todo '${todos[i].name}' has been renamed to '${todo.name}'`;
      if (todos[i].completed !== todo.completed)
        $alert = `todo '${todos[i].name}' marked as ${
          todo.completed ? "completed" : "active"
        }`;
      todos[i] = { ...todos[i], ...todo };
    }
    
  5. 在以let filter = 'all'開頭的塊下方新增以下反應式塊
    js
    $: {
      if (filter === "all") {
        $alert = "Browsing all to-dos";
      } else if (filter === "active") {
        $alert = "Browsing active to-dos";
      } else if (filter === "completed") {
        $alert = "Browsing completed to-dos";
      }
    }
    
  6. 最後,暫時更新const checkAllTodosconst removeCompletedTodos塊,如下所示
    js
    const checkAllTodos = (completed) => {
      todos = todos.map((t) => ({ ...t, completed }));
      $alert = `${completed ? "Checked" : "Unchecked"} ${todos.length} to-dos`;
    };
    const removeCompletedTodos = () => {
      $alert = `Removed ${todos.filter((t) => t.completed).length} to-dos`;
      todos = todos.filter((t) => !t.completed);
    };
    
  7. 所以基本上,我們匯入了儲存並在每個事件上更新了它,這會導致每次顯示一個新的警報。再次檢視您的應用程式,然後嘗試新增/刪除/更新一些待辦事項!

一旦我們執行$alert = …,Svelte 將執行alert.set()。我們的Alert元件——就像 alert 儲存的每個訂閱者一樣——在接收到新值時將收到通知,並且由於 Svelte 的反應性,其標記將被更新。

我們可以在任何元件或.js檔案中執行相同的操作。

注意:在 Svelte 元件外部,您無法使用$store語法。這是因為 Svelte 編譯器不會觸及 Svelte 元件外部的任何內容。在這種情況下,您必須依靠store.subscribe()store.set()方法。

改進我們的 Alert 元件

每次都必須單擊警報才能將其刪除有點煩人。如果通知在幾秒鐘後自動消失會更好。

讓我們看看如何做到這一點。我們將指定一個帶有等待毫秒數的 prop,以便在清除通知之前等待,並且我們將定義一個超時以刪除警報。我們還將在Alert元件解除安裝時清除超時,以防止記憶體洩漏。

  1. 像這樣更新Alert.svelte元件的<script>部分
    js
    import { onDestroy } from "svelte";
    import { alert } from "../stores.js";
    
    export let ms = 3000;
    let visible;
    let timeout;
    
    const onMessageChange = (message, ms) => {
      clearTimeout(timeout);
      if (!message) {
        // hide Alert if message is empty
        visible = false;
      } else {
        visible = true; // show alert
        if (ms > 0) timeout = setTimeout(() => (visible = false), ms); // and hide it after ms milliseconds
      }
    };
    $: onMessageChange($alert, ms); // whenever the alert store or the ms props changes run onMessageChange
    
    onDestroy(() => clearTimeout(timeout)); // make sure we clean-up the timeout
    
  2. 並像這樣更新Alert.svelte的標記部分

    svelte
    {#if visible}
    <div on:click={() => visible = false}>
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M12.432 0c1.34 0 2.01.912 2.01 1.957 0 1.305-1.164 2.512-2.679 2.512-1.269 0-2.009-.75-1.974-1.99C9.789 1.436 10.67 0 12.432 0zM8.309 20c-1.058 0-1.833-.652-1.093-3.524l1.214-5.092c.211-.814.246-1.141 0-1.141-.317 0-1.689.562-2.502 1.117l-.528-.88c2.572-2.186 5.531-3.467 6.801-3.467 1.057 0 1.233 1.273.705 3.23l-1.391 5.352c-.246.945-.141 1.271.106 1.271.317 0 1.357-.392 2.379-1.207l.6.814C12.098 19.02 9.365 20 8.309 20z"/></svg>
      <p>{ $alert }</p>
    </div>
    {/if}
    

首先,我們建立了一個名為 ms 的 prop,其預設值為 3000(毫秒)。然後,我們建立了一個 onMessageChange() 函式,用於控制 Alert 是否可見。透過 $: onMessageChange($alert, ms),我們告訴 Svelte 在 $alert 儲存或 ms prop 發生變化時執行此函式。

每當 $alert 儲存發生變化時,我們將清除任何掛起的超時。如果 $alert 為空,我們將 visible 設定為 false,Alert 將從 DOM 中移除。如果它不為空,我們將 visible 設定為 true,並使用 setTimeout() 函式在 ms 毫秒後清除 Alert。

最後,使用 onDestroy() 生命週期函式,我們確保呼叫 clearTimeout() 函式。

我們還在 Alert 段落上方添加了一個 SVG 圖示,使其看起來更美觀一些。再次嘗試一下,您應該可以看到更改。

使我們的 Alert 元件可訪問

我們的 Alert 元件執行良好,但對於輔助技術而言並不友好。問題在於動態新增到頁面並從中移除的元素。雖然對於可以看到頁面的使用者來說很明顯,但對於使用輔助技術(如螢幕閱讀器)的使用者來說可能就不那麼明顯了。為了處理這些情況,我們可以利用 ARIA 即時區域,它提供了一種以程式設計方式公開動態內容更改的方法,以便輔助技術可以檢測和宣佈這些更改。

我們可以宣告一個包含應由輔助技術宣佈的動態內容的區域,方法是使用 aria-live 屬性,後跟禮貌設定,該設定用於設定螢幕閱讀器處理該區域更新的優先順序。可能的設定有 offpoliteassertive

對於常見情況,您還有幾個預定義的專門 role 值可用,例如 logstatusalert

在我們的例子中,只需在 <div> 容器中新增 role="alert" 即可,如下所示

svelte
<div role="alert" on:click={() => visible = false}>

通常,使用螢幕閱讀器測試您的應用程式是一個好主意,這不僅可以發現可訪問性問題,還可以瞭解視障人士如何使用網路。您有多種選擇,例如 Windows 上的 NVDA、Chrome 上的 ChromeVox、Linux 上的 Orca 以及 macOS 和 iOS 上的 VoiceOver,以及其他選項。

要了解有關檢測和修復可訪問性問題的更多資訊,請檢視我們的 處理常見可訪問性問題 文章。

使用儲存契約來持久化我們的待辦事項

我們的簡易應用程式使我們能夠輕鬆管理待辦事項,但如果我們每次重新載入時都獲得相同的硬編碼待辦事項列表,它就沒什麼用了。為了使其真正有用,我們必須找出如何持久化我們的待辦事項。

首先,我們需要某種方法讓我們的 Todos 元件將其更新的待辦事項返回給其父元件。我們可以使用待辦事項列表發出更新事件,但繫結 todos 變數更容易。讓我們開啟 App.svelte 並嘗試一下。

  1. 首先,在 todos 陣列下方新增以下行
    js
    $: console.log("todos", todos);
    
  2. 接下來,更新您的 Todos 元件呼叫,如下所示
    svelte
    <Todos bind:todos />
    

    注意:<Todos bind:todos /> 只是 <Todos bind:todos={todos} /> 的簡寫。

  3. 返回您的應用程式,嘗試新增一些待辦事項,然後轉到您的開發者工具 Web 控制檯。您會看到,由於 bind 指令,我們對待辦事項所做的每次修改都會反映在 App.svelte 中定義的 todos 陣列中。

現在我們必須找到一種方法來持久化這些待辦事項。我們可以在 App.svelte 元件中實現一些程式碼,以將我們的待辦事項讀寫到 Web 儲存 或 Web 服務中。但是,如果我們可以開發一些通用的儲存來持久化其內容,豈不是更好?這將允許我們像使用任何其他儲存一樣使用它,並抽象化持久化機制。我們可以建立一個將其內容同步到 Web 儲存的儲存,然後開發另一個與 Web 服務同步的儲存。在它們之間切換將非常簡單,我們根本不需要觸碰 App.svelte

儲存我們的待辦事項

因此,讓我們首先使用常規的可寫儲存來儲存我們的待辦事項。

  1. 開啟檔案 stores.js,並在現有儲存下方新增以下儲存
    js
    export const todos = writable([]);
    
  2. 這很容易。現在我們需要匯入儲存並在 App.svelte 中使用它。請記住,要訪問待辦事項,現在我們必須使用 $todos 反應式 $store 語法。像這樣更新您的 App.svelte 檔案
    svelte
    <script>
      import Todos from "./components/Todos.svelte";
      import Alert from "./components/Alert.svelte";
    
      import { todos } from "./stores.js";
    
      $todos = [
        { id: 1, name: "Create a Svelte starter app", completed: true },
        { id: 2, name: "Create your first component", completed: true },
        { id: 3, name: "Complete the rest of the tutorial", completed: false }
      ];
    </script>
    
    <Alert />
    <Todos bind:todos={$todos} />
    
  3. 嘗試一下;一切應該都能正常工作。接下來,我們將瞭解如何定義我們自己的自定義儲存。

如何實現儲存契約:理論

您可以透過實現儲存契約來建立自己的儲存,而無需依賴 svelte/store。其功能必須按如下方式工作

  1. 儲存必須包含一個 subscribe() 方法,該方法必須接受訂閱函式作為其引數。每當儲存的值發生變化時,都必須呼叫儲存的所有活動訂閱函式。
  2. subscribe() 方法必須返回一個 unsubscribe() 函式,該函式在被呼叫時必須停止其訂閱。
  3. 儲存可以選擇包含一個 set() 方法,該方法必須接受儲存的新值作為其引數,並且同步呼叫儲存的所有活動訂閱函式。具有 set() 方法的儲存稱為可寫儲存。

首先,讓我們在 App.svelte 元件中新增以下 console.log() 語句,以檢視 todos 儲存及其內容的實際情況。在 todos 陣列下方新增這些行

js
console.log("todos store - todos:", todos);
console.log("todos store content - $todos:", $todos);

現在執行應用程式時,您將在 Web 控制檯中看到如下內容

web console showing the functions and contents of the todos store

如您所見,我們的儲存只是一個包含 subscribe()set()update() 方法的物件,$todos 是我們的待辦事項陣列。

僅供參考,以下是從頭開始實現的基本工作儲存

js
export const writable = (initial_value = 0) => {
  let value = initial_value; // content of the store
  let subs = []; // subscriber's handlers

  const subscribe = (handler) => {
    subs = [...subs, handler]; // add handler to the array of subscribers
    handler(value); // call handler with current value
    return () => (subs = subs.filter((sub) => sub !== handler)); // return unsubscribe function
  };

  const set = (new_value) => {
    if (value === new_value) return; // same value, exit
    value = new_value; // update value
    subs.forEach((sub) => sub(value)); // update subscribers
  };

  const update = (update_fn) => set(update_fn(value)); // update function

  return { subscribe, set, update }; // store contract
};

在這裡,我們聲明瞭 subs,它是一個訂閱者陣列。在 subscribe() 方法中,我們將處理程式新增到 subs 陣列中,並返回一個函式,該函式在執行時將從陣列中移除處理程式。

當我們呼叫 set() 時,我們將更新儲存的值並呼叫每個處理程式,並將新值作為引數傳遞。

通常,您不會從頭開始實現儲存;而是使用可寫儲存來建立 自定義儲存,並帶有特定於域的邏輯。在以下示例中,我們建立了一個計數器儲存,它只允許我們將計數器加一或重置其值

js
import { writable } from "svelte/store";
function myStore() {
  const { subscribe, set, update } = writable(0);

  return {
    subscribe,
    addOne: () => update((n) => n + 1),
    reset: () => set(0),
  };
}

如果我們的待辦事項列表應用程式變得過於複雜,我們可以讓我們的待辦事項儲存處理所有狀態修改。我們可以將修改 todo 陣列的所有方法(如 addTodo()removeTodo() 等)從 Todos 元件移動到儲存中。如果您有一個所有狀態修改都應用於其中的中心位置,則元件只需呼叫這些方法來修改應用程式的狀態並以反應方式顯示儲存公開的資訊。擁有一個處理狀態修改的唯一位置,可以更容易地推斷狀態流並發現問題。

Svelte 不會強迫您以特定方式組織狀態管理;它只是為您提供工具來選擇如何處理它。

實現我們的自定義待辦事項儲存

我們的待辦事項列表應用程式並不是特別複雜,因此我們不會將所有修改方法移動到中心位置。我們將保留它們原樣,而是專注於持久化我們的待辦事項。

注意:如果您正在遵循本指南並從 Svelte REPL 中工作,則將無法完成此步驟。出於安全原因,Svelte REPL 在沙盒環境中工作,該環境不允許您訪問 Web 儲存,並且您將收到“操作不安全”錯誤。為了遵循本節,您需要克隆儲存庫並轉到 mdn-svelte-tutorial/06-stores 資料夾,或者您可以使用 npx degit opensas/mdn-svelte-tutorial/06-stores 直接下載資料夾的內容。

要實現一個將內容儲存到 Web 儲存的自定義儲存,我們需要一個可寫儲存來執行以下操作

  • 最初從 Web 儲存讀取值,如果值不存在,則使用預設值進行初始化
  • 每當值被修改時,更新儲存本身以及本地儲存中的資料

此外,由於 Web 儲存僅支援儲存字串值,因此在儲存時我們將不得不從物件轉換為字串,反之亦然,當我們從本地儲存載入值時。

  1. src 目錄中建立一個名為 localStore.js 的新檔案。
  2. 賦予它以下內容
    js
    import { writable } from "svelte/store";
    
    export const localStore = (key, initial) => {
      // receives the key of the local storage and an initial value
    
      const toString = (value) => JSON.stringify(value, null, 2); // helper function
      const toObj = JSON.parse; // helper function
    
      if (localStorage.getItem(key) === null) {
        // item not present in local storage
        localStorage.setItem(key, toString(initial)); // initialize local storage with initial value
      }
    
      const saved = toObj(localStorage.getItem(key)); // convert to object
    
      const { subscribe, set, update } = writable(saved); // create the underlying writable store
    
      return {
        subscribe,
        set: (value) => {
          localStorage.setItem(key, toString(value)); // save also to local storage as a string
          return set(value);
        },
        update,
      };
    };
    
    • 我們的 localStore 將是一個函式,在執行時最初從 Web 儲存讀取其內容,並返回一個包含三個方法的物件:subscribe()set()update()
    • 當我們建立一個新的 localStore 時,我們將不得不指定 Web 儲存的鍵和初始值。然後,我們檢查該值是否在 Web 儲存中存在,如果不存在,則建立它。
    • 我們使用 localStorage.getItem(key)localStorage.setItem(key, value) 方法讀取和寫入 Web 儲存中的資訊,以及 toString()toObj()(使用 JSON.parse())輔助函式來轉換值。
    • 接下來,我們將從 Web 儲存接收到的字串內容轉換為物件,並將該物件儲存在我們的儲存中。
    • 最後,每次我們更新儲存的內容時,我們還會更新 Web 儲存,並將值轉換為字串。
    請注意,我們只需要重新定義 set() 方法,新增將值儲存到 Web 儲存的操作。其餘程式碼主要用於初始化和轉換內容。
  3. 現在,我們將從 stores.js 中使用我們的本地儲存來建立我們本地持久化的待辦事項儲存。像這樣更新 stores.js
    js
    import { writable } from "svelte/store";
    import { localStore } from "./localStore.js";
    
    export const alert = writable("Welcome to the to-do list app!");
    
    const initialTodos = [
      { id: 1, name: "Visit MDN web docs", completed: true },
      { id: 2, name: "Complete the Svelte Tutorial", completed: false },
    ];
    
    export const todos = localStore("mdn-svelte-todo", initialTodos);
    
    使用 localStore('mdn-svelte-todo', initialTodos),我們正在配置儲存,以便在鍵 mdn-svelte-todo 下將資料儲存到 Web 儲存中。我們還設定了一些待辦事項作為初始值。
  4. 現在讓我們刪除 App.svelte 中的硬編碼待辦事項。像這樣更新其內容。我們基本上只是刪除了 $todos 陣列和 console.log() 語句
    svelte
    <script>
      import Todos from './components/Todos.svelte'
      import Alert from './components/Alert.svelte'
    
      import { todos } from './stores.js'
    </script>
    
    <Alert />
    <Todos bind:todos={$todos} />
    

    注意:這是為了使用我們的自定義儲存而需要做的唯一更改。就我們使用哪種儲存而言,App.svelte 完全是透明的。

  5. 繼續嘗試您的應用程式。建立一些待辦事項,然後關閉瀏覽器。您甚至可以停止 Svelte 伺服器並重新啟動它。重新訪問 URL 後,您的待辦事項仍然存在。

  6. 您也可以在 DevTools 控制檯檢查它。在 Web 控制檯中,輸入命令localStorage.getItem('mdn-svelte-todo')。對您的應用程式進行一些更改,例如按下取消選中所有按鈕,然後再次檢查 Web 儲存內容。您將得到類似以下內容:待辦事項應用程式及其旁側的 Web 控制檯檢視,顯示當應用程式中更改待辦事項時,Web 儲存中的相應條目也會發生更改

Svelte 儲存提供了一種非常簡單輕量級但功能強大的方法,可以以響應式的方式從全域性資料儲存中處理複雜的應用程式狀態。並且由於 Svelte 編譯了我們的程式碼,因此它可以提供$store自動訂閱語法,使我們能夠像使用區域性變數一樣使用儲存。由於儲存具有最小的 API,因此建立自定義儲存以抽象儲存本身的內部工作原理非常簡單。

加分曲目:過渡

現在讓我們改變主題,做一些有趣和不同的事情:向我們的警報新增動畫。Svelte 提供了一個完整的模組來定義過渡動畫,以便我們可以使我們的使用者介面更具吸引力。

過渡是使用transition:fn指令應用的,並且由元素由於狀態更改而進入或離開 DOM 觸發。svelte/transition模組匯出七個函式:fadeblurflyslidescaledrawcrossfade

讓我們為我們的Alert元件提供一個 fly transition。我們將開啟Alert.svelte檔案並從svelte/transition模組匯入fly函式。

  1. 將以下import語句放在現有語句下方
    js
    import { fly } from "svelte/transition";
    
  2. 要使用它,請更新您的起始<div>標記,如下所示
    svelte
    <div role="alert" on:click={() => visible = false}
      transition:fly
    >
    
    過渡也可以接收引數,如下所示
    svelte
    <div role="alert" on:click={() => visible = false}
      transition:fly="{{delay: 250, duration: 300, x: 0, y: -100, opacity: 0.5}}"
    >
    

    注意:雙花括號不是特殊的 Svelte 語法。它只是一個作為引數傳遞給 fly 過渡的文字 JavaScript 物件。

  3. 再次嘗試您的應用程式,您會發現通知現在看起來更具吸引力。

注意:作為編譯器,Svelte 可以透過排除未使用的功能來最佳化捆綁包的大小。在這種情況下,如果我們使用npm run build將應用程式編譯為生產環境,我們的public/build/bundle.js檔案大小將略小於 22 KB。如果我們刪除transitions:fly指令,Svelte 足夠智慧,可以意識到 fly 函式未使用,並且bundle.js檔案大小將下降到僅 18 KB。

這僅僅是冰山一角。Svelte 具有許多處理動畫和過渡的選項。Svelte 還支援使用in:fn/out:fn指令指定在元素新增到或從 DOM 中移除時應用的不同過渡,並且它還允許您定義自定義CSSJavaScript過渡。它還有一些緩動函式來指定隨時間變化的變化率。檢視緩動視覺化工具以探索可用的各種緩動函式。

目前的程式碼

Git

要檢視本文結尾處程式碼的狀態,請訪問您儲存庫的副本,如下所示

bash
cd mdn-svelte-tutorial/07-next-steps

或直接下載資料夾的內容

bash
npx degit opensas/mdn-svelte-tutorial/07-next-steps

請記住執行npm install && npm run dev以在開發模式下啟動您的應用程式。

REPL

要在 REPL 中檢視程式碼的當前狀態,請訪問

https://svelte.dev/repl/378dd79e0dfe4486a8f10823f3813190?version=3.23.2

總結

在本文中,我們添加了兩個新功能:一個Alert元件和將todos持久化到 Web 儲存。

  • 這使我們能夠展示一些高階的 Svelte 技術。我們開發了Alert元件以展示如何使用儲存實現跨元件狀態管理。我們還了解了如何自動訂閱儲存以將其與 Svelte 反應式系統無縫整合。
  • 然後我們瞭解瞭如何從頭開始實現自己的儲存,以及如何擴充套件 Svelte 的可寫儲存以將資料持久化到 Web 儲存。
  • 最後,我們瞭解瞭如何使用 Svelte transition指令在 DOM 元素上實現動畫。

在下一篇文章中,我們將學習如何向我們的 Svelte 應用程式新增 TypeScript 支援。為了充分利用其所有功能,我們還將把整個應用程式移植到 TypeScript。