儲存

Framework7 附帶一個內建的輕量級應用程式狀態管理程式庫 - 儲存。它作為應用程式中所有元件的集中式儲存。

你可以使用特定於程式庫的狀態管理程式庫,例如 Vuex for Vue、Redux for React,並使用內建的 Svelte store 功能。但如果需要簡單的功能,Framework7 Store 會是很好的選擇。

建立 Store

首先,我們需要建立 store。讓我們為此建立一個獨立的 store.js 檔案

// First import createStore function from Framework7 core
import { createStore } from 'framework7/lite';

// create store
const store = createStore({
  // start with the state (store data)
  state: {
    users: [],
    // ...
  },

  // actions to operate with state and for async manipulations
  actions: {
    // context object containing store state will be passed as an argument
    getUsers({ state }) {
      // fetch users from API
      fetch('some-url')
        .then((res) => res.json())
        .then((users) => {
          // assign new users to store state.users
          state.users = users;
        })
    },
    // ...
  },

  // getters to retrieve the state
  getters: {
    // context object containing store state will be passed as an argument
    users({ state }) {
      return state.users;
    }
  }

})

// export store
export default store;

在此範例中,我們使用了下列 API 函式

createStore(storeParameters)- 建立 store

  • storeParameters - 物件。包含 store 參數的物件

函式會傳回已建立的 store 實例

Store 參數

現在,讓我們看看 storeParameters 物件

狀態

state 是包含所有應用程式層級狀態的單一物件,並作為「單一事實來源」。這也表示通常每個應用程式只會有一個 store。單一狀態樹讓尋找特定狀態變得簡單,並讓我們可以輕鬆擷取目前應用程式狀態的快照以進行除錯。

動作

actions 用於修改狀態、進行非同步操作,或呼叫其他 store 動作。動作處理函式會收到一個包含 store 狀態和 dispatch 方法的內容物件,以呼叫其他動作。因此,你可以存取 context.store 來存取狀態,或使用 context.dispatch 呼叫其他動作。

動作處理函式可能會收到任何自訂資料作為第二個引數。

為了保持 store 的反應性,狀態修改應透過指定來完成。例如

// modification of current state property - NOT REACTIVE
state.users.push(...users);

// assignemt to new value - REACTIVE
state.users = [...state.users, ...users];

取得器

getters 處理函式用於從 store 狀態傳回資料。當我們需要根據 store 狀態計算衍生狀態(例如過濾項目清單)時,這也很方便

const store = createStore({
  state: {
    users: [
      { id: 1, name: '...', registered: true },
      { id: 2, name: '...', registered: false }
    ]
  },
  getters: {
    registeredUsers: ({ state }) => {
      return state.users.filter((user) => user.registered);
    }
  }
})

取得器處理函式也會收到一個內容物件,但只包含 store 狀態。例如,無法從取得器呼叫其他動作。

使用 Store

現在我們已經建立了 store,讓我們找出如何使用它。

首先,我們需要將已建立的 store 傳遞給主要的 App 元件

<!-- pass store to the App's "store" prop -->
<App store={store}>
  <View main>
    <!-- ... -->
  </View>
</App>
<script>
  import { App, View } from 'framework7-svelte';
  // import our store
  import store from 'path/to/store.js';
</script>

存取 Store 和狀態

可以透過參照我們建立的 store 實例來直接存取 store(及其狀態)

import store from 'path/to/store.js';

console.log(store.state.users);

或透過存取 Framework7 實例的 store 屬性

import { f7 } from 'framework7-svelte';

console.log(f7.store.state.users);

發送動作

若要呼叫動作,我們需要使用 store.dispatch 方法,並指定要呼叫的動作名稱。

如果我們有下列 store 動作

const store = createStore({
  // ...
  actions: {
    // handler receives custom data in second argument
    getUsers({ state }, { total }) {
      fetch(`some-url?total=${total}`)
        .then((res) => res.json())
        .then((users) => {
          state.users = users;
        })
    },
  },
  // ...
})

我們必須呼叫 store.dispatch 方法

import store from 'path/to/store.js';

// call 'getUsers' actions
store.dispatch('getUsers', { total: 10 })

如果我們要在動作處理函式中呼叫另一個動作處理函式

const store = createStore({
  // ...
  actions: {
    setLoading({ state }, isLoading) {
      state.isLoading = isLoading;
    },
    // handler context also contains "dispatch" method
    getUsers({ state, dispatch }, { total }) {
      // call other action
      dispatch('setLoading', true);
      fetch(`some-url?total=${total}`)
        .then((res) => res.json())
        .then((users) => {
          state.users = users;
          // call other action
          dispatch('setLoading', false);
        })
    },
  },
  // ...
});

取得器

取得器值可以作為 store.getters 物件的靜態屬性來存取。

const store = createStore({
  state: {
    count: 10,
  },
  getters: {
    count({ state }) {
      return state.count;
    },
    double({ state }) {
      return state.count * 2;
    },
  },
});
import store from 'path/to/store.js';

const count = store.getters.count;
const double = store.getters.double;

Getter 值是具有包含 getters 處理常式的結果的 .value 屬性的靜態物件,因此

console.log(count.value); // -> 10
console.log(double.value); // -> 20

與狀態不同,getters 旨在具有反應性。因此,當您不需要任何反應性時,您可以直接存取 store.state,否則請使用 getters。

與 Svelte 組件搭配使用

有一個特殊的 useStore 輔助程式可供在 Svelte 組件中使用,以保持儲存庫反應性(當狀態/getters 值變更時自動更新組件)。

useStore(getterName, callback)- 直接傳回 getter 值並訂閱狀態更新

  • getterName - 字串 - getters 處理常式的名稱
  • callback - 函式 - 當依賴狀態已變更時,將使用新的 getter 值觸發的 callback 函式。

方法傳回 getter 處理常式值

如果我們需要從另一個儲存庫實例取得 getter 值,則我們也需要傳遞儲存庫

useStore(store, getterName, callback)- 直接傳回 getter 值並訂閱狀態更新

  • store - 儲存庫實例 - 要從中尋找 getters 的儲存庫實例。如果未指定,則會使用傳遞給 <App> 組件的預設儲存庫。
  • getterName - 字串 - getters 處理常式的名稱
  • callback - 函式 - 當依賴狀態已變更時,將使用新的 getter 值觸發的 callback 函式。

方法傳回 getter 處理常式值

如果我們有以下儲存庫

const store = createStore({
  state: {
    users: [],
  },
  actions: {
    getUsers({ state }) {
      // ...
    },
  },
  getters: {
    users({ state }) {
      return state.users;
    }
  },
});

然後,例如,我們應該在 Svelte 組件中使用以下內容

<Page>
  <List>
    {#each users as user}
      <ListItem title={user.name} />
    {/each}
  </List>
</Page>
<script>
  import { onMount } from 'svelte';
  // import special useStore helper/hook
  import { useStore, Page, List, ListItem } from 'framework7-svelte';
  // import store
  import store from 'path/to/store.js'

  // retrieve "users" getter handler value. Initially empty array
  let users = useStore('users', (value) => users = value);

  onMount(() => {
    // load users when component mounted
    store.dispatch('getUsers');
  });

</script>

由於我們使用了 Framework7 useStore 輔助程式/hook,因此當使用者載入時,組件將自動更新。

範例

import { createStore } from 'framework7/lite';

const store = createStore({
  state: {
    loading: false,
    users: [],
  },
  actions: {
    getUsers({ state }) {
      state.loading = true;
      setTimeout(() => {
        state.users = ['User 1', 'User 2', 'User 3', 'User 4', 'User 5'];
        state.loading = false;
      }, 3000);
    },
  },
  getters: {
    loading({ state }) {
      return state.loading;
    },
    users({ state }) {
      return state.users;
    },
  },
});

export default store;
store.svelte
<script>
  import {
    f7,
    useStore,
    Page,
    Navbar,
    Block,
    Button,
    Preloader,
    List,
    ListItem,
  } from 'framework7-svelte';

  // Subscribe to store getters
  let users = useStore('users', (value) => (users = value));
  let loading = useStore('usersLoading', (value) => (loading = value));

  // Call store action
  const load = () => f7.store.dispatch('loadUsers');
</script>

<Page>
  <Navbar title="Store" />
  <Block strong>
    <p>
      Framework7 comes with a built-in lightweight application state management library - Store. It
      serves as a centralized Store for all the components in an application.
    </p>
  </Block>
  {#if !users}
    <Block class="text-align-center">
      {#if !loading}
        <Button on:click={load} fill round>Load Users</Button>
      {/if}
      {#if loading}
        <Preloader />
      {/if}
    </Block>
  {/if}

  {#if users}
    <List strong outlineIos dividersIos insetMd>
      {#each users as user}
        <ListItem title={user} />
      {/each}
    </List>
  {/if}
</Page>