聊聊 Vuex

766 阅读2分钟

一、什么是 Vuex ?

Vuex 可以理解为一种开发模式或框架。比如 PHPthinkphp, javaspring 等。通过状态(数据源)集中管理驱动组件的变化(好比 springIOC 容器对 bean 进行集中管理)

二、Vuex 的5个核心属性

1、state

state 为单一状态树,在 state 中需要定义我们所需要管理的数组、对象、字符串等等,只有在这里定义了,在 Vue.js 的组件中才能获取你定义的这个对象的状态。

2、getter

getter 优点类似于 Vue.js 的计算属性,当我们需要从 storestate 中派生出一些状态,那么我们就需要使用 getter, getter 会接收 state 作为第一个参数,而且 getter 的返回值会根据它的依赖被缓存起来,只有 getter 中的依赖值(state 中的某个需要派生状态的值)发生改变的时候才会被重新计算。

3、mutation

更改 storestate 状态的唯一方法就是提交 mutation ,就很类似事件。每个 mutation 都有一个字符串类型的事件类型和一个回调函数,我们需要改变 state 的值就要在回调函数中改变。我们要执行这个回调函数,那么我们需要执行一个相应的调用方法:store.commit

4、action

action 可以提交 mutation ,在 action 中可以执行 store.commit,而且 action 可以有任何的异步操作。在页面中如果我们需要调用这个 action ,则需要执行 store.dispatch

5、module

module 其实只是解决了当 state 中很复杂臃肿的时候,module 可以将 store 分割成模块,每个模块中拥有自己的 statemutationactiongetter

三、Vuex 的使用示例

1、Vuex 无模块化(适合小项目)

1.1、src/store/index.js

// 0.导入 Vue
import Vue from 'vue'
// 1.下包 vuex
// 2.导入 Vuex
import Vuex from 'vuex'
// 3.安装 vue 插件
Vue.use(Vuex)

// 4.创建 Store 实例对象
const store = new Vuex.Store({
  // ◆state 全局共享的数据
  state: {
    count: 1,
    price: 20
  },
  // ◆mutations 同步操作
  mutations: {
    addCount (state, payload) {
      state.count += payload
    }
  },
  // ◆actions 异步操作
  actions: {
    addAsync (context, payload) {
      setTimeout(() => {
        context.commit('addCount', payload)
      }, 1000)
    }
  },
  // ◆getters 计算属性
  getters: {
    totalPrice (state) {
      return state.count * state.price
    }
  },
  // ◆modules 模块化
  modules: {
  }

})
// 5.默认导出
export default store

1.2、main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
// 6.导入 store
import store from './store'

Vue.config.productionTip = false

// 7.把 store 挂载到 vue 实例上
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

1.3、页面使用

法1:直接使用 this.$store(推荐)

<template>
  <div>
    <!-- ◆在模板中直接使用 this.$store.state.数据 (this可以省略) -->
    <p>商品价格:{{ $store.state.price }}元/件</p>
    <p>购买数量:{{ $store.state.count }}件</p>
    <button @click="addHandler">同步++</button>
    <button @click="addAsyncHandler">异步++</button>
    <!-- ◆在模板中直接使用 this.$store.getters.数据 (this可以省略) -->
    <p>商品总价:{{ $store.getters.totalPrice }}元</p>
  </div>
</template>

<script>
export default {
  methods: {
    addHandler () {
      // ◆直接使用 commit 方法: this.$store.commit('函数名',载荷)
      this.$store.commit('addCount', 5)
    },
    addAsyncHandler () {
      // ◆直接使用 dispatch 方法: this.$store.dispatch('函数名',载荷)
      this.$store.dispatch('addAsync', 10)
    }
  }
}
</script>

法2:利用辅助函数

<template>
  <div>
    <p>商品价格:{{ price }}元/件</p>
    <p>购买数量:{{ count }}件</p>
    <button @click="addHandler">同步++</button>
    <button @click="addAsyncHandler">异步++</button>
    <p>商品总价:{{ totalPrice }}</p>
  </div>
</template>

<script>
// 按需导入辅助函数
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {
  computed: {
    // ◆在 computed 中映射辅助函数 ...mapState(['要映射的数据'])
    ...mapState(['count', 'price']),
    // ◆在 computed 中映射辅助函数 ...mapGetters(['要映射的计算属性'])
    ...mapGetters(['totalPrice'])
  },
  methods: {
    // ◆在 methods 中映射辅助函数 ...mapMutations(['函数名'])
    ...mapMutations(['addCount']),
    // ◆在 methods 中映射辅助函数 ...mapActions(['函数名'])
    ...mapActions(['addAsync']),
    addHandler () {
      // 载荷只能有一个, 如果需要传递多个参数,就要用数组或对象
      // 调用 addCount 函数
      this.addCount(5)
    },
    addAsyncHandler () {
      // 载荷只能有一个, 如果需要传递多个参数,就要用数组或对象
      // 调用 addAsync 函数
      this.addAsync(10)
    }
  }
}
</script>

2、vuex 有模块化(适合大项目)

2.1、src/store/modules/goods.js 子模块

// ◆默认导出子模块 user
export default {
  // ◆◆◆子模块必须开启命名空间
  namespaced: true,
  // ◆state 全局共享的数据
  state: () => ({
    count: 1,
    price: 20

  }),
  // ◆mutations 同步操作
  mutations: {
    addCount (state, payload) {
      state.count += payload
    }

  },
  // ◆actions 异步操作
  actions: {
    addAsync (context, payload) {
      setTimeout(() => {
        context.commit('addCount', payload)
      }, 1000)
    }

  },
  // ◆getters 计算属性
  getters: {
    totalPrice (state) {
      return state.count * state.price
    }

  },
  // ◆modules 模块化 (子模块也可以导入它的子模块)
  modules: {
  }
}

2.2、src/store/index.js

// 0.导入 Vue
import Vue from 'vue'
// 1.下包 vuex
// 2.导入 Vuex
import Vuex from 'vuex'
// ◆导入子模块 goods
import goods from './modules/goods'
// 3.安装 vue 插件
Vue.use(Vuex)

// 4.创建 Store 实例对象
const store = new Vuex.Store({
  // ◆modules 模块化
  modules: {
    goods
  }
})
// 5.默认导出
export default store

2.3、main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
// 6.导入 store
import store from './store'

Vue.config.productionTip = false

// 7.把 store 挂载到 vue 实例上
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

2.4、页面使用

法1:利用辅助函数(推荐)

<template>
  <div>
    <p>商品价格:{{price }}元/件</p>
    <p>购买数量:{{count }}件</p>
    <button @click="addHandler">同步++</button>
    <button @click="addAsyncHandler">异步++</button>
    <p>商品总价:{{ totalPrice }}元</p>
  </div>
</template>

<script>
// 按需导入辅助函数
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {
  computed: {
    // ◆在 computed 中映射辅助函数 ...mapState('模块名',['要映射的数据'])
    ...mapState('goods', ['price', 'count']),
    // ◆在 computed 中映射辅助函数 ...mapGetters('模块名',['要映射的计算属性'])
    ...mapGetters('goods', ['totalPrice'])
  },
  methods: {
    // ◆在 methods 中映射辅助函数 ...mapMutations('模块名',['函数名'])
    ...mapMutations('goods', ['addCount']),
    // ◆在 methods 中映射辅助函数 ...mapActions('模块名',['函数名'])
    ...mapActions('goods', ['addAsync']),
    addHandler () {
      // 调用 addCount 函数
      this.addCount(5)
    },
    addAsyncHandler () {
      // 调用 addAsync 函数
      this.addAsync(10)
    }
  }
}
</script>

法2、直接使用 this.$store

<template>
  <div>
    <!-- ◆在模板中直接使用 this.$store.state.模块名.数据(this可以省略) -->
    <p>商品价格:{{ $store.state.goods.price }}元/件</p>
    <p>购买数量:{{ $store.state.goods.count }}件</p>
    <button @click="addHandler">同步++</button>
    <button @click="addAsyncHandler">异步++</button>
    <!-- ◆ this.$store.getters['模块名/映射的计算属性'] -->
    <p>商品总价:{{ $store.getters['goods/totalPrice']}}元</p>
  </div>
</template>

<script>
export default {
  methods: {
    addHandler () {
      // ◆直接使用 commit 方法: this.$store.commit('模块名/函数名',载荷)
      this.$store.commit('goods/addCount', 5)
    },
    addAsyncHandler () {
      // ◆直接使用 dispatch 方法: this.$store.dispatch('模块名/函数名',载荷)
      this.$store.dispatch('goods/addAsync', 10)
    }
  }
}
</script>