13_Vue_使用Vuex

1 .Vuex是什么

​ 由于Vue是单向数据流,子组件内部不能直接修改从父级传递过来的数据,子组件与子组件之间无法相互传递数据。如果我们想让两个子组件之间进行通信的话,可以借助子组件 A 向父组件传值,父组件接收子组件 A 的数据后再传给 B 组件这样的方式进行通信。

​ 但是这样会有一个问题,就是如果子组件 A 的父组件上面还有一层爷爷组件,或者还有更多祖父类型的层级,那么是不是会很麻烦。

因此,我们会想到能不能我们将一个共有的数据存在一个特定的地方,用的时候自己去拿,这样就不需要一层层传值。使用vuex来保存我们需要管理的状态值,值一旦被修改,所有引用该值的地方就会自动更新。

我们什么时候应该用到Vuex呢?

  1. 小应用不建议使用Vuex,因为小项目使用 Vuex 可能会比较繁琐冗余
  2. 中大型单页应用,因为要考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择

2. 安装vuex

vuex是一个单独的npm包,需要安装后才能使用。同时Vuex也是Vue的一个Plugin,所以需要:

npm i vuex -S
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

3. 简单案例

在这里插入图片描述

NumGenerater组件负责不断产生数字,产生的数字供NumShow组件读取。NumShow根据数字范围来显示不同的颜色。0-59 显示红色,59-100 显示绿色
在这里插入图片描述
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着应用中大部分的**状态 (state)**数据。

很显然,产生的数字就是多个组件需要共享的。这个共享的数据与单纯的全局对象是有区别的:

  1. Vuex的状态存储是响应式的,当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。而单纯的全局对象不会。
  2. Vuex中不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。而单纯的全局对象是可以直接修改的。

3.1 Step 1 两个组件

在这里插入图片描述
src/views/NumGenerater.vue

<template>
  <div class="genNum">{{ num }}</div>
</template>

<script>
let timer = null

export default {
  data() {
    return {
      num: 0,
    }
  },
  mounted() {
    timer = window.setInterval(() => {
      let value = parseInt(Math.random() * 100)
      this.num = value
    }, 1000)
  },
  beforeDestroy() {
    if (timer) {
      window.clearInterval(timer)
    }
  },
}
</script>

<style>
.genNum {
  border: 1px solid #cccccc;
  width: 400px;
  height: 100px;
  line-height: 100px;
  text-align: center;
  font-size: 25px;
}
</style>

src/views/NumShow.vue

<template>
  <div style="margin-top:20px;">
    <div class="alert alert-success show"></div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      num: 100,
    }
  },
}
</script>

<style>
.show {
  font-size: 18px;
  text-align: center;
}
</style>

src/App.vue

<template>
  <div class="container" style="margin-top: 10px">
    <div class="row">
      <div class="col-md-6">
        <num-generater />
      </div>
      <div class="col-md-6">
        <num-show />
      </div>
    </div>
  </div>
</template>
<script>
import NumGenerater from '@/views/NumGenerater'
import NumShow from '@/views/NumShow'
export default {
  components: {
    NumGenerater,
    NumShow,
  }
}
</script>

目前两个兄弟组件之间没有任何通信,因为共享那个数字都各自在各自的组件中。下面开始使用Vuex来实现这个数字的共享。

3.2 Step 2 创建Vuex 的Store对象

src/store/index.js

import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
const store = new Vuex.Store({
    state: {
        num: 0
    },
    mutations: {
        change(state, newNum) {
            state.num = newNum
        }
    }
})

export default store

这个js中,直接导出了new出来的 store对象。store中包含两个部分:

  • state :其中存放状态值。这里存放了一个num,默认值为0
  • mutations: 意思是变化,其中包含一些函数,Vuex 中只有这些函数才能更改 state中的数据。当store对象的commit 方法执行的时候,commit方法中会指定Vuex要调用的 mutations 函数,然后Vuex才开始调用 这些方法,此时Vuex会传递 state 对象(第一个参数)和 commit提交过来的数据(第二个参数),这样就可以对 state中的状态数据进行更新了。

3.3 Step 3 加载store对象

将Vuex的store对象配置到 Vue实例中,这样每个组件中就可以使用 $store来访问到这个store对象了

src/main.js

import Vue from 'vue'
import App from '@/App'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap/dist/js/bootstrap'
import store from '@/store'; // 导入stroe对象 @/stroe/index.js
const vm = new Vue({
    el: '#app',
    store,  // store对象配置到Vue实例上
    render: (h) => h(App),
})

3.4 在组件中使用store

NumGenerater 组件中调用 commit 方法向 store中提交 mutation,通知stroe更改 state 中的num值

NumShow 中直接通过 this.$store.state.num 来获取值

src/views/NumGenerater.vue

 ...
mounted() {
    timer = window.setInterval(() => {
      let value = parseInt(Math.random() * 100)
      this.num = value
      this.$store.commit('change', value) //向store对象提交要调用的mutations
    }, 1000)
  },
...

src/views/NumShow.vue 组件中使用 this.$store.state.xxx 直接访问状态数据

<template>
  <div style="margin-top:20px;">
      <!-- 访问计算属性 cssCls 获取到 使用哪个css 类样式 -->
    <div class="alert show" :class="cssCls"></div>
  </div>
</template>

<script>
export default {
  computed: {
    cssCls() {
        // 访问 Vuex Stroe 中的状态数据 num
      let success = this.$store.state.num < 60
      let danger = this.$store.state.num >= 60
      return {
        'alert-success': success, // 如果success = true, 那么div上将会应用 alert-success 类样式
        'alert-danger': danger,// 如果 danger = true, 那么div上将会应用 alert-danger 类样式
      }
    },
  },
}
</script>

程序启动后,会看到 当随机产生的数据 < 60 的时候,右侧显示为红色, >= 60 的时候,显色为绿色

也就是说 两个兄弟组件,通过共享 Vuex store 中的状态数据,实现了通信。 当Vuex store中的数据发生改变的时候,所有订阅它的组件都会响应,从而实现数据共享。但是如果使用全局变量实现共享,则不会有响应式的效果。

4. mapState

组件中可以通过 this.$store.state.xxx 来访问状态数据,可以通过辅助函数来生成计算属性,方便对状态数据的读取,比如在 NumShow 组件中有如下代码:

<script>
import { mapState } from 'vuex'
export default {
  computed: mapState({
      num : state=>{return state.num}
  }),
}
</script>

computed 后面是计算属性,只不过计算属性 由 mapState函数来生成的。 num 是生成的计算属性的名称,后面是计算属性要执行的内容,上面等效的代码是:

<script>
import { mapState } from 'vuex'
export default {
  computed: {
      num(){
          return this.$store.state.num
      }
  },
}
</script>

可以这样来理解mapState: 需要计算属性的时候,可以通过配置的方式由 mapState 函数来生成:

mapState({
    计算属性名: state=>{  计算属性要执行的逻辑  }
})

可以看到,在“计算属性要执行的逻辑” 代码块中,可以直接使用 state来获取 状态数据了。 当要使用计算属性的时候,由Vuex来调用后面的那个箭头函数,调用的时候 Vuex会传递 state到函数中。

问题来了,组件的整个计算属性都由 mapState() 来生成的话,那么组件的其它计算属性又与 Vuex无关,那这个计算属性该如何写呢?

  // 如果还要定义其它计算属性该怎么写?
  computed: mapState({
      num : state=>{return state.num}
  }),

一种写法是继续在 mapState中进行配置,大不了不使用 state参数

  computed: mapState({
      num : state=>{return state.num},
      myProp: state=>{ 计算属性要执行的代码 }
  }),

还有一种写法,这需要用到 ES6 的"展开" 运算符 “…” . 相当于把 mapState() 生成的对象 “展开” 到了 computed 指向的对象中了。

  computed: {
  	myProp(){
  	    计算属性要执行的代码
  	},
  	...mapState({
  		计算属性: state=>{ 要执行的代码 }
  	})
  }

在使用 mapState() 函数生成计算属性的时候,如果计算属性刚好与 状态数据名称一样,还可以简化成这样:

  computed: mapState({
      "num",  
      myProp: state=>{ 计算属性要执行的代码 }
  }),

这里的计算属性名称num 刚好与状态数据名称一致,则直接使用字符串“num” 即可。字符串“num” 等效的代码是 : " num : state=>{return state.num}"

如果store中的状态数据比较多,而刚好计算属性又与 状态数据名一样,就可以简化成这样:

  computed: mapState(['num','v1'])

5. Getter

如果store 中的状态数据需要经过复杂的加工后取得一个值,那么可以在stroe的定义中定义getter,如在上面的 src/store/index.js文件中可以这样定义:

import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
const store = new Vuex.Store({
    state: {...},
    mutations: {...},
    getters: {
        doneTodos: state => { 执行逻辑 }
  }
})
export default store

可以把 Vuex中的Getter立即成 stroe的计算属性。

那么在组件中就可以这样来访问Getter了:

this.$store.getters.doneTodos

Getter也有辅助函数mapGetters 来快速生成组件中的计算属性

更复杂的用法参看文档

6. Mutation

​ 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})

官方文檔

7. Action

Action与 Mutation类似:

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

不同:

mutationsactions
触发this.$store.commit(名称,数据)store.dispatch(名称,数据)
状态数据修改可直接修改通过context.commit (mutation名称), 不可直接修改数据
同步/异步只能同步执行可以异步执行

Action 提供同样提供了 mapActions 辅助函数

参考文档

8. Module模块

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 的状态

参考文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

paopao_wu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值