Vuex 快速使用

2019-04-12  本文已影响0人  大前端艺术家

本文对 Vuex 官方文档重新组织编排,希望正在学习 Vue 的同学们,在阅读后可快速使用 Vuex。

开始使用 Vuex,把状态拿到应用外部管理,Vuex 管这个管理状态的玩意叫 Store,一个完全独立的应用,他只负责状态管理。尝试把 Vuex 应用和 Vue 应用划清界限,

Vuex 应用

所谓状态管理,无非就是定义状态,修改状态

定义 state

在 Vuex 里定义状态,我们需要 new 一个 Store 出来,每一个 Vuex 应用的核心就是 store(仓库)。

// store.js
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    count: 0
  }
});

export default store;

以上代码创建了一个 store,store.state 里定义了状态。与在 Vue 里定义 data 没有任何区别,

下面修改状态。

直接修改

如果你想快点用上 Vuex,你可以在组件里直接修改 store 里的 state,(直接修改的意思就是,用点操作修改)

state.count = 2;

虽然这样可以正常工作,但在严格模式下会报错,更改 store 中的状态的唯一方法应该是提交 mutation

严格模式

开启严格模式,仅需在创建 store 的时候传入 strict: true

const store = new Vuex.Store({
  strict: true
});

在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。

mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。

Vuex.Store的构造器选项中,有一个 mutation 选项,这个选项就像是事件注册:key 是一个字符串表示 mutation 的类型(type),value 是一个回调函数(handler),

这个回调函数就是我们实际进行状态更改的地方,它有两个入参,第一个参数是 state,就是 store 里的 state;第二个参数是 Payload,这是提交 mutation 时候额外传入的参数。

定义 mutation

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment(state) {
      state.count++; // 变更状态
    }
  }
});

提交 mutation

上面定义了 mutation,要唤醒一个 mutation handler,唯一的接口是store.commit,如果你熟悉事件监听,commit 就类似于 Vue 的$emit,jquery 的trigger

可想而知,commit 方法传参必须至少有一个能区分 mutation 的唯一标识,这个标识就是 type

commit 参数是对象

当 commit 的参数是一个对象的时候,对象里必须要有 type 字段,除了 type 字段,你还可以添加额外任意字段为载荷(payload)。

// 对象风格的提交方式
store.commit({
  type: "increment",
  amount: 10
});

type 做第一参数

把 type 和 payload 分开也是个不错的选择,可以把 type 单独拿出来当第一个参数,以commit(type,[payload])的形式提交,官方称此为以载荷形式提交

store.commit("increment");
store.commit("increment", 10);
store.commit("increment", { count: 2 });

在大多数情况下,payload 应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读。

到这里我们已经知道如何定义 mutation 和提交 mutation 了,commit 接口很简单,但在哪里使用呢?有两个地方用

Action

状态管理不过就是定义 state 和修改 state,mutation 已经可以修改 state 了,为什么还需要 action?

同步和异步

在 Vuex 中,mutation 都是同步事务,在 mutation 中混合异步调用会导致你的程序很难调试。

例如,当你调用了两个包含异步回调的 mutation 来改变状态,你怎么知道什么时候回调和哪个先回调呢?这就是为什么我们要区分这两个概念。

Action 确实和 mutation 很类似,不同在于:

注册 action:

注册 action 就跟定义 mutation 一样,除了 handler 的入参不同。

Action 函数的入参是一个与 store 实例具有相同方法和属性的 context 对象。因此可以

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    increment(context) {
      context.commit("increment");
    }
  }
});

分发 Action

Action 通过 store.dispatch方法触发:

// 以载荷形式分发
store.dispatch("incrementAsync", {
  amount: 10
});

// 以对象形式分发
store.dispatch({
  type: "incrementAsync",
  amount: 10
});

组合 Action

store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise,因此,通过 async / await,很方便控制流程

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

表单处理

表单的问题在于使用v-model时候,v-model会试图直接修改 state,而 Vuex 在严格模式下是不允许直接修改 state 的。

很容易解决,只要我们把修改 state 的行为按 Vuex 的要求以commit mutation 方式修改即可。

有两种方式。

用“Vuex 的思维”解决

抛弃v-model指令,自己去实现v-model双向绑定,非常简单,

<input :value="message" @input="updateMessage" />
computed: {
  ...mapState({
    message: state => state.obj.message
  })
},

methods: {
  updateMessage (e) {
    this.$store.commit('updateMessage', e.target.value)
  }
}

store 中的 mutation 函数:

mutations: {
  updateMessage (state, message) {
    state.obj.message = message
  }
}

用 Vue 计算属性解决

如果坚持使用v-model,可以在 Vue 里对v-model绑定的计算属性设置 set 行为。

<input v-model="message" />
computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}

使用 Vuex 进行状态管理,到此结束。

从 flux 架构来看,三个核心,StateMutationAction,已经足够了,总结一下其实很简单,同步 commit mutation,异步 dispatch action

核心原则

记住这些原则,开始动手把 Vuex 集成到 Vue 项目中吧,至于一些更多的概念,都是些锦上添花的东西。

Vue 应用使用 Vuex

Vuex 的常用 api 上面都涉及到了,使用思路就是

现在状态管理的部分已经全部由 store 实例去管理了,如何在 Vue 组件中使用 store,非常简单,一点也不神奇。

store.js文件里我们创建了 store 实例并将其导出了(export),按照 js 模块化的知识,我们只要在需要的地方导入它就可以直接使用了。

组件引入 store

可以在需要的组件里直接引入,就像引入一个普通的 js 一样。

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

组件中引入了 store,就可以直接通过store.state引用 state,以及直接使用 store 实例简洁的 api,真的就是这么简单。

store.state.count = 2; // 直接修改,并不建议
store.commit(); // 在 store 中调用 mutation
store.dispatch("increment"); // 在 store 中分发 action

我们往往需要在 Vue 组件的template中展示状态,由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态:

import store from "path/to/store.js";
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count() {
      return store.state.count;
    }
  }
};

每当 store.state.count 变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM。

其实到现在为止,对于在 Vue 中使用 Vuex 就已经足够了。下面的东西其实可以不用看了,但 Vuex 还是提供了一些方便我们使用的方式,锦上添花。

组件中引入的方式,缺点:这种模式导致组件依赖全局状态单例。在模块化的构建系统中,在每个需要使用 state 的组件中需要频繁地导入。

全局引入 store

Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中:

// app.js
import Vue from "vue";

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

const app = new Vue({
  store // 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
});

通过在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store 访问到。这样我们就不用每个组件单独引入了。

this.$store.state.count = 2; // 直接修改,并不建议
this.$store.commit("", {}); // 直接在组件中提交 mutation
this.$store.dispatch("increment"); // 在组件中分发 action

组件绑定的辅助函数

既然是辅助,就不是必须的东西,辅助函数可以方便的把 Vuex 中的 state,getter,mutation,action 映射到组件,方便调用。

更多概念

Getter

如果你很喜欢 Vue 提供的计算属性(computed),Vuex 允许在 store 中定义“getter”(可以认为是 store 的计算属性)。

但对状态管理来说,Getter 其实并不是必须的,如果你需要的话,可以查看文档使用。

Module

模块化并不是状态管理的概念,也不是必须的,但如果应用十分复杂,将 store 分割成模块(module)会是一个必经之路,Vuex 提供了模块化选项,需要可查看文档。

上一篇 下一篇

猜你喜欢

热点阅读