Vue 组件之间有很多种通信方式,例如 propsref$emit 等。但是这些通信方式都有一个问题就是一次只能和一个组件通信,如果要把值传给多个组件就需要写多行代码,而且对于层级较多的组件来说需要传很多次,不太方便。

Vuex 是 Vue 的一个状态管理插件,它能实现让多个组件共享同一份数据,如果某个组件更改了数据其它组件也能响应数据变化。

安装 & 引入

使用 npm 安装:

npm install vuex --save

main.js 中引入:

import Vuex from 'vuex';  //  引入 Vuex
vue.use(Vuex);  //  注册 Vuex

编写 Store

每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。

下面编写一个简单的 Store:

const store = new Vuex.Store({
  state: {
    content: 'Hello Vuex'
  },
  mutations: {
    chContent(state) {
      state.content = 'Hello World';
    }
  }
});

上面的 state 中的 content 就是状态,mutations 中的 chContent 就是改变状态的方法,如果调用 chContentstate 中的 content 就会被更改为 Hello World

为了让其它组件能够访问 Store,在实例化 Vue 的时候需要传入 Store,如下:

const store = new Vuex.Store({
  state: {
    content: 'Hello Vuex'
  },
  mutations: {
    chContent(state) {
      state.content = 'Hello World';
    }
  }
});

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

Store 对象你也可以放到单独的文件中,我上面为了方便,直接放到了 main.js 中。

获取状态

下面在组件中获取 Store 的状态:

<template>
  <div id="app">
    <h1>{{$store.state.content}}</h1>
  </div>
</template>

上面在 h1 中输出了 content

下面通过函数在控制台中输出 content

export default {
  name: 'App',
  created() {
    console.log(this.$store.state.content);
  }
}

更改状态

上面 Store 的 mutations 中有一个 chContent 方法可以把 content 更改为 Hello World,下面就调用 chContent 方法:

this.$store.commit('chContent');

使用 $store.commit 方法可以调用 mutations 中的方法来更改状态,第一个参数 type 就是方法名称。

如果要动态改变内容,在编写 Store 的时候可以给 mutations 中的方法加第二个参数,如下:

const store = new Vuex.Store({
  state: {
    content: 'Hello Vuex'
  },
  mutations: {
    chContent(state, content) {
      state.content = content;
    }
  }
});

在调用 $store.commit 更改状态的时候也可以传入第二个参数,如下:

this.$store.commit('chContent', '更改的内容');

第二个参数就是需要动态改变的内容。

如果需要接收多个字段可以传入对象,而不是多个参数,如下:

const store = new Vuex.Store({
  state: {
    userName: '',
    login: false
  },
  mutations: {
    chContent(state, user) {
      state.userName = user.userName;
      state.login = user.login;
    }
  }
});
this.$store.commit('chContent', {
  userName: 'Jack',
  login: true
});

$store.commit 也可以使用对象语法提交状态更改,上面的 Store 不变,下面使用对象语法更改状态:

this.$store.commit({
  type: 'chContent',
  userName: 'Mark',
  login: true
});

type 的值就是 mutations 的方法名称。

Action

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

下面是一个简单的 Action:

const store = new Vuex.Store({
  state: {
    content: 'Hello Vuex'
  },
  mutations: {
    chContent(state) {
      state.content = 'Hello World';
    }
  },
  actions: {
    chContent(context) {
      context.commit('chContent');
    }
  }
});

actions 的方法可以接收一个 context,可以通过 context 提交 commit 来调用 mutations 中的方法。

下面调用 actions 中的 chContent 来更改 state 中的 content

this.$store.dispatch('chContent');

使用 context.state 也可以获取 state 的状态,下面在 actions 中编写一个 getContent 方法来获取状态:

const store = new Vuex.Store({
  state: {
    content: 'Hello Vuex'
  },
  mutations: {
    chContent(state) {
      state.content = 'Hello World';
    }
  },
  actions: {
    getContent(context) {
      return context.state.content;
    }
  }
});

下面调用 actions 中的 getContent 来获取 state 中的 content

const content = this.$store.dispatch('getContent');
//  返回一个 Promise
content.then(value => {
  console.log(value);  //  在控制台输出
});

actions 中的方法也可以接收第二个参数,如下:

const store = new Vuex.Store({
  state: {
    content: 'Hello Vuex'
  },
  mutations: {
    chContent(state, text) {
      state.content = text;
    }
  },
  actions: {
    chContent(context, text) {
      context.commit('chContent', text);
    }
  }
});
this.$store.dispatch('chContent', '更改的内容');

上面说过 Action 可以包含异步操作,下面就在 actionschContent 方法中设置一个 setTimeout 来延迟调用 mutations 中的 chContent

const store = new Vuex.Store({
  state: {
    content: 'Hello Vuex'
  },
  mutations: {
    chContent(state, text) {
      state.content = text;
    }
  },
  actions: {
    chContent(context, text) {
      setTimeout(() => {
        context.commit('chContent', text)
      }, 3000);
    }
  }
});
this.$store.dispatch('chContent', '更改的内容');

3 秒后 mutations 中的 chContent 才会被调用。

Module

对于复杂的大型项目来说,如果把所有的状态都写到一个 store 对象中,可能会导致 store 对象的内容过多,不太好维护。

Vuex 允许将 store 分割成 模块 (module),每个模块拥有自己的 state、mutation、action、getter。

下面是两个 store 模块:

//  模块A的 store
const moduleA = {
  state: () => {
    return {text: '这里是模块A'};
  },
  mutations: {
    chText(state) {
      state.text = '模块A';
    }
  },
  actions: {
    chText(context) {
      //  延迟 3 秒调用 mutations 的 chText
      setTimeout(() => {
        context.commit('chText');
      }, 3000);
    }
  }
};
//  模块B的 store
const moduleB = {
  state: () => {
    return {text: '这里是模块B'};
  },
  mutations: {
    chText(state) {
      state.text = '模块B';
    }
  }
};

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
});

在组件中调用这两个模块:

<template>
  <div id="app">
    <h1>{{$store.state.a.text}}</h1>
    <h1>{{$store.state.b.text}}</h1>
  </div>
</template>

相关文章: