store存储

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

您可以使用特定于库的状态管理库,如Vue的Vuex,React的Redux,并使用内置的Svelte存储功能。但是,如果需要一些简单的东西,那么Framework7 Store可能是一个很好的选择。

Create Store

首先,我们需要创建存储器。让我们为此创建一个单独的store.js文件:

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

// 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: {
    // 上下文对象包含存储状态,将作为参数传递。
    users({ state }) {
      return state.users;
    }
  }

})

// export store
export default store;

如果您不使用模块,那么store.js文件将如下所示:

// 将store保存为全局对象
window.store = Framework7.createStore({
  state: { /* ... */ },
  actions: { /* ... */ },
  getters: { /* ... */ },
})

在这个例子中,我们使用了以下API函数:

createStore(storeParameters)- 创建 store

  • storeParameters - object. 包含store参数的对象

方法返回创建的store实例

Store Parameters

现在,让我们看一下storeParameters对象:

State

state是一个包含所有应用级状态的单一对象,它作为"单一数据源"。这也意味着通常每个应用只有一个store。一个单一的状态树使得定位特定的状态片段变得简单,并且允许我们轻松地对当前应用状态进行快照以进行调试。

Actions

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

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

为了保持store的响应性,状态修改应该使用赋值操作。例如:

// 修改当前状态属性 - 非响应式 not reactive
state.users.push(...users);

// 赋值给新值 - 响应式 reactive
state.users = [...state.users, ...users];

Getters

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

Getter 处理程序也接收一个上下文对象,但只有存储状态。例如,无法从 getter 中调用其他操作。

Use Store

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

首先,我们需要将创建的存储传递给主应用程序实例:

//导入我们的存储
import store from 'path/to/store.js';

const app = new Framework7({
  // 将存储传递给应用程序的 "store" 参数
  store,
  ...
})

Access Store & State

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

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

console.log(store.state.users);

或者通过访问Framework7实例的store属性:

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

const app = new Framework7({
  store,
  ...
})

// somewhere later
console.log(app.store.state.users);

Dispatching Actions

要调用一个操作,我们需要使用store.dispatch方法并传入要调用的操作的名称。

如果我们有以下存储操作:

const store = createStore({
  // ...
  actions: {
    // 处理程序在第二个参数中接收自定义数据
    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';

  // 调用'getUsers'操作
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);
        })
    },
  },
  // ...
});

Getters

获取器的值可以作为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;

获取器的值是一个静态对象,其中包含获取器处理程序的结果,因此:

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

与状态不同,获取器是用于响应式的。因此,当您不需要任何响应性时,可以直接访问store.state,否则请使用获取器。

Usage With Router Components

路由组件上下文具有$store属性,其中包含以下属性:

如果我们有以下存储:

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

然后,在路由组件中,我们应该使用以下内容:

<template>
  <div class="page">
    <ul>
      <!-- getter has value in ".value" property -->
      ${users.value.map((user) => $h`
        <li>${user.name}</li>
      `)}
    </ul>
  </div>
</template>
<script>
  export default (props, { $store, $on }) => {
    // retrieve "users" getter handler value. Initially empty array
    const users = $store.getters('users');

    $on('pageInit', () => {
      // load users on page init
      $store.dispatch('getUsers');
    });

    return $render;
  }
</script>

Examples

import { createStore } from 'store';

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_page.f7.html
<template>
  <div class="page">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="title">Store</div>
      </div>
    </div>
    <div class="page-content">
      ${users.value.length > 0 ? $h`
      <div class="list list-strong list-outline-ios list-dividers-ios inset-md">
        <ul>
          ${users.value.map((user) => $h`
          <li class="item-content">
            <div class="item-inner">
              <div class="item-title">${user}</div>
            </div>
          </li>
          `)}
        </ul>
      </div>
      ` : $h`
      <div class="block block-strong block-outline-ios inset-md">
        <a href="#" class="button button-fill button-round button-preloader ${loading.value ? 'button-loading' : ''}"
          @click=${loadUsers}>
          <span class="preloader"></span>
          <span>Load Users</span>
        </a>
      </div>
      `}
    </div>
  </div>
</template>
<script>
  export default (props, { $store }) => {
    const loading = $store.getters.loading;
    const users = $store.getters.users;

    const loadUsers = () => {
      $store.dispatch('getUsers');
    };

    return $render;
  };
</script>