Vuex数据管理
如何理解数据管理?
这个Vuex就是vue中的数据管理中心 ,相信看过亮剑的大家都知道 ,本来独立团老李管理独立团那是想喝酒喝酒想吃肉吃肉 ,想打仗打仗,但这样的独立团就是比较混乱啊,就是和我们的vue一样 如果数据,都在data里边随取随用,那也一样很混乱,所以我们就需要一个单独的数据中心。也就是赵刚赵政委来了,管独立团的生活了,政委管生活,团长管打仗 ,这样分工也就比较明确。Vuex就是我们的政委。 来吧我们的资料数据管理,任何的管理都不能直接操作,而通过Vuex来统一的调配和转发。
管理不断变化的 state 非常困难。如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,依次地,可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么。state 在什么时候,由于什么原因,如何变化已然不受控制。 当系统变得错综复杂的时候,想重现问题或者添加新功能就会变得举步维艰。– 摘自《 Redux 中文文档 》
-
起步安装
npm install vuex --save 复制代码
-
核心概念
- store : 我们的数据管理中心 ,任何的操作都需要通过store ,也就是我们的赵政委
- state :就是我们存储数据的地方
- mutations : 我们修改数据都需要通过 mutations 来进行操作
-
使用
-
新建store.js
import Vuex from 'vuex'; import Vue from 'vue'; Vue.use(Vuex); // 虽然和我们的router使用类似但我们不能在main.js中use export default new Vuex.Store({ state:{ count:0 } }) 复制代码
-
main.js中使用 将我们的store.js 挂载到我们的原型链之上
import Vue from 'vue' import App from './App.vue' import VueRouter from 'vue-router'; import router from './routes'; import store from './store'; Vue.config.productionTip = false; Vue.use(VueRouter); //将this.$router 挂载到原型链上,使得vue具有处理路由的能力 new Vue({ router, store, // 相当于把我们的store挂载到原型链之上 render: h => h(App), }).$mount('#app'); 复制代码
-
数据使用 在.vue中使用this.$store来获取
<script> export default { name: "Page2", computed:{ count(){ return this.$store.state.count } } } </script> 复制代码
-
-
当我们在对state的数据进行修改的时候,你一定想这样 —直接修改
仔细想想这样修改的话不是又回到了我们原来那种 更改state 其他组件也相应,最后你想找到问题来源或者添加新功能时变得困难那种状态了么?
<script> export default { name: "Page2", created(){ setTimeout(()=>{ this.$store.state.count++ },2000) }, } </script> 复制代码
—解决使用vuex的严格模式使用,使用 mutations修改数据
//--------------store.js中 export default new Vuex.Store({ state:{ count:0 }, strict:true //使用严格模式 }) 复制代码
-
使用mutations修改数据
在vuex中,关于修改state的方式,需要commit提交mutation。官方文档中有这么一句话:
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
export default new Vuex.Store({ state:{ count:0 }, mutations:{ increment(state){ state.count++ } }, strict:true //使用严格模式 }) 复制代码
// 相当于我们现在不能直接去获取物资了,得去政委那等级一下子才能获取物资 created(){ setTimeout(()=>{ // this.$store.state.count++ this.$store.commit('increment') },2000) }, 复制代码
-
上边其实是两种修改state的方式
-
在vuex官方文档上看到了关于严格模式的描述
开启严格模式,仅需在创建 store 的时候传入 strict: true;
在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。 -
于是,将vuex设置成了严格模式。
直接修改state发现控制台确实是报出了错误,但是state修改成功,并且依然是响应式的。错误提示:
Do not mutate vuex store state outside mutation handlers.
- 通过commit 提交 mutation 的方式来修改 state 时,vue的调试工具能够记录每一次state的变化,这样方便调试。但是如果是直接修改state,则没有这个记录。
-
分析源码
-
mutation通过commit修改state ,可以看到通过this._withCommit函数处理
-
看_ withCommit ,_ withCommit函数的参数是fn 也就是我们上边的更改state的函数,在执行fn之前 ,将this.comiting =true ,等fn执行完 ,再将this. _commiting = commiting
-
那么这个commiting和 严格模式的strict:true 设置有什么关系
-
enableStrictMode() 干了什么 ?
在 enableStrictMode 函数内部,调用了 $watch 函数来观察 state的变化。当state变化时,就会调用 assert 函数,判断 store. _commiting( 上边的 this.committing) 的值,如果不为 true,就会报出异常:
-
所以通过外部直接修改state,则没有执行 commit 函数,也就没有执行 withCommit 函数,进而 this. _ withCommitting 的值 不为 true,故当执行 enableStrictMode 时,则会执行 assert 函数,因为_withCommitting不为true,则报出异常了。
-
-
-
getters :Vuex内部也需要 computed这个功能 ,比如将数据格式化
有时候我们需要从store中的state中派生出一些状态 ,可以理解为vuex中数据的computed功能
//--------------store.js getters:{ money:state => `${state.count*1000}` }, 复制代码
//--------------.vue 文件中 computed:{ money(){ return this.$store.getters.money } } 复制代码
-
Action :修改数据
但是Mutations必须是同步的,Action是异步的Mutation,配合dispatch使用,定义上是一个异步的任务 ,大部分的任务 包括网络请求,网络数据的获取、文件的读取全部是异步的使用dispatch
//--------------.vue文件中 created(){ // setTimeout(()=>{ // // this.$store.state.count++ // },2000) this.$store.dispatch('incrementAsync') //在incrementAsync内部我们再去执行我们的异步操作 this.$store.dispatch('incrementAsync',{ //还可以加参数 num:10 }) }, 复制代码
//--------------store.js mutations:{ increment(state,args){ state.count+= args.num || 1 //加参数后的变化 } }, actions:{ incrementAsync(store,args){ setTimeout(()=>{ store.commit('increment',args);//在去执行Mutations的任务,args参数 },2000) } }, 复制代码
-
mapState
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会使得代码冗余和重复
为了解决这个问题使用mapState辅助函数帮助我们生成计算属性
computed:{ count(){ return this.$store.state.count }, ... //还有一堆的属性 } 复制代码
computed:{ ...mapState({ count:state=>state.count }) // count(){ // return this.$store.state.count // } } 复制代码
-
mapActions
和mapState类似 ,把actions方法映射进来
created(){ // this.$store.dispatch('incrementAsync',{ //还可以加参数 // num:10 // }); this.incrementAsync({ //直接使用这个更优雅 、更像组件内部的代码 num:10 }) }, methods:{ ...mapActions(["incrementAsync"]) }, 复制代码
-
mapMutations
聪明的你也一定和我一样想到了mapMutations
setTimeout(()=>{ this.increment({num:1}) },2000) methods:{ ...mapMutations(["increment"]) }, 复制代码
-
总结一下
State:用来存状态。在根实例中注册了store 后,用
this.$store.state
来访问。Getters:从 state 上派生出来的状态。可以理解为基于 State 的计算属性。很多时候,不需要 Getters,直接用 State 即可。
Mutations:用来改变状态。需要注意的是,Mutations 里的修改状态的操作必须是同步的。在根实例中注册了 store 后, 可以用
this.$store.commit('xxx', data)
来通知 Mutations 来改状态。Actions:通过调用 Mutations 来改状态。Actions 可以包含异步操作。在根实例中注册了 store 后, 可以用
this.$store.dispatch('xxx', data)
来存触发 Action。 -
Vuex完整数据流程