Vuex的基础知识

208 阅读4分钟

Vuex

vuex 是终极的组件之间的数据共享方案,在企业级的 vue 项目开发中,vuex 可以让组件之间的数据共享变得高效、清晰且易于维护

小范围的数据共享还是建议使用之前父向子等传值

​ 一般情况下,只有组件之间共享的数据才有必要存储到 vuex 中,对于组件中的私有数据,依旧存储在组件自身的 data 中即可

优点

  • 能够在 vuex 中集中管理共享的数据,易于开发和后期的维护
  • 能够高效地实现组件之间的数据共享,提高开发效率
  • 存储在 vuex 中的数据都是响应式的,能够实时保持数据与页面的同步

安装和配置

npm i vuex -S

src 目录中创建一个 store 文件夹,在 store 文件夹中的 index.js 中配置

import Vue from 'vue'
import Vuex from 'vuex'
// 需要使用插件一次
Vue.use(Vuex)
// state: 窗口存储数据的地方
const state = {}
// mutations: 修改 state 的唯一手段
const mutations = {}
// action: 处理 action 可以书写自己的业务逻辑,也可以处理异步
const actions = {}
// getters: 理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
const getters = {}

// 对外暴露 Store 类的一个实例
export default new Vuex.Store({
  state,
  mutations,
  actions,
  getters
})

在到 main.js 中注册

import Vue from 'vue'
import App from './App.vue'

// 引入路由
import router from '@/router'
// 引入仓库
import store from '@/store'

// productionTip设置为 false ,可以阻止 vue 在启动时生成生产提示
// 开发环境下,Vue 会提供很多警告来帮你对付常见的错误与陷阱。而在生产环境下,这些警告语句却没有用,反而会增加应用的体积。此外,有些警告检查还有一些小的运行时开销,这在生产环境模式下是可以避免的
Vue.config.productionTip = false

new Vue({
  // 注册路由:当这里书写 router 的时候,组件身上都有 $route,$router 属性
  router,
  // 注册仓库:组件实例的身上会多一个属性 $store 
  store,
  render: h => h(App),
}).$mount('#app')

核心概念

State

示例1

state 提供唯一的公共数据源,所有共享的数据都要统一放到 store 文件夹的 state 中进行存储

export default new Vuex.Store({
  state: {
    count: 0
  },
})

// 或者
const store = new Vuex.store({
	state: { count: 0 }
})

组件访问 state 中数据的第一种方式

this.$store.state.全局数据名称
<template>
  <div>
    <h3>当前最新的count值为:{{ $store.state.count }}</h3>
  </div>
</template>

示例2

第二种方式

// 1.从 vuex 中按需导入 mapState 函数
import { mapState } from 'vuex'

通过导入的 mapState 函数,将当前组件需要的全局数据映射为当前组件的 computed 计算属性

computed: {
	// mapState 函数返回的是一个对象,使用对象展开运算符将它展开
	...mapState(['count'])
}

如下

<template>
  <div>
    <h3>当前最新的count值为:{{ count }}</h3>
    <button>-1</button>
  </div>
</template>

<script>
import { mapState } from 'vuex'

export default {
  data () {
    return {}
  },
  computed: {
    // 里面的 count 相当于一个计算属性
    ...mapState(['count'])
  }
}
</script>

<style></style>

注意: 在组件中这样写没有报错,但是 vuex 中是不允许在组件中直接修改 store 中的数据,需要使用 Mutation

Mutation

Mutation 用于变更 Store 中的数据

  • 只能通过 mutation 变更 Store 数据,不可以直接操作 Store中的数据
  • 通过这种方式虽然操作繁琐,但是可以集中监控所有数据的变化
const store = new Vuex.Store({
	state: {
		count: 0
	},
	mutations: {
	add (state) {
		// 变更状态
		state.count++
		}
	}
})

示例1

第一种调用方式

store 文件夹的 index.js

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    add (state) {
      state.count++
    },
    addn (state, n) {
      state.count += n
    }
  }
})

组件中

<template>
  <div>
    <h3>当前最新的count值为:{{ $store.state.count }}</h3>
    <button @click="btnHandler1">+1</button>
    <button @click="btnHandler2">+n</button>
  </div>
</template>

<script>
export default {
  ...
  methods: {
    btnHandler1 () {
      this.$store.commit('add')
    },
    btnHandler2 () {
      // commit 的作用就是调用某个 mutation 函数
      this.$store.commit('addn', 10)
    }
  }
}
</script>

示例2

第二种调用方式

store 文件夹的 index.js

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    sub (state) {
      state.count--
    },
    subn (state, n) {
      state.count -= n
    }
  }
})

组件中

<template>
  <div>
    <h3>当前最新的count值为:{{ count }}</h3>
    <button @click="btnHander1">-1</button>
    <button @click="btnHander2">-n</button>
  </div>
</template>

<script>
import { mapState, mapMutations } from 'vuex'

export default {
  ...
  computed: {
    ...mapState(['count'])
  },
  methods: {
    ...mapMutations(['sub', 'subn']),
    btnHander1 () {
      this.sub()
    },
    btnHander2 () {
      this.subn(10)
    }
  }
}
</script>

注意:在 mutation 不能进行异步操作(setTimeout,Ajax,文件操作,promise,回调函数等等)

Action

Action 用于处理异步任务

如果通过异步操作变更数据,必须通过 Action,而不能使用 mutation ,但是 Action 中还是要通过触发 mutation 的方式间接变更数据

示例1

触发 action 的第一种方式

store 文件夹中的 index.js

export default new Vuex.Store({
 ...
 mutations: {
    add (state) {
      state.count++
    },
    addn (state, n) {
      state.count += n
    }
  },
  actions: {
    addAsync (context) {
      setTimeout(() => {
        context.commit('add')
      }, 1000)
    },
    addnAsync (context, n) {
      setTimeout(() => {
        context.commit('addn', n)
      }, 1000)
    }
  }
})

组件中调用

<template>
  <div>
    <h3>当前最新的count值为:{{ $store.state.count }}</h3>

    <button @click="btnHandler3">+1 Async</button>
    <button @click="btnHandler4">+n Async</button>
  </div>
</template>

<script>
export default {
  ...
  methods: {
    // 异步的自增加1
    btnHandler3 () {
      // 这里的 dispatch 专门用来触发 action
      this.$store.dispatch('addAsync')
    },
   	btnHandler4 () {
      this.$store.dispatch('addnAsync', 10)
    }
  }
}
</script>

示例2

触发 action 的第二种方式

store 文件夹的 index.js

export default new Vuex.Store({
  ...
  mutations: {
    sub (state) {
      state.count--
    },
    subn (state, n) {
      state.count -= n
    }
  },
  actions: {
    subAsync (context) {
      setTimeout(() => {
        context.commit('sub')
      })
    },
    subnAsync (context, n) {
      setTimeout(() => {
        context.commit('subn', n)
      })
    }
  }
})

在组件中使用

<template>
  <div>
    <h3>当前最新的count值为:{{ count }}</h3>
    <button @click="subAsync">-1 Async</button>
    <button @click="subnAsync(10)">-n Async</button>
  </div>
</template>

<script>
import { mapState,mapActions } from 'vuex'

export default {
  ...
  computed: {
    ...mapState(['count'])
  },
  methods: {
    ...mapActions(['subAsync', 'subnAsync']),
    btnHander1 () {
      this.sub()
    }
  }
}
</script>

Getter

Getter 用于对 store 中的数据进行加工处理形成新的数据

  • Getter 可以对 Store 中已有的数据加工之后形成新的数据,类似于 Vue 的计算属性
  • store 中数据发生变化,Getter 的数据也会跟着变化

示例1

使用 getters 的的使用方式与 state 差不多

第一种方式

store 文件夹中

export default new Vuex.Store({
  state: {
    count: 0
  },
  ...
  getters: {
    showNum (state) {
      return '当前最新的数据是: ' + state.count
    }
  }
})

组件中使用

<h3>{{ $store.getters.showNum }}</h3>

示例2

第二种使用方式

组件中使用

<h3>{{ showNum }}</h3>

<script>
import { mapGetters } from 'vuex'

export default {
  ...
  computed: {
    ...mapGetters(['showNum'])
  }
}
</script>