存储

Framework7 带有一个内置的轻量级应用程序状态管理库 - Store。它充当应用程序中所有组件的集中式存储。

您可以使用特定于库的状态管理库,如 Vuex 用于 Vue,Redux 用于 React,以及使用内置的 Svelte 存储功能。但如果需要简单的东西,那么 Framework7 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)- 创建存储

  • storeParameters - 对象. 包含存储参数的对象

方法返回创建的存储实例

存储参数

现在,让我们看看 storeParameters 对象

状态

state 是包含所有应用程序级状态的单个对象,并充当“单一事实来源”。这也意味着通常每个应用程序只有一个存储。单个状态树使定位特定状态变得直观,并允许我们轻松地为调试目的获取当前应用程序状态的快照。

操作

actions 用于修改状态,进行异步操作,或调用其他存储操作。操作处理程序接收一个包含存储状态和 dispatch 方法的上下文对象,用于调用其他操作。因此,您可以访问 context.store 来访问状态,或使用 context.dispatch 调用其他操作。

作为第二个参数,操作处理程序可以接收任何自定义数据。

为了保持存储的反应性,状态修改应该通过赋值来完成。例如

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

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

获取器

getters 处理程序用于从存储状态中返回数据。当我们需要根据存储状态计算派生状态时也很方便,例如过滤项目列表

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);
    }
  }
})

获取器处理程序也接收一个上下文对象,但只包含存储状态。例如,从获取器中调用其他操作是不可能的。

使用存储

现在我们创建了存储,让我们了解如何使用它。

首先,我们需要将创建的存储传递给主 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>

访问存储和状态

可以通过引用我们创建的存储实例来直接访问存储(及其状态)

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 方法。

如果我们有以下存储操作

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;

获取器值是包含 .value 属性的静态对象,该属性包含获取器处理程序的结果,因此

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

获取器与状态不同,它们旨在具有反应性。因此,当您不需要任何反应性时,您可以直接访问 store.state,否则使用获取器。

与 Svelte 组件一起使用

有一个特殊的 useStore 帮助程序用于在 Svelte 组件中使用,以保持存储的反应性(当状态/获取器值更改时自动更新组件)。

useStore(getterName, callback)- 直接返回获取器值并订阅状态更新

  • getterName - 字符串 - 获取器处理程序的名称
  • callback - 函数 - 当相关状态发生更改时,将使用新的获取器值触发回调函数。

方法返回获取器处理程序值

如果我们需要从另一个存储实例中获取获取器值,那么我们还需要传递存储

useStore(store, getterName, callback)- 直接返回获取器值并订阅状态更新

  • store - 存储实例 - 要从中查找获取器的存储实例。如果未指定,则将使用传递给 <App> 组件的默认存储。
  • getterName - 字符串 - 获取器处理程序的名称
  • callback - 函数 - 当相关状态发生更改时,将使用新的获取器值触发回调函数。

方法返回获取器处理程序值

如果我们有以下存储

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 帮助程序/钩子,所以当用户加载时,组件将自动更新。

示例

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>