vue 开发中,组件通信一直是一大痛点。

当项目是很简单的 SPA 或者多入口项目时,可以靠着 vue 自带的 prop/$emit 进行组件通信;规模再大一些,可以搭配使用 bus 总线进行兄弟组件通信;项目再大一些,出现更复杂的组件关系时,复杂的组件通信可以让你写得怀疑人生。

万幸的是, vue 官方出品了 vuex ,通过全局式的状态管理,解决了这一痛点。

虽然 vuex 很好用,但是,很多小伙伴和我吐槽 vuex 的文档和 vue-ssr 的文档一样,让人看得一脸懵逼。

好吧,下面就让我来带着大家一起入门 vuex

安装并引入

正常情况下,我们使用 vue-cli3 生成项目时,可以选择集成 vuex 到项目中。此时, vue-cli3 会自动安装 vuex ,并在 src 文件夹下生成 store.js 完成 vuex 的引入和配置。

但是,很多同学并没有使用 vue-cli3 或者生成项目时没有选择集成 vuex 。此时,就只能手动安装并引入 vuex 了。

安装

由于 vuex 是用于全局状态管理的,所以,它不仅仅作用于开发环境,而且还要用于生产环境。

显而易见,安装 vuex 应该使用 -S--save 命令。

npm install vuex -S

引入

类似于 vue-cli3 生成的项目,我们在 src 文件夹下新建 store.js ,并在其中写入:

// store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({})

然后,我们只需要在 vue 实例中引入 store.js 中的 Vuex.Store 实例即可:

// main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

new Vue({
  router,
  // 引入store
  store,
  render: h => h(App)
}).$mount('#app')

Vuex的使用

完成了 vuex 的安装和引入,接下来我们进入 Vuex 的使用。

vuex 中有三要素: statemutation 以及 action 。它们之间的关系可以用官网那张著名的图来表示:

Vuex入门-LMLPHP

State

简单来说, state 表示状态,类似于 vue 中的 data (其实本质上就是差不多的, vuexvuebeforeCreate 钩子中将 state 混入进 data)。但是,它们又有很大的不同: 在使用者看来, state 是全局的,这得益于 vuex 的设计理念——单一状态树。这些我将在后几篇文章中详细,现在我们只需要知道 state 是类似于全局下的 data

接下来我们通过一个简单例子来感受下 state

首先,我们需要修改 store.js 文件,配置 state 。可以看到,我们在生成 Vuex.Store 实例时传入了实例化选项对象,对象包含一个 state 属性, state 对象的属性就是我们定义的全局状态。

此时,我们定义了一个全局状态——count ,并将其的初始值设为1

// store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  // 添加state
  state: {
    count: 1
  }
})

接下来,我们需要在组件中引用 count,由于它是全局状态,我们可以在任何一个组件中使用。为了展示其威力,我们在两个不同的组件中使用它。

首先我们在 App.vue 中使用它:

在模板中,我们使用 $store.state.count 引入该全局状态,没错,使用它就是那么简单,只需要 以 $store.state.key 的形式调用。

// App.vue
<template>
  <div id="app">
    <div id="nav">
      {{$store.state.count}}
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view/>
  </div>
</template>

可以发现, Home 前多出了一个 1 ,这代表着我们成功引入了全局状态 count

接下来我们在 Home.vue 的子组件 HelloWorld.vue 中引入 count

相同的引用方式: $store.state.count

// HelloWorld.vue
<template>
  <div class="hello">
    {{$store.state.count}}
  </div>
</template>

可以发现,页面中又多出了一个 1 ,代表着我们又一次引用成功。现在,是不是已经感受到了 vuex 的威力?

Mutation

但是,上面的示例有个问题,那就是全局状态是静态的。如果在实际应用场景中,一般来说,会经常更改状态。

有的同学会说,我们直接在方法中修改 this.$store.state.key 的值不就行了吗?

不好意思,当然是不行的。

statedata 的另一大区别在于,你不能直接改变 state 。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

简而言之,我们把 mutation 当做接收 state 作为参数并修改 state 的自定义事件即可,上一段所说的 commit 就是触发 mutaion 这个自定义事件的方法。

光说不练假把式,接下来,我们对为 vuex 添加上 mutation ,实现 state 的动态改变:

首先,当然是修改生成 Vuex.Store 示例的选项对象,为其添加 mutations

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 1
  },
  // 添加mutation
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

在上面的代码中,我们添加了一个名为 incrementmutation 。完成了自定义事件,接下来,我们只需要在组件中对 mutation 进行触发即可。

我们在 HelloWorld.vue 添加一个按钮,每次点击触发一次 increment 这个 mutation 。可以发现,触发方式很简单,只需要调用 store 自带的 commit 方法,其中参数为需要触发的 mutation 的名称。

// HelloWorld.vue
<template>
  <div class="hello">
    <div>{{$store.state.count}}</div>
    <button @click="$store.commit('increment')">修改count</button>
  </div>
</template>

点击页面中的按钮,你会发现,页面中的两个 count 都同时增加了1,说明我们成功实现了 state 的动态修改。

Action

action 类似于 mutation ,也相当于一种自定义事件。只不过, action 操作的是 mutation 而不是 state

添加 action 的方法类似,在选项对象中新增 action 属性即可。与 mutation 的参数不同, action 的参数就是当前创建的 Vue.store 对象实例的上下文,一般将其命名为 context 。我们需要使用其自带的 commit 方法来触发 mutation

下面我通过实际的例子来尝试下 action

首先,修改选项对象,使得新添加的 action 可以触发之前的 mutation

// store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment ({ commit }) {
      commit('increment')
    }
  }
})

由于我们一般来说仅仅需要 context 中的 commit 方法,所以可以采用解构的方式,直接调用 commit 方法,而不需要以 context.commit 的方式使用它。

接下来,只需要修改 HelloWorld.vue ,使其能够在点击按钮时触发即可。

action 的触发方式和 mutation 类似,只不过调用的方法是 dispatch

// HelloWorld.vue
<template>
  <div class="hello">
    <div>{{$store.state.count}}</div>
    <button @click="$store.dispatch('increment')">修改count</button>
  </div>
</template>

点击页面按钮,你会发现,实现了和之前相同的效果。

总结

学会了 vuex 三贱客: statemutationaction ,我们再回过头看看前面的那张关系图,此时应该很容易理解了吧?

组件交互触发 action , 在 action 中进行异步操作(可选)并触发 mutationmutation 控制 state 的变动, state 修改之后,触发响应式,重新渲染组件。

彩蛋

在官方文档中,提到需要将异步操作放入 action 中,而不能放在 mutation 中。实际上,在 mutation 中也可以进行异步操作,而且也不会导致什么奇怪的事情。

但是,既然官方文档中这么说了,我们在实际开发中,一般还是老老实实地将所有异步操作放在 action 中。(猥琐保平安 -_-)

另外,说了这么多,其实在一些项目中,可以使用 provide/inject 代替 vuex 。具体的用法在此不再赘述,就当一个课后作业吧。

在项目中灵活使用 provide/inject ,有时可以起到出乎意料的作用哦。

最后的最后

篇幅有限,所以该篇文章只讲述了 vuex 三贱客的基本用法,其他的进阶用法,如: gettermodule , 简写以及 vuex 项目结构优化,甚至 vuex 源码解析将会在之后的文章一一讲解。

如果您觉得这片文章不错的话,不如给我的 gayhub 点个star再走呗。

这个项目个人认为对很多新手在实际开发中使用 vuex 还是很有启发和帮助的(手动狗头)。

欢迎交流,谢谢~

12-26 06:55