必备-12.vuex
vuex:状态管理库**(公共大仓库)**,集中式存储管理所有组件的状态值,相当于把所有Vue组件的状态数据放到一个公共数据库中,任何一个组件修改了公共数据库中的状态值,其他使用了该状态值的组件都会跟着刷新渲染这个值
-
Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源“而存在。这也意味着,每个应用将仅仅包含一个 store 实例
-
vuex的作用:
- 1、vuex是专为Vue.js应用程序开发的状态管理模式(集中式存储)—>公共仓库
- 2、缓存 keep-alive
localStorage
:本地存储,刷新有数据、数据永久sessionStorage
:会话存储,刷新有数据、关闭页面没有数据vuex
:不刷新,不关闭页面,就会保存以前的数据,刷新就不存在数据
-
vuex:“单向数据流”理念:
-
vuex的异步函数执行的过程:
vuex的五个核心属性
-
state
:相当于vue中的data -
getters
:相当于vue中的computed -
mutations
:相当于vue中的methods(同步) -
actions
:相当于vue中的methods(异步) -
Modules
:模块 -
在讲解vuex的五个核心属性之前需要先创建一个vuex仓库的实例:
-
//1、新建index.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex)//在vue上挂载vuex包,扩展vuex插件 const store = new Vuex.Store({//创建Vuex的store实例 state: { count: 0 }, mutations: { increment (state) { state.count++ } } }) export default store; //2、在main.js中挂载,注意,这个store实例是挂载到了Vue的prototype上 new Vue({ el: '#app', store: store, })
-
state
-
state
:相当于vue中的data -
作用:存放所有组件能够共用的状态值
-
用法:
-
vuex实例仓库端定义:
-
const store = new Vuex.Store({ //在大仓库中存放一个count为0 state: { count: 0 } })
-
-
所有vue组件端调用:
-
//方式一:直接沿着原型链调用(不推荐没缓存) {{this.$store.state.count}} //方式二:放到computed中调用(有缓存) computed: { count () { return this.$store.state.count } } //方式三:使用辅助方法,放到computed中调用多个(推荐,有缓存) import { mapState } from 'vuex'//导入辅助函数Vuex.mapState,它返回的是一个对象 //第一种:计算属性中只存在映射过来的,参数是数组或对象,以数组为例: computed: mapState(['count'])// 映射 this.count 为 store.state.count //第二种:计算属性中存在映射过来的也存在自定义的,参数是数组或对象,以对象为例: computed: { addCount(){}, // 使用对象展开运算符将此对象混入到外部对象中 ...mapState({ //... }) }
-
-
getters
-
getters
:相当于vue中的computed -
作用:在vue组件获取state状态值之前,对状态值做格式化,再传给vue组件
-
参数:
state
:【默认参数】指向的是vuex的实例store里的stategetters
:【默认参数】指向的是vuex的实例store里的getters,可以根据它,获得getters中其他属性的信息
-
用法:
-
vuex实例仓库端定义:
-
getters: { getCountAdd: (state,getter )=> { return state.count+1+getter.getc;//0+1+666 } getc(){return 666} }
-
-
所有vue组件端调用:在computed中调用
-
import { mapGetters } from 'vuex';//导入辅助函数 //方式一:以数组作为参数执行辅助函数 computed: { // 使用对象展开运算符将 getter 混入 computed 对象中 ...mapGetters([ 'getCountAdd', 'getc', // ... ]) } //方式一:以数组作为参数执行辅助函数,可以自定义状态名 computed: { ...mapGetters({ // 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount` doneCount: 'doneTodosCount' }) }
-
-
mutations
-
mutations
:相当于vue中的methods(同步) -
要点:mutations中的函数必须是同步的,不能放异步方法
-
作用:vue组件不能直接修改store大仓库中的状态值,(会导致页面与仓库不同步),如果想做到所有组件数据,与store内state状态值同步,就要经过mutations中的方法来修改
-
参数:
state
:【默认参数】指向当前vuex实例store的state对象payload
:载荷,【第二个参数】用来接收组件传给函数的参数,如果想传多个数据,载荷应该是一个对象- 第三个及以后的参数不会发送给mutations里的方法
-
用法:
-
vuex实例仓库端定义:
-
mutations: { //自增函数 increment (state, payload) { state.count += payload.amount } }
-
-
所有vue组件端调用:在methods中调用,
-
//方式一:使用store.commit方法可直接调用 methods:{ addCount(){ this.$store.commit('increment', { amount: 10 }) } } //方式二:使用辅助函数,参数为对象或数组 import {mapMutations} form "vuex";//导入vuex的辅助函数 methods:{ ...mapMutations(["increment"]),//数组传参 ...mapMutations({//对象传参 inc:"increment" }) } //调用 <telement> <div> <button @click="inc(1,2,3)">点击 </button> </div> </telement>
-
-
actions
-
actions
:相当于vue中的methods(异步) -
作用:mutation只能存储同步函数,actions就是将同步函数封装为异步函数
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
-
参数:
context
:【默认参数】指向vuex的实例store大仓库
-
用法:
-
vuex实例仓库端定义:
-
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { //定义自增方法 increment (context) { //异步函数 setTimeOut(function(){ //内部再调用了commit方法 context.commit('increment') },1000) } } })
-
-
所有vue组件端调用:在methods中调用
-
//方式一:使用store的dispatch方法调用触发 methods:{ addCount(){ this.$store.dispatch('increment') } } //方式二:使用辅助函数,参数为对象或数组 import {mapActions} form "vuex";//导入vuex的辅助函数 methods:{ ...mapActions(["increment"]),//数组传参 ...mapmapActions({//对象传参 inc:"increment" }) } //调用 <telement> <div> <button @click="inc(1,2,3)">点击 </button> </div> </telement>
-
-
modules
-
作用:减少单一状态树的shore对象存放数据导致的臃肿,将整个sotre对象拆分为多个模块module,每个模块都存在自己局部的
state、getters、mutations、actions、namespaced
,甚至仍然将这个模块继续向下拆分 -
namespaced:默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
-
如果希望你的模块具有更高的封装度和复用性,你可以通过添加
namespaced: true
的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。 -
我们在
...mapGetters("moduleA",["msg"])
,寻找的是moduleA
模块中的msg状态值,如果没有写moduleA
的话,它会直接去全局的store中找状态值msg
-
-
用法:
-
第一步:需要创建一个独立的模块:
-
//store/moduleA.js //就是vuex实例类型的对象 const mouduleA={ //存在state,mutations,actions,getters,namespaced。。。 state:{ msg:"我是moduleA", arr:[1,2,3,4,5,6] }, getters:{ getNum(state){ return state.arr.filter(item=> item%2==0) } } } //导出模块 export default moduleA;
-
-
第二步:在store实例中导入并挂载:
-
modules: { a:moduleA } }) //重点: //store挂载moduleA之后: //moduleA中的state对象会挂载到store的state对象上 store:{ state:{ a:{//重点:a指向的就是moduleA模块中的state对象 msg:"我是moduleA"; } } //moduleA中的mutations、getters、actions对象会挂载到store的对应对象上 //以getters为例 store:{ getters:{ "a/getNum":[2,3,4];//将子模块中计算属性的计算结果放入getters中 } //mutations、actions相同做法 } }
-
-
第三步:在vue组件中调用vuex的modules中:
-
//方式一:通过$store导入 this.$store.state.a.msg;//获取a模块中的属性 this.$store.getters["a/getNum"];//获取方法或计算属性 //方式二:通过辅助函数写法(常用) import {mapState,mapGetters} from "vuex"; computed:{ ...mapState("a",["msg"]),//a是nameSpace的命名空间,所以必须开启namespace才行 ...mapState(["a/msg"]),//这样获取不了,因为state的挂载方式不是这样挂载的 ...mapGetters(["a/getNum"]),//这样能获取,以为getters、mutations、actions的挂载方式 } //方式三:通过导入方法指定mapxxx的地址 import { createNamespacedHelpers } from 'vuex' const { mapState, mapActions } = createNamespacedHelpers('a') computed:{ ...mapState(["msg"]),//它会直接找a里的msg,因为上面设置了命名空间 }
-
-
辅助函数
mapState()获取的是大仓库state属性中的状态值
mapGetters()获取的是大仓库getters属性中的状态值
mapMutations()获取的是大仓库mutations属性中的方法
mapActions)获取的是大仓库actions属性中的方法
mapState()
-
在真实项目中我们不会使用
{{this.$store.state.count}}
来调用仓库中的状态值,以为这样每次页面渲染或打开页面都需要去大仓库中获取(无缓存) -
所以我们基于computed(有缓存)的优势,来获取仓库中的数据,这时就需要调用到vuex中内置的辅助函数:
mapState()
; -
第一步:导入
mapState
辅助函数-
//Home.js import mapState from "vuex";
-
-
第二步:在计算属性
computed
中配置mapState,mapState返回的是一个对象-
mapState(param):辅助函数的参数可以是对象或数组
- 对象可以自定义获取的状态值名,数组就是仓库中状态名是啥,我们调用就得用啥状态名
-
方式一:computed中只需要大仓库中的
mapState
-
//1、mapState函数中的参数是数组,会默认返回对应的num和age状态值,我们调用时也直接调用num age即可 computed:mapState(["num","age"]) //2、mapState函数中的参数是对象,会默认返回对应num和age状态值,并且赋值给调用方的私有属性n和a,我们调用时用n和a调用 computed:mapState({ n:"num", a:"age" })
-
-
方式二:computed中既有自己定义的计算属性,又用辅助函数获取的
-
computed:{ getData(){ return aaa; }, //因为mapState中获取的是对象,所以需要通过展开运算符将其展开 ...mapState( ["num","age"] ) }
-
-
注意:mapState获取的是对象,需要展开运算符展开
-
mapGetters()
mapGetters()获取的是大仓库getters属性中的状态值(必须是同步的方法)
-
vuex实例的getters属性相当于vue实例中的computed属性
-
第一步:在vuex实例中添加getters属性,并定义格式化方法:
-
//store/index.js getters:{ //第一个参数是当前vuex实例的state属性对象 //第二个参数是当前vuex实例的getters属性对象 addCount(state,getters){ return state.count++; } }
-
-
第二步:在调用方实例中导入,并引入
-
//Home.vue import mapGetter from "vuex";//导入mapGetter computed: { //方式一:直接在computed中调用:通过Vue公有属性中挂载的$store调用 addCount () { return this.$store.getters.addCount } //方式二:通过mapGetter调用 ...mapGetters(["addCount"]);//数组参数形式获取,不能改状态名 ...mapGetters({ add:"addCount";//对象形式获取,可以改状态名 }) }
-
mapMutations()
-
mapMutations()获取的是大仓库mutations属性中的状态值
-
**第一步:**在vuex中创建mutations属性
-
mutations: { addCount (state) { // 变更状态值 state.count++ }
-
-
第二步:在Home.vue中导入mapMutations并注册到实例methods上
-
export default { // ... methods: { ...mapMutations([ 'addCount', // 将 `this.increment()` 映射为 `this.$store.commit('increment')` ]), ...mapMutations({ add: 'addCount' // 将 `this.add()` 映射为 `this.$store.commit('increment')` }) } }
-
mapActions()
-
mapActions获取的是大仓库actions属性中的方法
-
第一步:在vuex实例中添加actions方法(必须是异步的):actions的作用就是将mutations里的同步方法异步化
-
//store/index.js actions: { //context是指向当前vuex实例的对象,也就是$store,所以能调用它上面的所有方法 increment (context) { context.commit('addCount') } }
-
-
第二步:在Home.vue中导入,并注册到自己身上
-
import { mapActions } from 'vuex' export default { // ... methods: { ...mapActions([ 'addCount', // 将 `this.addCount()` 映射为 `this.$store.dispatch('addCount')` // `mapActions` 也支持载荷: 'addCount' // 将 `this.addCount(amount)` 映射为 `this.$store.dispatch('addCount', amount)` ]), ...mapActions({ add: 'addCount' // 将 `this.add()` 映射为 `this.$store.dispatch('addCount')` }) } }
-
vuex缓存
-
问题:没有用缓存之前,我们每次打开页面都会发送请求
-
目标:应用vuex实现缓存,只要页面不刷新,不关闭,数据如果是null,我就发送请求,如果不是null就从vuex的缓存中拿数据
-
实现:
-
第一步:创建异步方法获取服务器数据,并把数据赋值给状态值
-
//index.js import Vue from 'vue' import Vuex from 'vuex' import moduleA from './moduleA/moduleA' import api from "@/api/index.js"; Vue.use(Vuex) //store实例 export default new Vuex.Store({ state: { // 第一步:list专门存放数据 list:null }, mutations: { // 第二步:修改list的值,如果请求成功就不为null了 changelist(state,payload){ state.list=payload; } }, actions: { //第三步:异步函数需要写在actions中 async getData(context){ let result=await api.getTaskList(); if(result.code===0){//返回数据成功 context.commit("changelist",result.list);//异步函数想修改state状态值,必须经过mutations } } }, modules: { a:moduleA } })
-
-
第二步:在页面加载之前,先判断是否有缓存,如果有直接用
-
//Three.vue <template> <div class="three"> {{this.$store.state.list}} </div> </template> <script> export default{ name:"Three", data(){ return{ } }, created(){ if(!this.$store.state.list){//只要list是null,表示没缓存,向服务器端发请求 this.getData(); } //否则直接调用list即可 }, methods:{ async getData(){ let result= await this.$api.getTaskList(); console.log(result); } } } </script> <style> </style>
-
-
vuex的使用步骤
-
第一步:安装
vue和vuex
模块包:$npm i vue vuex -S
-
第二步:导入vue和vuex
-
//为了防止导入的包太大,所以按需导入 // /store/index.js import vue from "vue"; import Vuex from "vuex";
-
-
第三步:Vue注册vuex并导出
-
// /store/index.js import vue from "vue"; import Vuex from "vuex"; //Vue注册Vuex Vue.use(Vuex) //创建vuex的实例 const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } } }) //导出创建好的vuex实例 export default store;
-
-
第四步:在main.js中导入、挂载已经创建好的实例
-
//main.js import store from "@/store/index.js"; new Vue({ router, //store被挂载到了全局Vue实例的prototype上,任何实例都可以利用this.$store获取到store大仓库实例 store, render: h => h(App) }).$mount('#app')
-
-
第五步:任意组件可以调用修改仓库中的信息,但是想修改数据,都必须通知仓库(调用仓库mutations)中的方法,来实现页面数据与仓库数据的同步修改
-
面试题:能不能在界面上直接修改仓库中获取的数据:能!,但是修改的数据只是在页面上显示修改了,因为没有经过仓库,仓库中的数据实质上并没有修改,所以我们在工作中不能直接修改仓库中获取的值。
-
//Home.vue <div class="home"> {{this.$store.state.count}}//调用大仓库中的count <button @click="changeData">add</button>//修改仓库中的count </div> <script> methods:{ //Home.vue changeData(){ //使用store.commit来执行store中mutations中的方法 store.commit('increment') } } </script>
-
// /store/index.js const store = new Vuex.Store({ state: { //放到大仓库中的状态值,所有组件都可以用这个count count: 1000 }, //存放方法(同步) mutations: { //状态值自增的方法 increment (state) { state.count++ } } })
-