Vuex 概述
官方文档:https://vuex.vuejs.org/zh/
- Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
- Vue 应用中的每个组件在 data() 中封装着自己数据属性,而这些 data 属性都是私有的,完全隔离的。
- 如果我们希望多个组件都能读取到同一状态数据属性,或者不同组件的行为需要更新同一状态数据属性,
- 这就需要一个将共享的状态数据属性进行集中式的管理。
- 这就是 Vuex 状态管理所要解决的问题。
简单使用
使用vue-cli创建一个项目,命名为vue-demo,安装依赖和运行
# 进入工程目录 cd vuex-demo
# 安装依赖 npm install --save vuex
# 运行项目 npm run serve
访问 http://localhost:8080/,正常访问就表示配置的没有问题
每一个 Vuex 项目的核心就是 store(仓库)。 store 就是一个对象,它包含着你的项目中大部分的状态(state)。
state 是 store 对象中的一个选项,是 Vuex 管理的状态对象(共享的数据属性)
在 src 目录下创建 store 目录,store 下创建 index.js 文件, 编码如下:
import Vue from 'vue' import Vuex from 'vuex' // 引用vuex插件 Vue.use(Vuex) const store = new Vuex.Store({ //注意V和S都是大写 // 存放状态(共享属性) state:{ count: 1 } }) export default store
修改 main.js,导入和注册 store,编码如下:
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, // 注册 render: h => h(App) }).$mount("#app");
组件中读取 state 状态数据,修改 srcviewsHome.vue,编码如下:
<template> <div> count: {{ $store.state.count }} </div> </template> <script> </script>
访问 http://localhost:8080/ 页面显示如下
改变状态值 mutation
在 store 的 mutations 选项中定义方法,才可以改变状态值。在通过 $store.commit('mutationName') 触发状态值的改变
修改 store/index.js , 在 store 中添加 mutations 选项,编码如下:
import Vue from 'vue' import Vuex from 'vuex' // 引用vuex插件 Vue.use(Vuex) const store = new Vuex.Store({ //注意V和S都是大写 // 存放状态(共享属性) state:{ count: 1 }, // 改变state状态 mutations:{ // 加法 increment(state){ state.count++ }, // 减法 decrement(state){ state.count-- } } }) export default store
修改 srcviewsHome.vue ,调用 mutations 中 increment、decrement 方法
<template> <div> count: {{ $store.state.count }} <button @click="addCount">加法</button> <button @click="decrement">减法</button> </div> </template> <script> export default{ methods: { addCount() { // 获取状态值 console.log(this.$store.state.count) // 通过commit 调用 mutations 中的 increment 改变状态值 this.$store.commit('increment') }, decrement() { console.log(this.$store.state.count) // 通过commit 调用 mutations 中的 decrement 改变状态值 this.$store.commit('decrement') } }, } </script>
点击 加法 按钮,控制台和页面显示数字变化
载荷
你可以向 $store.commit 传入额外的参数,即 mutation 的 载荷(payload):
修改 srcstore 下的 index.js
...... // 改变state状态 mutations:{ increment(state, n){ // n 为载荷 state.count+=n // state.count=state.count+n }, decrement(state){ state.count-- } } })
修改 viewsHome.vue 组件
<template> <div> count: {{ $store.state.count }} <button @click="addCount">加法</button> <button @click="decrement">减法</button> </div> </template> <script> export default{ methods: { addCount() { // 获取状态值 console.log(this.$store.state.count) // 通过commit 调用 mutations 中的 increment 改变状态值 this.$store.commit('increment',5) // 提交载荷 }, decrement() { console.log(this.$store.state.count) // 通过commit 调用 mutations 中的 decrement 改变状态值 this.$store.commit('decrement') } }, } </script>
页面上点击加法会一次加5,减法还是一个一个的减
Action
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是在组件中直接变更状态, 通过它间接更新 state。
- 在组件中通过 this.$store.dispatch('actionName') 触发状态值间接改变
- Action 也支持载荷
- Action 可以包含任意异步操作。
修改 store/index.js ,增加 actions 选项
import Vue from 'vue' import Vuex from 'vuex' // 引用vuex插件 Vue.use(Vuex) const store = new Vuex.Store({ //注意V和S都是大写 // 存放状态(共享属性) state:{ count: 1 }, // 改变state状态 mutations:{ increment(state, n){ // n 为载荷 state.count+=n // state.count=state.count+n }, decrement(state){ state.count-- } }, actions: { add(context, n) { // 触发 mutations 中的 increment 改变 state context.commit('increment', n) }, decrement({commit, state}) { // 按需传值 commit('decrement') } } }) export default store
修改 viewsHome.vue, 触发 action 改变值
<template> <div> count: {{ $store.state.count }} <button @click="addCount">加法</button> <button @click="decrement">减法</button> </div> </template> <script> export default{ methods: { addCount() { // 获取状态值 console.log(this.$store.state.count) // 通过commit 调用 mutations 中的 increment 改变状态值 // this.$store.commit('increment') // this.$store.commit('increment', 10) // 提交载荷 // 触发 actions 中的 add 改变状态值 this.$store.dispatch('add', 10) }, decrement() { console.log(this.$store.state.count) // 通过commit 调用 mutations 中的 decrement 改变状态值 // this.$store.commit('decrement') this.$store.dispatch('decrement') } }, } </script>
派生属性 getter
有时候我们需要从 store 中的 state 中派生出一些状态。
例如:基于上面代码,增加一个 desc 属性,当 count 值小于 50,则 desc 值为 吃饭 ; 大于等于 50 小于100,则desc 值为 睡觉 ; 大于100 , 则 desc 值为 打豆豆 。这时我们就需要用到 getter 为我们解决。
getter 其实就类似于计算属性(get)的对象
组件中读取 $store.getters.xxx
修改 storeindex.js ,增加 getters 选项
getters 中接受 state 作为其第一个参数,也可以接受其他 getter 作为第二个参数
import Vue from 'vue' import Vuex from 'vuex' // 引用vuex插件 Vue.use(Vuex) const store = new Vuex.Store({ //注意V和S都是大写 // 存放状态(共享属性) state:{ count: 1 }, // 改变state状态 mutations:{ increment(state, n){ // n 为载荷 state.count+=n // state.count=state.count+n }, decrement(state){ state.count-- } }, actions: { add(context, n) { // 触发 mutations 中的 increment 改变 state context.commit('increment', n) }, decrement({commit, state}) { // 按需传值 commit('decrement') } }, //派生属性 getters: { desc(state) { if(state.count < 50) { return '吃饭' }else if(state.count < 100) { return '睡觉' }else { return '打豆豆' } } } }) export default store
修改 viewsHome.vue, 显示派生属性的值
<template> <div> count: {{ $store.state.count }} <button @click="addCount">加法</button> <button @click="decrement">减法</button> 派生属性desc: {{ $store.getters.desc }} </div> </template>
点击 Home 页面的 触发改变 按钮,当 count 新增到 51 , desc 是否会显示为 睡觉
Module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter 等,参见以下代码模型
const moduleA = { state: { ... }, mutations: { ... }, actions: { ... }, getters: { ... } } const moduleB = { state: { ... }, mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } }) store.state.a // -> moduleA 的状态 store.state.b // -> moduleB 的状态
修改 storeindex.js
import Vue from 'vue' import Vuex from 'vuex' // 引入 Vuex 插件 Vue.use(Vuex) const home = { // 存放状态(共享属性) state: { count: 1 }, //派生属性 getters: { desc(state) { if(state.count < 50) { return '吃饭' }else if(state.count < 100) { return '睡觉' }else { return '打豆豆' } } }, // 改变 state 状态 mutations: { increment(state, n) { // n 为载荷 // state.count ++ state.count += n }, decrement(state) { state.count -- } }, actions: { add(context) { // 触发 mutations 中的 increment 改变 state context.commit('increment', 10) }, decrement({commit, state}) { // 按需传值 commit('decrement') } } } const store = new Vuex.Store({ // 注意V 和 S都是大写字母 modules: { home // home: home } }) export default store
修改 Home.vue
<template> <div> <!--修改部分--> count: {{ $store.state.home.count }} <button @click="addCount">加法</button> <button @click="decrement">减法</button> 派生属性desc: {{ $store.getters.desc }} </div> </template> <script> export default { methods: { addCount() { console.log(this.$store.state.home.count) this.$store.dispatch('add', 10) }, decrement(){ this.$store.dispatch('decrement') } }, } </script>
标准项目结构
如果所有的状态都写在一个 js 中,这个 js 必定会很臃肿,Vuex 并不限制你的代码结构。但是它建议你按以下代码
结构来构建项目结构:
├── index.html ├── main.js ├── api │ └── ... # 抽取出API请求 ├── components │ ├── App.vue │ └── ... └── store ├── index.js # 我们组装模块并导出 store 的地方 ├── actions.js # 根级别的 action ├── mutations.js # 根级别的 mutation └── modules ├── cart.js # 购物车模块 └── products.js # 产品模块
在 store下创建 modules 目录,该目录下创建 home.js
const state = { count: 1 } const getters ={ desc(state) { if(state.count < 50) { return '吃饭' }else if(state.count < 100) { return '睡觉' }else { return '打豆豆' } } } const mutations = { increment(state, n) { // n 为载荷 // state.count ++ state.count += n }, decrement(state) { state.count -- } } const actions = { add(context) { // 触发 mutations 中的 increment 改变 state context.commit('increment', 10) }, decrement({commit, state}) { // 按需传值 commit('decrement') } } export default { // 存放状态(共享属性) state, //派生属性 getters, // 改变 state 状态 mutations, actions }
修改 storeindex.js, 导入 ./modules/home.js,删除之前的home变量
import Vue from 'vue' import Vuex from 'vuex' // 导入 Module import home from './modules/home' // 引入 Vuex 插件 Vue.use(Vuex) const store = new Vuex.Store({ // 注意V 和 S都是大写字母 modules: { home // home: home } }) export default store
正常访问, 与重构前一样