vuex store 怎么获得.dispatch 怎么获得

  Vuex 的核心是 store, 它是一个通过 Vuex.Store 构造函数生成的对象。为什么它会是核心呢?因为我们调用这个构造函数创建store 对象的时候,给它传递参数中包装了state, mutation , action 等核心内容。看一下官网的例子
const store = new Vuex.Store({
mutations: {
increment (state) {
state.count++
  Vuex 的思想是 当我们在页面上点击一个按钮,它会处发(dispatch)一个action, action 随后会执行(commit)一个mutation, mutation 立即会改变state,& state 改变以后,我们的页面会state 获取数据,页面发生了变化。 Store 对象,包含了我们谈到的所有内容,action, state, mutation,所以是核心了。
  state: &When we say state we are talking about a group of dozens of variables to store data, 就是我们页面中用到的各种变量。在写vue 页面中,我们经常写 {{mes}}, mes 就是一个状态,因为它变化,页面就会重新渲染,页面也会发生改变,改变就意状态改变了。
  写一个简单的小项目体验一下, 这里用vue-cli webpack-simple
    1, vue init webpack-simple count, count 是项目文件名。
& & & & & &2,cd count &&& &cnpm install &&& &npm run dev, 可以看到大的vue logo ,证明项目启动成功。
& & & & & &3,webpack-simple默认没有安装vuex, 所以要安装 在命令行中按两次ctrl+c 结束服务器,cnpm install& vuex –save& 安装vuex.
& & & & & &4, 打开app.vue,删除掉除图片在内的所有内容。
& & & & & &5,在src 目录下新建一个display.vue 组件,用于展示;新建一个increment 组件进行操作。
  display.vue:
&template&
&h3&Count is 0&/h3&
&/template&
&style scoped&
font-size: 30
  increment.vue
&template&
&button&+1&/button&
&button&-1&/button&
&/template&
&style scoped&
width: 100
height: 100
font-size: 30
&template&
&div id="app"&
&img src="./assets/logo.png"&
&display&&/display&
&increment&&/increment&
&/template&
import display from "./display.vue";
import increment from "./increment.vue";
export default {
components: {
text-align:
项目很简单,就是单击按钮+-1. 可以想到,我们这个项目中只有一个变量,就是count, 现在它是0.
   用vuex 进行状态管理,store 是vuex的核心,所以命名为store.js. 在src 目录下新建store.js 文件(如下)。可以看到使用vuex 之前,要告诉 vue 使用它,Vue.use(Vuex); 我们这里只有一个变量count 需要管理,所以在创建 store 对象的时候,给构造函数传参,state 下面只有一个count, 且初始化为0。
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const store = new Vuex.Store({
})export default store
  现在所有的状态,也就是变量都放到了store.js中,那我们组件怎么才能获取到状态修值呢?这里有两个步骤需要操作
    1, vue 提供了注入机制,就是把我们的store 对象注入到根实例中。vue的根实例就是 new Vue &构造函数,然后在所有的子组件中,this.$store 来指向store 对象。在store.js 中,我们export store, 把store已经暴露出去了,new Vue() 在main.js中,所以直接在main.js 中引入store &并注入即可。
import Vue from 'vue'
import App from './App.vue'
import store from "./store.js"
// 引入store 对象
el: '#app',
// 注入到根实例中
render: h =& h(App)
  2, 在子组件中,用computed 属性, computed 属性是根据它的依赖自动更新的。所以只要store中的state 发生变化,它就会自动变化。在display.vue 中作下面的更改, 子组件中 this.$store 就是指向store 对象。我们把 store.js 里面的count 变为8, 页面中就变为了8。
&template&
&h3&Count is {{count}}&/h3&
&/template&
export default {
computed: {
count () {
return this.$store.state.count
  3, 通过computed属性可以获取到状态值,但是组件中每一个属性(如:count)都是函数,如果有10个,那么就要写10个函数,且重复写10遍return this.$store.state,不是很方便。vue 提供了 mapState 函数,它把state 直接映射到我们的组件中。
当然使用mapState 之前要先引入它。它两种用法,或接受一个对象,或接受一个数组。还是在display.vue 组件下。
  对象用法如下:
import {mapState} from "vuex";
// 引入mapState
export default {      // 下面这两种写法都可以
computed: mapState({
count: state =& state.count
// 组件内的每一个属性函数都会获得一个默认参数state, 然后通过state 直接获取它的属性更简洁
count: 'count'         // 'count' 直接映射到state 对象中的count, 它相当于 this.$store.state.count,
  数组方式:上面的第二种方式count: 'count',还是有点麻烦,因为要写两遍。如果我们组件中的属性和state 中的属性名称一样,我们不想改名字,那就把直接把属性名写在数组中。
import {mapState} from "vuex";
export default {
computed: mapState([
  4, &还有最后一个问题,如果我们组件内部也有computed 属性怎么办?它又不属于mapState 中。那就用到了对象分割,把mapState函数生成的对象再分割成一个个的,就像最开始的时候,我们一个一个罗列计算属性,有10个属性,我们就写10个函数。
es6中的... 就是分割用的,但是只能分割数组。在ECMAScript stage-3 阶段它可以分割对象,所以这时还要用到babel-stage-3; &npm install babel-preset-stage-3 --save-dev, 安装完全后,一定不要忘记在babelrc 就是babel 的配置文件中,写入stage-2,
否则一直报错。在页面中添加个 p 标签,显示我们组件的计算熟悉
"presets": [
"es2015": { "modules": false }
// 一定不要忘记
display.vue 组件更改后
&template&
&h3&Count is {{count}}&/h3&
&p&组件自己的内部计算属性 {{ localComputed }}&/p&
&/template&
import {mapState} from "vuex";
export default {
computed: {
localComputed () {
return this.count + 10;
...mapState({
count: "count"
&把store.js 中state.count 改为20, &查看一个效果
阅读(...) 评论()Vuex 2.0 源码分析(下)
Vuex 2.0 源码分析(下)
大家好,我叫黄轶,来自滴滴公共前端团队,最近在幕课网上线了一门 Vue.js 的实战课程——,同时,我们团队最近写了一本书 ——,内容丰富,由浅入深。不过有一些同学反馈说缺少 Vuex 的介绍的章节。既然 Vue.js 2.0 已经正式发布了,我们也要紧跟步伐,和大家聊一聊 Vuex 2.0。本文并不打算讲官网已有的内容,而会通过源码分析的方式,让同学们从另外一个角度认识和理解 Vuex 2.0。
Vuex 除了提供我们 Store 对象外,还对外提供了一系列的辅助函数,方便我们在代码中使用 Vuex,提供了操作 store 的各种属性的一系列语法糖,下面我们来一起看一下:
工具函数会将 store 中的 state 映射到局部计算属性中。为了更好理解它的实现,先来看一下它的使用示例:
// vuex 提供了独立的构建工具函数 Vuex.mapState
import { mapState } from 'vuex'
export default {
computed: mapState({
// 箭头函数可以让代码非常简洁
count: state =& state.count,
// 传入字符串 'count' 等同于 `state =& state.count`
countAlias: 'count',
// 想访问局部状态,就必须借助于一个普通函数,函数中使用 `this` 获取局部状态
countPlusLocalState (state) {
return state.count + this.localCount
当计算属性名称和状态子树名称对应相同时,我们可以向 mapState 工具函数传入一个字符串数组。
computed: mapState([
// 映射 this.count 到 this.$store.state.count
通过例子我们可以直观的看到,mapState 函数可以接受一个对象,也可以接收一个数组,那它底层到底干了什么事呢,我们一起来看一下源码这个函数的定义:
export function mapState (states) {
const res = {}
normalizeMap(states).forEach(({ key, val }) =& {
res[key] = function mappedState () {
return typeof val === 'function'
? val.call(this, this.$store.state, this.$store.getters)
: this.$store.state[val]
return res
函数首先对传入的参数调用 normalizeMap 方法,我们来看一下这个函数的定义:
function normalizeMap (map) {
return Array.isArray(map)
? map.map(key =& ({ key, val: key }))
: Object.keys(map).map(key =& ({ key, val: map[key] }))
这个方法判断参数 map 是否为数组,如果是数组,则调用数组的 map 方法,把数组的每个元素转换成一个 {key, val: key}的对象;否则传入的 map 就是一个对象(从 mapState 的使用场景来看,传入的参数不是数组就是对象),我们调用 Object.keys 方法遍历这个 map 对象的 key,把数组的每个 key 都转换成一个 {key, val: key}的对象。最后我们把这个对象数组作为 normalizeMap 的返回值。
回到 mapState 函数,在调用了 normalizeMap 函数后,把传入的 states 转换成由 {key, val} 对象构成的数组,接着调用 forEach 方法遍历这个数组,构造一个新的对象,这个新对象每个元素都返回一个新的函数 mappedState,函数对 val 的类型判断,如果 val 是一个函数,则直接调用这个 val 函数,把当前 store 上的 state 和 getters 作为参数,返回值作为 mappedState 的返回值;否则直接把 this.$store.state[val] 作为 mappedState 的返回值。
那么为何 mapState 函数的返回值是这样一个对象呢,因为 mapState 的作用是把全局的 state 和 getters 映射到当前组件的 computed 计算属性中,我们知道在 Vue 中 每个计算属性都是一个函数。
为了更加直观地说明,回到刚才的例子:
import { mapState } from 'vuex'
export default {
computed: mapState({
// 箭头函数可以让代码非常简洁
count: state =& state.count,
// 传入字符串 'count' 等同于 `state =& state.count`
countAlias: 'count',
// 想访问局部状态,就必须借助于一个普通函数,函数中使用 `this` 获取局部状态
countPlusLocalState (state) {
return state.count + this.localCount
经过 mapState 函数调用后的结果,如下所示:
import { mapState } from 'vuex'
export default {
computed: {
return this.$store.state.count
countAlias() {
return this.$store.state['count']
countPlusLocalState() {
return this.$store.state.count + this.localCount
我们再看一下 mapState 参数为数组的例子:
computed: mapState([
// 映射 this.count 到 this.$store.state.count
经过 mapState 函数调用后的结果,如下所示:
computed: {
return this.$store.state['count']
mapGetters
mapGetters 工具函数会将 store 中的 getter 映射到局部计算属性中。它的功能和 mapState 非常类似,我们来直接看它的实现:
export function mapGetters (getters) {
const res = {}
normalizeMap(getters).forEach(({ key, val }) =& {
res[key] = function mappedGetter () {
if (!(val in this.$store.getters)) {
console.error(`[vuex] unknown getter: ${val}`)
return this.$store.getters[val]
return res
mapGetters 的实现也和 mapState 很类似,不同的是它的 val 不能是函数,只能是一个字符串,而且会检查 val in this.$store.getters 的值,如果为 false 会输出一条错误日志。为了更直观地理解,我们来看一个简单的例子:
import { mapGetters } from 'vuex'
export default {
computed: {
// 使用对象扩展操作符把 getter 混入到 computed 中
...mapGetters([
'doneTodosCount',
'anotherGetter',
经过 mapGetters 函数调用后的结果,如下所示:
import { mapGetters } from 'vuex'
export default {
computed: {
doneTodosCount() {
return this.$store.getters['doneTodosCount']
anotherGetter() {
return this.$store.getters['anotherGetter']
再看一个参数 mapGetters 参数是对象的例子:
computed: mapGetters({
// 映射 this.doneCount 到 store.getters.doneTodosCount
doneCount: 'doneTodosCount'
经过 mapGetters 函数调用后的结果,如下所示:
computed: {
doneCount() {
return this.$store.getters['doneTodosCount']
mapActions
mapActions 工具函数会将 store 中的 dispatch 方法映射到组件的 methods 中。和 mapState、mapGetters 也类似,只不过它映射的地方不是计算属性,而是组件的 methods 对象上。我们来直接看它的实现:
export function mapActions (actions) {
const res = {}
normalizeMap(actions).forEach(({ key, val }) =& {
res[key] = function mappedAction (...args) {
return this.$store.dispatch.apply(this.$store, [val].concat(args))
return res
可以看到,函数的实现套路和 mapState、mapGetters 差不多,甚至更简单一些, 实际上就是做了一层函数包装。为了更直观地理解,我们来看一个简单的例子:
import { mapActions } from 'vuex'
export default {
methods: {
...mapActions([
'increment' // 映射 this.increment() 到 this.$store.dispatch('increment')
...mapActions({
add: 'increment' // 映射 this.add() to this.$store.dispatch('increment')
经过 mapActions 函数调用后的结果,如下所示:
import { mapActions } from 'vuex'
export default {
methods: {
increment(...args) {
return this.$store.dispatch.apply(this.$store, ['increment'].concat(args))
add(...args) {
return this.$store.dispatch.apply(this.$store, ['increment'].concat(args))
mapMutations
mapMutations 工具函数会将 store 中的 commit 方法映射到组件的 methods 中。和 mapActions 的功能几乎一样,我们来直接看它的实现:
export function mapMutations (mutations) {
const res = {}
normalizeMap(mutations).forEach(({ key, val }) =& {
res[key] = function mappedMutation (...args) {
return this.$mit.apply(this.$store, [val].concat(args))
return res
函数的实现几乎也和 mapActions 一样,唯一差别就是映射的是 store 的 commit 方法。为了更直观地理解,我们来看一个简单的例子:
import { mapMutations } from 'vuex'
export default {
methods: {
...mapMutations([
'increment' // 映射 this.increment() 到 this.$mit('increment')
...mapMutations({
add: 'increment' // 映射 this.add() 到 this.$mit('increment')
经过 mapMutations 函数调用后的结果,如下所示:
import { mapActions } from 'vuex'
export default {
methods: {
increment(...args) {
return this.$mit.apply(this.$store, ['increment'].concat(args))
add(...args) {
return this.$mit.apply(this.$store, ['increment'].concat(args))
Vuex 的 store 接收 plugins 选项,一个 Vuex 的插件就是一个简单的方法,接收 store 作为唯一参数。插件作用通常是用来监听每次 mutation 的变化,来做一些事情。
在 store 的构造函数的最后,我们通过如下代码调用插件:
import devtoolPlugin from './plugins/devtool'
// apply plugins
plugins.concat(devtoolPlugin).forEach(plugin =& plugin(this))
我们通常实例化 store 的时候,还会调用 logger 插件,代码如下:
import Vue from 'vue'
import Vuex from 'vuex'
import createLogger from 'vuex/dist/logger'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
plugins: debug ? [createLogger()] : []
在上述 2 个例子中,我们分别调用了 devtoolPlugin 和 createLogger() 2 个插件,它们是 Vuex 内置插件,我们接下来分别看一下他们的实现。
devtoolPlugin
devtoolPlugin 主要功能是利用 Vue 的开发者工具和 Vuex 做配合,通过开发者工具的面板展示 Vuex 的状态。它的源码在 src/plugins/devtool.js 中,来看一下这个插件到底做了哪些事情。
const devtoolHook =
typeof window !== 'undefined' &&
window.__VUE_DEVTOOLS_GLOBAL_HOOK__
export default function devtoolPlugin (store) {
if (!devtoolHook) return
store._devtoolHook = devtoolHook
devtoolHook.emit('vuex:init', store)
devtoolHook.on('vuex:travel-to-state', targetState =& {
store.replaceState(targetState)
store.subscribe((mutation, state) =& {
devtoolHook.emit('vuex:mutation', mutation, state)
我们直接从对外暴露的 devtoolPlugin 函数看起,函数首先判断了devtoolHook 的值,如果我们浏览器装了 Vue 开发者工具,那么在 window 上就会有一个 __VUE_DEVTOOLS_GLOBAL_HOOK__ 的引用, 那么这个 devtoolHook 就指向这个引用。
接下来通过 devtoolHook.emit('vuex:init', store) 派发一个 Vuex 初始化的事件,这样开发者工具就能拿到当前这个 store 实例。
接下来通过 devtoolHook.on('vuex:travel-to-state', targetState =& { store.replaceState(targetState) })监听 Vuex 的 traval-to-state 的事件,把当前的状态树替换成目标状态树,这个功能也是利用 Vue 开发者工具替换 Vuex 的状态。
最后通过 store.subscribe((mutation, state) =& { devtoolHook.emit('vuex:mutation', mutation, state) }) 方法订阅 store 的 state 的变化,当 store 的 mutation 提交了 state 的变化, 会触发回调函数——通过 devtoolHook 派发一个 Vuex mutation 的事件,mutation 和 rootState 作为参数,这样开发者工具就可以观测到 Vuex state 的实时变化,在面板上展示最新的状态树。
loggerPlugin
通常在开发环境中,我们希望实时把 mutation 的动作以及 store 的 state 的变化实时输出,那么我们可以用 loggerPlugin 帮我们做这个事情。它的源码在 src/plugins/logger.js 中,来看一下这个插件到底做了哪些事情。
// Credits: borrowed code from fcomb/redux-logger
import { deepCopy } from '../util'
export default function createLogger ({
collapsed = true,
transformer = state =& state,
mutationTransformer = mut =& mut
return store =& {
let prevState = deepCopy(store.state)
store.subscribe((mutation, state) =& {
if (typeof console === 'undefined') {
const nextState = deepCopy(state)
const time = new Date()
const formattedTime = ` @ ${pad(time.getHours(), 2)}:${pad(time.getMinutes(), 2)}:${pad(time.getSeconds(), 2)}.${pad(time.getMilliseconds(), 3)}`
const formattedMutation = mutationTransformer(mutation)
const message = `mutation ${mutation.type}${formattedTime}`
const startMessage = collapsed
? console.groupCollapsed
: console.group
startMessage.call(console, message)
} catch (e) {
console.log(message)
console.log('%c prev state', 'color: #9E9E9E; font-weight: bold', transformer(prevState))
console.log('%c mutation', 'color: #03A9F4; font-weight: bold', formattedMutation)
console.log('%c next state', 'color: #4CAF50; font-weight: bold', transformer(nextState))
console.groupEnd()
} catch (e) {
console.log('—— log end ——')
prevState = nextState
function repeat (str, times) {
return (new Array(times + 1)).join(str)
function pad (num, maxLength) {
return repeat('0', maxLength - num.toString().length) + num
插件对外暴露的是 createLogger 方法,它实际上接受 3 个参数,它们都有默认值,通常我们用默认值就可以。createLogger 的返回的是一个函数,当我执行 logger 插件的时候,实际上执行的是这个函数,下面来看一下这个函数做了哪些事情。
函数首先执行了 let prevState = deepCopy(store.state) 深拷贝当前 store 的 rootState。这里为什么要深拷贝,因为如果是单纯的引用,那么 store.state 的任何变化都会影响这个引用,这样就无法记录上一个状态了。我们来了解一下 deepCopy 的实现,在 src/util.js 里定义:
function find (list, f) {
return list.filter(f)[0]
export function deepCopy (obj, cache = []) {
// just return if obj is immutable value
if (obj === null || typeof obj !== 'object') {
return obj
// if obj is hit, it is in circular structure
const hit = find(cache, c =& c.original === obj)
if (hit) {
return hit.copy
const copy = Array.isArray(obj) ? [] : {}
// put the copy into cache at first
// because we want to refer it in recursive deepCopy
cache.push({
original: obj,
Object.keys(obj).forEach(key =& {
copy[key] = deepCopy(obj[key], cache)
return copy
deepCopy 并不陌生,很多开源库如 loadash、jQuery 都有类似的实现,原理也不难理解,主要是构造一个新的对象,遍历原对象或者数组,递归调用 deepCopy。不过这里的实现有一个有意思的地方,在每次执行 deepCopy 的时候,会用 cache 数组缓存当前嵌套的对象,以及执行 deepCopy 返回的 copy。如果在 deepCopy 的过程中通过 find(cache, c =& c.original === obj) 发现有循环引用的时候,直接返回 cache 中对应的 copy,这样就避免了无限循环的情况。
回到 loggerPlugin 函数,通过 deepCopy 拷贝了当前 state 的副本并用 prevState 变量保存,接下来调用 store.subscribe 方法订阅 store 的 state 的变。 在回调函数中,也是先通过 deepCopy 方法拿到当前的 state 的副本,并用 nextState 变量保存。接下来获取当前格式化时间已经格式化的 mutation 变化的字符串,然后利用 console.group 以及 console.log 分组输出 prevState、mutation以及 nextState,这里可以通过我们 createLogger 的参数 collapsed、transformer 以及 mutationTransformer 来控制我们最终 log 的显示效果。在函数的最后,我们把 nextState 赋值给 prevState,便于下一次 mutation。
Vuex 2.0 的源码分析到这就告一段落了,最后我再分享一下看源码的小心得:对于一个库或者框架源码的研究前,首先了解他们的使用场景、官网文档等;然后一定要用他,至少也要写几个小 demo,达到熟练掌握的程度;最后再从入口、API、使用方法等等多个维度去了解他内部的实现细节。如果这个库过于庞大,那就先按模块和功能拆分,一点点地消化。
最后还有一个问题,有些同学会问,源码那么枯燥,我们分析学习它的有什么好处呢?首先,学习源码有助于我们更深入掌握和应用这个库或者框架;其次,我们还可以学习到源码中很多编程技巧,可以迁移到我们平时的开发工作中;最后,对于一些高级开发工程师而言,我们可以学习到它的设计思想,对将来有一天我们也去设计一个库或者框架是非常有帮助的,这也是提升自身能力水平的非常好的途径。
本文原创发布于慕课网 ,转载请注明出处,谢谢合作!
相关标签:
分享即可 +1积分
请登录后,发表评论
评论(Enter+Ctrl)
评论加载中...
评论加载中...
这位童鞋很懒,什么也没有留下~~!
作者的热门手记
Copyright (C)
All Rights Reserved | 京ICP备 号-2今天,在我编写系统中一个模块功能的时候,由于我使用vuex存储数据的状态,并分模块存储。我是这样在存储文件中定义state,getters,actions,mutations的,我打算在不同模块文件都使用相同的方法名称,然后在页面中带上模块名进行访问:
import * as types from '../mutation-types'
const state = {
const getters = {
const actions = {
* 获得一页数据
page(context) {
* 获得一项信息
get(context) {
* 新增数据
create(context) {
* 更新数据
update(context) {
* 删除数据
delete(context) {
const mutations = {
export default {
导出为模块:
import country from "./modules/countryStore"
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
modules: {
//strict: debug,
strict: false,
//不启用严格模式
然后我发现,在使用模块属性时,在页面里只能使用state里定义的属性,其他都不能获得
import { mapState,mapGetters, mapActions } from 'vuex'
export default{
computed:mapState({
tableData: state =& state.country.countryDataSet
//不能获得
//mapGetters({
tableData: (getters) =& getters.country.countryDataSet
created(){
this.$store.actions.country.page //.dispatch("country.page")
//this.$store.dispatch("country.page")
这两种方法this.$store.dispatch("page")、this.$store.getters.countryDataSet(缺点是每个方法名都得是唯一的) 都是可以访问的,但是在一个大规范的应用中,应该存在不同模块中存在同名的方法,如:数据更新都使用update方法名,根据模块进行区分调用。这样开发起来也简单,代码也好理解。但是目前还没有找到使用模块来访问store中定义的方法的方法。
实验过多种方式之后,下面这种方式可以在使用时加上方法前缀。
//countryStore.js
export default {
/*//错误,没法使用命名空间
getPage(context) {
//正确,在vue文件中可以使用 this.$store.dispatch("country/getPage") 即可
["country/getPage"](context) {
mutations: {}
阅读(...) 评论()}

我要回帖

更多关于 vue store.dispatch 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信