Store

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

你可以使用库特定的状态管理库,例如 Vue 的 Vuex、React 的 Redux 以及内置的 Svelte 存储功能。但如果需要简单的东西,那么 Framework7 存储可能是一个不错的选择。

创建存储

首先我们需要创建存储。让我们为此创建单独的 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 - object. 具有存储参数的对象

方法返回创建的存储实例

存储参数

现在,让我们看看 storeParameters 对象

状态

state 是包含你所有应用程序级别状态的单一对象,并作为“唯一真实来源”。这也意味着通常每个应用程序只有一个存储。一个单独的状态树使得定位特定状态片很容易,并允许我们轻松获取当前应用程序状态的快照,以便于调试。

行动

actions 用于修改状态、异步操作或调用其他存储操作。操作处理程序接收一个带有存储状态和调度方法以调用其他操作的上下文对象。所以你可以访问 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];

获取器

获取器处理程序用于返回存储库状态中的数据。当我们需要根据存储库状态计算导出状态时,此处理程序也很方便,例如通过一系列项进行筛选

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 组件

<template>
  <!-- pass store to the App's "store" prop -->
  <f7-app store={store}>
    <f7-view main>
      <!-- ... -->
    </f7-view>
  </f7-app>
</template>
<script>
// import our store
import store from 'path/to/store.js';

export default {
  setup() {
    return {
      store,
    }
  }
}
</script>

访问存储库和状态

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

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

console.log(store.state.users);

或者通过访问 Framework7 实例的“存储库”属性

import { f7 } from 'framework7-vue';

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,否则使用获取器。

在 Vue 组件中使用

有一个特殊的useStore辅助函数,可用于 Vue 组件中以保持存储库的响应性(在状态/获取器值更改时自动更新组件)。

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

  • getterName - 字符串 - 获取器处理程序的名称

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

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

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

  • store - 存储库实例 - 要从中查找获取器的存储库实例。如果未指定,则将使用传递给<App>的默认存储库。
  • getterName - 字符串 - 获取器处理程序的名称

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

如果我们有以下存储库

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

那么,例如,我们应该在 Vue 组件中使用以下内容

<template>
  <f7-page>
    <f7-list>
      <f7-list-item v-for="user in users" :title="user.name" />
    </f7-list>
  </f7-page>
</template>
<script>
  import { onMounted } from 'vue';
  // import special useStore helper/hook
  import { useStore } from 'framework7-vue';
  // import store
  import store from 'path/to/store.js'

  export default {
    setup() {
      // retrieve "users" getter handler value. Initially empty array
      const users = useStore('users');

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

      return {
        users,
      }
    }
  }
</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.vue
<template>
  <f7-page>
    <f7-navbar title="Store" />
    <f7-block strong outline-ios inset-md>
      <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>
    </f7-block>
    <f7-block v-if="!users" class="text-align-center">
      <f7-button v-if="!loading" fill round @click="load">Load Users</f7-button>
      <f7-preloader v-else />
    </f7-block>
    <f7-list v-if="users" strong outline-ios dividers-ios inset-md>
      <f7-list-item v-for="user in users" :key="user" :title="user" />
    </f7-list>
  </f7-page>
</template>
<script>
import {
  f7,
  useStore,
  f7Page,
  f7Navbar,
  f7Block,
  f7Button,
  f7Preloader,
  f7List,
  f7ListItem,
} from 'framework7-vue';

export default {
  components: {
    f7Page,
    f7Navbar,
    f7Block,
    f7Button,
    f7Preloader,
    f7List,
    f7ListItem,
  },
  setup() {
    // Subscribe to store getters
    const users = useStore('users');
    const loading = useStore('usersLoading');

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

    return {
      users,
      loading,
      load,
    };
  },
};
</script>