Vuex是什么:
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 解决了多个视图依赖于同一状态
和来自不同视图的行为需要变更同一状态
的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上
状态管理模式包含以下几个部分:
- state,驱动应用的数据源;
- view,以声明方式将 state 映射到视图;
- actions,响应在 view 上的用户输入导致的状态变化
以下是一个表示“单向数据流”理念的简单示意:
安装:
npm
npm install vuex --save
yarn
yarn add vuex
在一个模块化的打包系统中,您必须显式地通过 Vue.use()
来安装 Vuex:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:
-
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
-
你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
核心概念 :
配置项
配置项 | 含义 | 注意 |
state | 单一状态树 | 类似data |
mutations | 数据管家(同步) | 唯一修改state的地方 |
actions | 异步请求 | 要改state需要提交给mutations |
getters | vuex计算属性 | 类似computed |
modules | 模块拆分 |
关系图
定义与使用:
Vux-state数据源
语法 :
/*
const store = new Vuex.Store({
state: {
变量名: 初始值
}
})
*/
使用方式:
1、组件内直接使用
this.$store.state.变量名 开启命名空间 this.$store.state.模块名.变量名
2、组件内 - 映射使用 (推荐)
// 1. 拿到mapState辅助函数
import { mapState } from 'vuex'
export default {
computed: {
// 2. 把state里变量映射到计算属性中
...mapState(['state里的变量名']) 开启命名空间 ...mapState("模块名", ['state变量名'])
}
}
Vuex-mutatons同步修改
语法:
/*
const store = new Vuex.Store({
mutations: {
函数名 (state, 可选值) {
// 同步修改state值代码
}
}
})
*/
使用方式:
1、组件内直接使用
this.$store.commit("mutations里的函数名", 具体值)
开启命名空间
this.$store.commit("模块名/mutations里的函数名", 具体值)
2、组件内 - 映射使用 (推荐)
// 1. 拿到mapMutations辅助函数
import { mapMutations } from 'vuex'
export default {
methods: {
// 2. 把mutations里方法映射到原地
...mapMutations(['mutations里的函数名'])开启命名空间 ..mapMutations("模块名", ['mutations里方法名'])
}
}
Vuex-actions异步修改
语法:
/*
const store = new Vuex.Store({
actions: {
函数名 (store, 可选值) {
// 异步代码, 把结果commit给mutations给state赋值
}
}
})
*/
使用方式:
1、组件内直接使用
this.$store.dispatch('actions函数名', 具体值)
开启命名空间
this.$store.dispatch("模块名/actions里的函数名", 具体值)
2、组件内 - 映射使用 (推荐)
// 1. 拿到mapActions辅助函数
import { mapActions } from 'vuex'
export default {
methods: {
// 2. 把actions里方法映射到原地
...mapActions(['actions里的函数名'])开启命名空间 ...mapActions("模块名", ['actions里方法名'])
}
}
Vuex- getters计算属性
语法:
/*
const store = new Vuex.Store({
getters: {
计算属性名 (state) {
return 值给计算属性
}
}
})
*/
使用方式:
1、组件内直接使用
this.$store.getters.计算属性名
开启命名空间
this.$store.getters['模块名/计算属性名']
2、组件内 - 映射使用 (推荐)
// 1. 拿到mapGetters辅助函数
import { mapGetters } from 'vuex'
export default {
computed: {
// 2. 把getters里属性映射到原地
...mapGetters(['getters里的计算属性名'])开启命名空间
...mapGetters("模块名", ['getters里计算属性名'])
}
}
Vuex-modules分模块
由于使用单一状态图,应用的所有状态会集中到一个比较大的对象,当应用变得复杂时,store对象就有可能变得相当臃肿。为了解决所说的问题,Vuex允许我们将store分割成模块(modules)。每个模块拥有自己的state,mutations,actions,getters,嵌套的子模块
创建modules模块对象
新建store/modules/user.js
新建store/modules/cart.js
语法: 每个模块对象里包含5个核心概念, 只有state变成函数形式
// user.js用户模块
const userModule = {
// 开启命名空间
// 防止不同模块下的 属性 和 方法 冲突
namespaced: true,
state() {
return {
name: '',
token: ''
}
},
mutations: {},
actions: {},
getters: {}
}
export default userModule
// cart.js购物车模块对象
import axios from 'axios'
const cartModule = {
// 开启命名空间
// 防止不同模块下的 属性 和 方法 冲突
namespaced: true,
state() {
return {
goodsList: []
}
},
mutations: {
setGoodsList(state, newList) {
state.goodsList = newList
}
},
actions: {
async asyncGetGoodsList(store) {
const url = `https://www.escook.cn/api/cart`
// 发送异步请求
const res = await axios({ url: url });
store.commit('setGoodsList', res.data.list) // 提交mutation修改state中的数据
}
},
getters: {
allCount(state) {
return state.goodsList.reduce((sum, obj) => {
if (obj.goods_state === true) { // 选中商品才累加数量
sum += obj.goods_count;
}
return sum;
}, 0)
},
allPrice(state) {
return state.goodsList.reduce((sum, obj) => {
if (obj.goods_state) {
sum += obj.goods_count * obj.goods_price
}
return sum;
}, 0)
}
}
}
export default cartModule
定义modules(在根store中注册子模块)
import Vue from 'vue'
import Vuex from 'vuex'
import cartModule from './modules/cart'
import userModule from './modules/user'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
user: userModule,
cart: cartModule
}
})
export default store
分模块-命名空间
防止多个模块之间, state/mutations/actions/getters的名字冲突
//在模块对象内设置namespaced: true
const cartModule = {
namespaced: true,
state () {},
mutations: {},
actions: {},
getters: {},
modules: {}
}
代码示例:
1、首先为了项目格式便于维护和相对规范一点,我们先在 目录下建立一个 store 文件夹,并且在下面建立一个 index.js 文件:
2、先引入 Vue 和 Vuex 并且别忘了 Vue.use(Vuex),当然你 Vuex 首先得跟 main.js 扯上点关系嘛
= > 引入 store 文件 = > 并且 Vue 实例上得将 挂载 store ,这下万无一失。可以继续了
然后我们就可以开始编写我们的vuex业务代码
3、现在开始 Vuex 的主宰部分 new Vuex.Store({})
在这张图上可以清楚的看到 new Vuex.Store 里面有一个 state:{ } 注释也写了,
这是你要设置的全局访问的 state 对象,也就是你需要 count 就丢个 count进去,需要 price 就丢个 price进去,
这里我丢了个 count 和 ChangeShowCom 两个不同的数据类型 作为一个对比。
4、在页面中获取 state 里面的数据
5、getters 和 组件的 computed 类似,方便直接生成一些可以直接用的数据。当组装的数据要在多个页面使用时,就可以使用 getters 来做。
6、那么, 我们如果想改变 count 的值,应该怎么做呢? 这时候就可以用到 mutations 。
要修改store中的值唯一的方法就是提交mutation来修改,我们现在App.vue文件中添加两个按钮,一个加1,一个减1;
这里我们点击按钮调用add(执行加的方法)和del(执行减法的方法),然后在里面直接提交mutations中的方法修改值:
修改index.js文件,添加mutations,在mutations中定义两个函数,用来对count加1和减1,
这里定义的两个方法就是上面commit提交的两个方法如下:
7、Action 类似于 mutation,不同在于:
1、Action 提交的是 mutation,而不是直接变更状态。
2、Action 可以包含任意异步操作。
这里我们把commit提交mutations修改为使用dispatch来提交actions;我们点击页面,效果是一样的。
8、来看一下 Vuex 里面的好东西,辅助函数 mapState、mapGetters、mapActions
如果我们不喜欢这种在页面上使用“this.$stroe.state.count”和“this.$store.dispatch('funName')”这种很长的写法,
那么我们可以使用mapState、mapGetters、mapActions就不会这么麻烦了;
并且我们配合 ... 拓展符 一起使用。
正常显示,效果是一样的,我们就可以不再使用很长的写法来调用了。
9、Vuex与localStorage
vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。
这里需要注意的是:由于vuex里,我们保存的状态,都是数组,而localStorage只支持字符串,所以需要用JSON转换
JSON.stringify(state.'数据名') array=>string
JSON.parse(window.localStorage.getItem(key)) string=>array