Vuex 重学指南

场景

当我们遇到多个组件共享的状态时,单向数据流的简洁性很容易被破坏:
  • 多个视图依赖于同一状态。
  • 来自不同视图的行为需要变更同一状态。

对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。

对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。


什么是Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式

也就是说 Vuex 用于单页面应用组件之间的数据共享,在组件嵌套很多层的情况下,Vue 中父子组件的通信过程就变得很麻烦,此时使用 Vuex 方便了组件间的通信。
  • HTML5 提供的数据存取机制 localStorage ,localStorage 存储的数据存在浏览器中,也就是本地磁盘中,localStorage 多数情况用于页面之间传递数据。
  • Vuex 是将数据存储在了内存中,每一次刷新页面,之前存在 Vuex 中的数据就会重新初始化。

vuex的优点:

  • js 原生的数据对象写法, 比起 localStorage 不需要做转换, 使用方便
  • 属于 vue 生态一环, 能够触发响应式的渲染页面更新 (localStorage 就不会)
  • 限定了一种可预测的方式改变数据, 避免大项目中, 数据不小心的污染

vuex的缺点:

  • 刷新浏览器,vuex中的state会重新变为初始状态 (解决方案-插件vuex-persistedstate)

为什么要用Vuex,它解决了什么问题

多个组件依赖于同一状态时,对于多层嵌套的组件的传参将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
来自不同组件的行为需要变更同一状态。以往采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。

安装Vuex

npm install vuex --save

新建Vuex文件

在src目录下新建store.js

│  App.vue
│  main.js
│
├─assets
│      logo.png
│
├─components
│      HelloWorld.vue
│
├─router
│      index.js
│
└─store
       index.js

编写store.js中的Vuex内容

import Vue from 'vue'
import Vuex from 'vuex'

//挂载Vuex
Vue.use(Vuex)

const state = {    // 组件间共享的数据
    msg: "你好"
}

const getters = { // 获取共享数据
    getList: state => {
        return state.list +"吗"
    }
}

const mutations = { // 修改共享数据
    setList: (state, value) => {    //value就是外部调用传进来的值
        state.list = value
    }
}

actions:{
    changeList(context,value){
      /**
       * 模拟异步的过程,2000毫秒后通过commit()方法执行mumations中的setList方法改变数据
       * 同样,value可以是单数据或通过对象的方式,传递多个数据
       * 这里只举例通过对象传递多数据的情况
       */
      setTimeout(()=>{
        context.commit("setList",value)
      },2000)
    }
  }


export default new Vuex.Store({
    state,
    getters,
    mutations,
    actions
})

main.js 引入Vuex文件

import store from "@/store"; //引入src下的store.js文件
new Vue({
  router,
  store,  //注入Vuex
  i18n,
  render: h => h(App),
}).$mount('#app')

外部的vue组件去调用Vuex

a.vue 文件中设置共享数据list

    <script>
    //mapMutations是vuex的mutation的辅助函数,用于在组件中映射mutation内的方法
    //以便在该组件中直接使用mutation里的方法 (说白了,就是一语法糖),本质上等同于:
    //this.setList === this.$store.commit('setList') //true

    import { mapMutations } from 'vuex' 
    export default {
        mounted () {
            this.setList(['hello'])
        },
        methods: {
            ...mapMutations(['setList'])
        }
    }
    </script>

b.vue 文件中获取共享数据list

<script>
import { mapGetters } from 'vuex' //mapGetters是getters的语法糖,
import { mapState } from 'vuex'  //mapState是state的语法糖,
export default {
     computed: {
        ...mapGetters(['getList' ])
     },
     mounted () {
        console.log(this.getList)               // ['你好吗']
        console.log(this.$store.state.list)     // ['hello']
        console.log(this.getList)               // ['hello吗']
     }
}
</script>

Vuex的5大属性

1.State

  • state是一个数据存储的仓库,所有的数据源都会存放在这里,就类似组件中的data。
  • 在store.js的state中的数据,可以在任意组件中通过this.$store.state访问到。
  • 相比EventBus,需要到处去调用$emit$emit$emit$on方法去监听数据和拷贝数据副本,做到了数据和视图层的解耦

我们可以在a.vue中直接获取到list

console.log("list======"+this.$store.state.list)

2.Getter

  • getter主要用于在获取数据时,对数据进行加工后返回。
  • 与EventBus相比,通过Vuex的Getter,我们可以在Store中对数据做统一加工处理,利于日后的项目维护

我们打印出Getter

console.log("getList======"+this.$store.getters.getList)

Mutation

  • 通过Mutation我们可以对数据仓中的数据进行修改,我们可以在组建中通过调用this.$store.commit()方法去调用对应的Mutation去修改数据。
  • Mutation中只能执行同步的方法,如果需要执行异步方法,我们要使用接下来即将登场的Action。
  • 通过this.$store.commit()去调用Action的方法
//注意,当我们需要传多个参数的时候,就需要把参数放到一个对象中
 this.$store.commit("setList",{"msg1":"早上好","msg2":",吃饭吗"})
 console.log("list======"+this.$store.state.list.msg1)  //早上好
 console.log("list======"+this.$store.state.list.msg2)  //吃饭吗

Action

  • ActionMutation类似,它只是能够处理异步的情况,最终通过commit()函数调用Mutation去修改数据。
  • 通过this.$store.dispatch()去调用Action的方法。
//通过Action修改数据
    this.$store.dispatch("changeList",{"msg":"action修改后的数据"})
    setTimeout(()=>{
      console.log("1秒后list======"+this.$store.state.list.msg)
     //你好action修改后的数据
    },1000)

Module

当我们项目比较小的时候,直接创建一个Vuex文件就可以了,所有的方法数据都写在一起。
但是,当我们项目越来越大时,我们这个Store中的stateMutationGetterAction的数量和Store的代码行数就会爆炸性的增加,使得我们的Store变得维护困难。这时候我们就需要对Vuex就行模块化了。

这时候,我们希望把Store模块化,然后通过Module整合在一起,例如不同子组件的数据抽取出来写成单独的一个Store

现在我们创建3个新的Store文件:

1.index.js Store,主要负责整合所有的Store模块。
2.a.js, 主要负责a.vue的数据维护
3.b.js,主要负责b.vue子组件的数据维护

首先我们来看a.js和b.js:

\\a.js
export default {
  state: {
    text:""
  },
  getters:{
    gettext(state){
      return state.text;
    }
  },
  mutations:{
    changetext(state,payload){
      state.text=payload
    }
  }
}
b.js
export default {
  state: {
    bText:[]
  },
  getters:{
    getbText(state){
      return state.bText;
    }
  },
  mutations:{
    changebText(state,payload){
      state.text.push(payload)
    }
  }
}

这里,我们只是通过export default将a和b输出。 接下来让我们看index.js,它将a.js和b.js整合在一起

index.js
import Vue from 'vue'
import vuex from 'vuex'
import a from './a'
import b from './b'
Vue.use(vuex)
export default new vuex.Store({
  modules:{
    a:a,
    b:b
  }
})

在index中我们首先将a.js和b.js通过import引入,然后在Store的modules中将它们两引入。 接下来我们将index.js挂载在根组件下,我们修改一下main.js:

main.js

import Vue from 'vue'
import App from './App'
import router from './router'
import vueResource from 'vue-resource'
import store from './store/index'   //引用index.js
// import searchStore from './store/SearchStore'
Vue.use(vueResource)
Vue.config.productionTip = false

new Vue({
  el: '#app',
  router,
  store,    //修改部分,简写
  components: { App },
  template: '<App/>'
})

这样我们就成功整合了两个Store模块,需要注意的是接下来我们访问state对象的对象时,需要加上模块名:
例如,我们要访问a的text的数据时,我们需要使用this.$store.state.a.text进行访问


Vuex的辅助函数

import {mapState, mapGetters, mapMutations, mapActions } from "vuex"; //引入辅助函数

mapState

computed: {
  localComputed () //本地计算属性
  //使用对象展开运算符将此对象混入到外部对象中
  ...mapState({
    //..
  })

mapGetters

import { mapGetters } from 'vuex'
 
export default {
  // ...
  computed: {
  // 使用对象展开运算符将 getters 混入 computed 对象中
    ...mapGetters([
      'a',
      'b',
      //..
    ])
  }

mapMutations

mport { mapMutations } from 'vuex'
 
export default {
  //..
  methods: {
    ...mapMutations([
      'increment' // 映射 this.increment() 为 this.$store.commit('increment')
    ]),
  }

mapActions

import { mapActions } from 'vuex'
 
export default {
  //..
  methods: {
    ...mapActions([
      'incrementN' //映射 this.incrementN() 为 this.$store.dispatch('incrementN')
    ])
  }

text.vue 组件中使用模块化的Vuex

import { mapGetters, mapMutations, mapActions } from "vuex";
  mounted() {
        this.tabsValue = this.tabs  //使用注册后的tabs计算属性  
  },

  computed: {
       // myOrder是Vuex定义的模块名,这里表示 注册myOrder.js 中的 tabs
    ...mapGetters('myOrder', ['tabs']), 
  },
  methods: {
      // myOrder是Vuex定义的模块名,这里表示 注册myOrder.js 中setTabs方法
    ...mapMutations("myOrder", ["setTabs"])  

    tabsClick(value) {
      this.setTabs(value)  // 使用上面注册的setTabs方法
    }
  }

定义的Vuex 其中的一个模块 myOrder.js

// 我的订单
const myOrder = {
  // 设置命名空间
  namespaced: true,
  state: {
    tabs: 0
  },
  getters: {
    tabs: state => state.tabs,
  },
  mutations: {
    setTabs(state, tabs = {}) {
      state.tabs = tabs
    },
  },
  actions: {

  }
}
export default myOrder;
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,997评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,603评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,359评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,309评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,346评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,258评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,122评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,970评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,403评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,596评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,769评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,464评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,075评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,705评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,848评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,831评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,678评论 2 354

推荐阅读更多精彩内容

  • 理解vue 引用一段官方的原话: Vue.js(读音 /vjuː/,类似于 view) 是一套构建用户界面的渐进式...
    绰号陆拾柒阅读 2,379评论 3 6
  • Vuex 是 状态管理的编程模式 + 工具库,适用于 Vue.js 编写的应用。它作为一个集中化的 store (...
    lion1ou阅读 3,593评论 2 7
  • Vuex学习 一、Vuex是做什么的? 官方解释:Vuex是一个专为Vue.js应用程序开发的状态管理模式,它采用...
    waigo阅读 262评论 0 0
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 7,523评论 16 22
  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,564评论 0 11