vuex使用方式
store/index.js
import Vue from 'vue'
import Vuex from '@/vuex'
// import Vuex from 'vuex'
// import logger from 'vuex/dist/logger.js'
Vue.use(Vuex)
// // new Vue
let store = new Vuex.Store({
state: { // state = > data
name: 'zhufeng',
age: 12
},
mutations: { // method commit 同步更改状态
changeAge(state, payload) {
state.age += payload
}
},
actions: { // 异步操作 调用api接口 dispatch, 多次commit mutation
changeAge({ commit }, payload) {
setTimeout(() => {
commit('changeAge', payload);
}, 1000);
}
},
getters: { // 计算属性
myAge(state) {
return state.age + 10
}
},
strict: true, // 如果不是在mutation中操作的状态会发生警告
modules: { // 进行模块分割
// namespaced 能解决子模块和父模块的命名冲突文件 ,相当于增加了一个命名空间
// 如果没有namespaced 默认getters都会被定义到父模块上,
// mutations 会被合并在一起, 最终一起调用,有了命名空间就没有这个问题了
// 子模块的名字不能和父模块中的状态重名
a: {
namespaced: true,
state: {
name: 't1',
age: 10
},
// 所有的getters 都会被合并到跟上
getters: { // 首页一个模块 home 订单页一个模块 order 用户一个模块 user
myAge(state) {
return state.age + 20;
}
},
mutations: {
changeAge(state, payload) {
state.age += payload
}
},
modules: {
c: {
namespaced: true,
state: {
age: 100
},
mutations: {
changeAge(state, payload) {
state.age += payload
}
},
modules: {
d: {
namespaced: true,
state: {
age: 100
},
}
}
}
}
},
}
})
export default store;
1、 初始化
一开始Vue.use(Vuex): Vue.use就是使执行vuex的install方法
Vuex里面有个install方法, 用于执行install()
install.js
export let Vue;
function install(_Vue) {
Vue = _Vue;
Vue.mixin({
beforeCreate(){ //this代表的是每个组件实例
// 获取根组件上的store 将他共享给每个组件
// 每个组件中都应该有$store
let options= this.$options;
if(options.store){
// 根
// console.log('根',options.name)
this.$store = options.store
}else{
// 先保证他是一个子组件,并且父亲上有$store
if(this.$parent && this.$parent.$store){
this.$store = this.$parent.$store
}
}
}
})
}
// 父 this.$store -》 子 this.$store -》孙子 this.$store
export default install
install方法: 传入了_Vue, 是为了拿到全局vue对象,统一vue版本, 同时执行minxin方法,各个组件等到执行beforeCreate生命周期时:给每个vue实例获取根对象的store,每个vue实例就有$store对象了。
2、创建store对象 — 创建模块收集器对象
let store = new Vuex.Store({
...
})
这一步说明了Vuex.Store是一个构造函数
vuex/store.js
class Store {
constructor(options) {
// 对用户的模块进行整合
// 当前格式化完毕的数据 放到了this._modules里
this._modules = new ModuleCollection(options); // 对用户的参数进行格式化操作
this.wrapperGetters = {}
// 我需要将模块中的所有的getters,mutations,actions进行收集
this.mutations = {};
this.actions = {};
this._subscribes = [];
this._committing = false; // 默认不是在mutation中更改的
this.strict = options.strict;
// 没有namespace的时候 getters都放在根上 ,actions,mutations 会被合并数组
let state = options.state;
installModule(this, state, [], this._modules.root);
resetVM(this, state);
if (options.plugins) { // 说明用户使用了插件
options.plugins.forEach(plugin => plugin(this))
}
}
_withCommittting(fn) {
this._committing = true; // 如果true
fn(); // 函数是同步的 获取_commiting 就是true,如果是异步的那么就会变成false 就会打印日志
this._committing = false;
}
subscribe(fn) {
this._subscribes.push(fn);
}
replaceState(newState) { // 需要替换的状态
this._withCommittting(() => {
this._vm._data.$$state = newState; // 替换最新的状态, 赋予对象类型会被重新劫持
})
// 虽然替换了状态,但是mutation getter中的state在初始化的时候 已经被绑定死了老的状态
}
get state() {
return this._vm._data.$$state
}
commit = (mutationName, payload) => { // 发布
this.mutations[mutationName] && this.mutations[mutationName].forEach(fn => fn(payload))
}
dispatch = (actionName, payload) => {
this.actions[actionName] && this.actions[actionName].forEach(fn => fn(payload))
}
registerModule(path, module) { // 最终都转换成数组 register(['a','c'])
if (typeof path == 'string') path = [path];
// module 是用户直接写的
this._modules.register(path, module); // 模块的注册, 将用户给的数据放到树中
// 注册完毕后 ,在进行安装
// 将用户的module 重新安装
installModule(this, this.state, path, module.newModule);
// vuex内部重新注册的话 会重新生成实例, 虽然重新安装了 ,只解决了状态的问题,但是computed就丢失了
resetVM(this, this.state); // 销毁重来
}
}
一开始 this._modules = new ModuleCollection(options);
ModuleCollection是一个模块收集器:传入用户定义的所有选项options。
vuex/module/module-collection.js
import { forEach } from '../util'
import Module from './module';
class ModuleCollection {
constructor(options) {
// 对数据进行格式化操作
this.root = null;
this.register([],options); // 为了记录父子关系
}
getNamespace(path){ // [a,b,c]
// 返回一个字符串 a/b/c ''
let root = this.root
let ns = path.reduce((ns,key)=>{ // this.root.c.namespace
let module = root.getChild(key);
root = module;
return module.namespaced ? ns + key + '/' :ns
},'');
return ns;
}
register(path,rawModule){
let newModule = new Module(rawModule)
rawModule.newModule = newModule;// 自定义属性
if(path.length == 0){
this.root = newModule
}else{
// [a] // [a,c] => [a]
// 找父亲
let parent = path.slice(0,-1).reduce((memo,current)=>{
return memo.getChild(current)
}, this.root);
parent.addChild(path[path.length-1],newModule);
// 根据当前注册的key ,将他注册到对应的模块的儿子处
}
// 注册完毕当前模块,在进行注册子模块
if(rawModule.modules){
forEach(rawModule.modules,(module,key)=>{
this.register(path.concat(key),module);
})
}
}
}
export default ModuleCollection
new ModuleCollection(options) 执行 this.register([],options);
然后 let newModule = new Module(rawModule) ,
展开Module构造函数看看:
vuex/module/module.js
import { forEach } from '../util'
class Module {
constructor(rawModule) {
this._raw = rawModule;
this._children = {};
this.state = rawModule.state
}
getChild(childName) {
return this._children[childName]
}
addChild(childName, module) {
this._children[childName] = module
}
forEachGetter(cb) {
this._raw.getters && forEach(this._raw.getters, cb)
}
forEachMutation(cb) {
this._raw.mutations && forEach(this._raw.mutations, cb)
}
forEachAction(cb) {
this._raw.actions && forEach(this._raw.actions, cb)
}
forEachChildren(cb) {
this._children && forEach(this._children, cb)
}
// 用于标识他自己是否写了namesapced
get namespaced(){ // module.namespaced
return !!this._raw.namespaced
}
}
export default Module
一个module就是包含state 、_raw(整个module对象属性)、_children对象, 同时加上一些操作children、actions、getters的方法。把对象级别的rawModule格式化成一个可以操作函数的newModule。
这里就是new了一个module而已,方便后续函数操作。
接着看回来module-collection.js 用 rawModule.newModule = newModule; 标记这个新的newModule
if (path.length == 0){
this.root = newModule
}else{
// [a] // [a,c] => [a]
// 找父亲
let parent = path.slice(0,-1).reduce((memo,current)=>{
return memo.getChild(current)
}, this.root);
parent.addChild(path[path.length-1],newModule);
// 根据当前注册的key ,将他注册到对应的模块的儿子处
}
如果path = [] 即 path.length === 0 就是根module, 用 this.root = newModule即可。
如果不是根module, 即path = [a] 或者 [a,c]
那么就会按照路径往上一级查找父module
比如[a] 就会找到根module [a,c]则会找到a的module
找到这个父module后 就执行addChild,传入模块名字和模块即可
addChild(childName, module) {
this._children[childName] = module
}
此时父module的_children对象有个a属性的module
接着往下走
// 注册完毕当前模块,在进行注册根模块
if(rawModule.modules){
forEach(rawModule.modules,(module,key)=>{
this.register(path.concat(key),module);
})
}
如果原始传入的rawModule有modules属性。 就遍历modules 然后再执行this.register() 注册子module
new ModuleCollection(options) 最终目的就是创建一个模块收集的对象,
这个对象里面包含了root对象, root对象包含父子嵌套关系的所有用户传入的格式化后的modules.
格式化后的modules. 里面有自己模块的state _raw(原始对象) _children
root对象:
3、创建store对象 — 安装模块installModule
接着看回来vuex/store.js,执行installModule, 传入的参数 store对象, options.state, 根模块名[],模块收集器的root对象
installModule(this, state, [], this._modules.root);
这个方法展开看看
function installModule(store, rootState, path, module) { // a/b/c/d
// 需要循环当前模块的
// 获取moduleCollection类的实例
let ns = store._modules.getNamespace(path);
// module.state => 放到rootState对应的儿子里
if (path.length > 0) { // 儿子模块
// 需要找到对应父模块,将状态声明上去
// {name:'zf',age:'12',a:aState}
let parent = path.slice(0, -1).reduce((memo, current) => {
return memo[current];
}, rootState);
// 对象新增属性不能导致重新更新视图
store._withCommittting(() => {
Vue.set(parent, path[path.length - 1], module.state);
})
}
module.forEachGetter((fn, key) => {
store.wrapperGetters[ns + key] = function() {
return fn.call(store, getNewState(store, path));
}
});
module.forEachMutation((fn, key) => { // {myAge:[fn,fn]}
store.mutations[ns + key] = store.mutations[ns + key] || [];
store.mutations[ns + key].push((payload) => {
store._withCommittting(() => {
fn.call(store, getNewState(store, path), payload); // 先调用mutation 在执行subscirbe
})
store._subscribes.forEach(fn => fn({ type: ns + key, payload }, store.state));
})
});
module.forEachAction((fn, key) => {
store.actions[ns + key] = store.actions[ns + key] || [];
store.actions[ns + key].push((payload) => {
return fn.call(store, store, payload)
})
});
module.forEachChildren((child, key) => {
installModule(store, rootState, path.concat(key), child);
});
}
一开始执行
store._modules就是模块收集器对象
let ns = store._modules.getNamespace(path);
getNamespace(path){ // [a,b,c]
// 返回一个字符串 a/b/c ''
let root = this.root
let ns = path.reduce((ns,key)=>{ // this.root.c.namespace
let module = root.getChild(key);
root = module;
return module.namespaced ? ns + key + '/' :ns
},'');
return ns;
}
根modules情况下 传入的path = [] 所以这里返回ns = ‘’
如果传入的path = [a,b],且namespaced = true 那么返回 a/b/
接着执行module.forEachGetter
module.forEachGetter((fn, key) => {
store.wrapperGetters[ns + key] = function() {
return fn.call(store, getNewState(store, path));
}
});
fn就是各个定义的getter函数, key就是getter函数名字, 大致就是把各个getter函数转为下面格式
store.wrapperGetters.myAge = function() {
return myAge(getNewState(store, path));
}
getNewState(store, path) 就是返回对应path下的state, 比如[a,b] 就是返回b下面的state
function getNewState(store, path) {
return path.reduce((memo, current) => {
return memo[current];
}, store.state)
}
接着执行 module.forEachMutation
module.forEachMutation((fn, key) => { // {myAge:[fn,fn]}
store.mutations[ns + key] = store.mutations[ns + key] || [];
store.mutations[ns + key].push((payload) => {
store._withCommittting(() => {
fn.call(store, getNewState(store, path), payload); // 先调用mutation 在执行subscirbe
})
store._subscribes.forEach(fn => fn({ type: ns + key, payload }, store.state));
})
});
上面 mutations为什么是一个数组? 可能会有多个模块使用相同mutations的名字而且没有添加namespaced: true,那么就会有是一个数组包含多个mutations函数。
比如commit(‘changeAge’,10)就会触发多个不同模块的mutation的changeAge(假如没有命名空间)
这里以数组形式添加了各个mutations, 然后用store._withCommittting函数包裹。
_withCommittting函数如下 , 为了_committing = true状态后再去执行函数,严格模式下, 更好的防止用户其他方式直接修改了state, 必须要commit方式修改state
_withCommittting(fn) {
this._committing = true; // 如果true
fn(); // 函数是同步的 获取_commiting 就是true,如果是异步的那么就会变成false 就会打印日志
this._committing = false;
}
同时执行store._subscribes队列里面的函数,他们是订阅时添加进去的。
接着执行 module.forEachAction
module.forEachAction((fn, key) => {
store.actions[ns + key] = store.actions[ns + key] || [];
store.actions[ns + key].push(payload => {
return fn.call(store, store, payload);
});
});
同理 mutations, action也是一个数组维护,
通过上面可以看出getter mutation 和 action都是放在顶层的一个store对象下面的, 如果是子modules, 并且有命名空间就会用命名空间再做一层隔离存放而已。
接着执行子模块的安装
module.forEachChildren((child, key) => {
installModule(store, rootState, path.concat(key), child);
});
子模块遍历时
执行installModule 子模块安装, 传入的path 是父级的path加上模块名字key 比如 一开始顶层path[] 然后子模块是a 那就是[a],
child就是该子模块
重新进入installModule
if (path.length > 0) {
// 儿子模块
// 需要找到对应父模块,将状态声明上去
// {name:'zf',age:'12',a:aState}
let parent = path.slice(0, -1).reduce((memo, current) => {
return memo[current];
}, rootState);
// 对象新增属性不能导致重新更新视图
store._withCommittting(() => {
Vue.set(parent, path[path.length - 1], module.state);
});
}
此时 path.length > 0
根据路径找到父级state -> parent
然后执行Vue.set(parent, path[path.length - 1], module.state);
比如 父级state.a = 子模块的state
也就是说此时父级的module就有个a 记录该a模块的state
比如 rootState = {
name : 1,
a: {
age: 2,
b: {
sex: 'man'
}
}
}
父子级别state就直接维护在父子对应的对象里面。
紧接着如果再有子模块继续遍历安装子模块,直到无子模块需要安装了。
这一大步骤 最终整合了所有模块的mutations actions wrapperGetters到 store对象下面,会根据命名空间需要,组合key再命名。可以说全部子模块的方法都挂在store这一大对象里面。
同时根据各个模块的state , 整合成一个大的state, 通过模块名字连接各个子模块的state.
整个state整合了如下
store对象整合了所有子模块的函数了:
4、installModule完毕后,就执行resetVM(this, state);
function resetVM(store, state) {
let oldVm = store._vm;
store.getters = {};
const computed = {};
forEach(store.wrapperGetters, (getter, key) => {
computed[key] = getter;
Object.defineProperty(store.getters, key, {
get: () => store._vm[key]
});
});
store._vm = new Vue({
data: {
$$state: state
},
computed
});
if (store.strict) {
// 说明是严格模式我要监控状态
store._vm.$watch(
() => store._vm._data.$$state,
() => {
// 我希望状态变化后 直接就能监控到,watcher都是异步的? 状态变化会立即执行,不是异步watcher
console.assert(
store._committing,
'no mutate in mutation handler outside'
);
},
{ deep: true, sync: true }
); // 内部会遍历所有的属性
}
if (oldVm) {
// 重新创建实例后,需要将老的实例卸载掉
Vue.nextTick(() => oldVm.$destroy());
}
}
4.1 先记录oldVm 后面用到
4.2 store对象里面加一个getters属性
4.3 然后遍历store.wrapperGetters , 把里面的函数放到computed对象, 然后store.getters.key 指向 store._vm.key
也就是说把getter的属性取值,映射到computed取值
4.4 store._vm就是一个vue实例, computed指向getter函数。
store._vm = new Vue({
data: {
$$state: state
},
computed
});
4.5 如果是严格模式 store.strict = true;
监听$$state的属性, state属性变化, 就会触发console.assert函数, 如果store._committing = false, 就打印报错信息, 如果是true, 就不打印。
if (store.strict) {
// 说明是严格模式我要监控状态
store._vm.$watch(
() => store._vm._data.$$state,
() => {
// 我希望状态变化后 直接就能监控到,watcher都是异步的? 状态变化会立即执行,不是异步watcher
console.assert(
store._committing,
'no mutate in mutation handler outside'
);
},
{ deep: true, sync: true }
); // 内部会遍历所有的属性
}
4.6 如果有oldVm, 说明是至少第二次进入了这个函数,而且是有旧的vue实例, 故卸载掉旧的oldVm.$destroy()。
if (oldVm) {
// 重新创建实例后,需要将老的实例卸载掉
Vue.nextTick(() => oldVm.$destroy());
}
5、调用插件
如果有插件这个参数 就遍历插件 执行插件函数,然后传入store对象
if (options.plugins) {
// 说明用户使用了插件
options.plugins.forEach(plugin => plugin(this));
}
6、页面使用vuex的state
<template>
<div id="app">
学校的年龄{{this.$store.state.name}} {{this.$store.state.age}} <br>
我的年龄是 {{this.$store.getters.myAge}}
我的年龄是 {{this.$store.getters.myAge}}
<button @click="$store.commit('changeAge',10)">更改年龄</button>
<button @click="$store.dispatch('changeAge',10)">异步年龄</button>
<hr>
t1的年龄 {{this.$store.state.a.name}} {{this.$store.state.a.age}} <br>
t1的计算年龄 {{this.$store.getters['a/myAge']}}
<button @click="$store.commit('a/changeAge',10)">t1更改年龄</button>
<hr>
c的年龄 {{this.$store.state.a.c.age}}
<button @click="$store.commit('a/c/changeAge',10)">t1更改年龄</button>
</div>
</template>
<script>
export default {
name:'app',
}
</script>
<style>
</style>
this.$store.state.name
this.$store.state.age
因为每个实例都有了$store 故每个组件可以直接this.$store取值
store对象 有个state取值函数, 实际就是取_vm的_data.$$state
get state() {
return this._vm._data.$$state;
}
这些变量就是直接取$store.state对象里面的就可以了, 不管有无namespaces=true或者false的命名空间, state都是通过模块名去区分
取a模块的name 就直接取 this.$store.state.a.name
取a模块的c模块 则取this.$store.state.a.c.name
而修改state 我们通过$store.commit函数触发
$store.commit('changeAge', 10)
commit函数实现
vuux/store.js 里面查看
commit = (mutationName, payload) => {
// 发布
this.mutations[mutationName] &&
this.mutations[mutationName].forEach(fn => fn(payload));
};
实际就是去store.mutations.changeAge这个数组属性里面看看有无函数,有函数则遍历执行,传入payload就是了, 就会去找用户定义的这个mutation函数执行。
如果需要按照修改模块里面的state,且有命名空间,则需要根据commit( ‘模块名字’ + ‘/’ + mutationName)触发修改
<button @click="$store.commit('a/changeAge', 10)">t1更改年龄</button>
比如 上面就是触发a模块的’changAge’ mutation
同理getters属性也是依据是否有命名空间, 加上比如 ‘a/’ + 取值state
如
this.$store.getters["a/myAge"]
而dispatch 可以实现异步commit state
$store.dispatch('changeAge', 10)
actions: { // 异步操作 调用api接口 dispatch, 多次commit mutation
changeAge({ commit }, payload) {
setTimeout(() => {
commit('changeAge', payload);
}, 1000);
}
},
实现原理 触发dispatch 就是去actions队列里面找到对应的actionName的函数 触发传入payload,可以异步操作,操作完毕后就commit数据
dispatch = (actionName, payload) => {
this.actions[actionName] && this.actions[actionName].forEach(fn => fn(payload))
}
整体来说 dispatch commit 都是执行用户定义的函数。
如果有命名空间的模块, commit dispatch 函数名都加上模块名,getters对应属性的取值加上模块名再取 否则不需要加,会触发所以模块的相关名称函数。
7、动态注册模块
<button @click="registerModule">动态注册模块</button>
export default {
name: "app",
methods: {
registerModule() {
store.registerModule("b", {
state: {
name: "jiangwen",
age: 30,
},
getters:{
bAge(state){
return state.age+1;
}
}
});
},
},
};
store.registerModule实现动态注册模块
源码实现 vuex/store.js
registerModule(path, module) { // 最终都转换成数组 register(['a','c'])
if (typeof path == 'string') path = [path];
// module 是用户直接写的
this._modules.register(path, module); // 模块的注册, 将用户给的数据放到树中
// 注册完毕后 ,在进行安装
// 将用户的module 重新安装
installModule(this, this.state, path, module.newModule);
// vuex内部重新注册的话 会重新生成实例, 虽然重新安装了 ,只解决了状态的问题,但是computed就丢失了
resetVM(this, this.state); // 销毁重来
}
7.1、根据传参 path类型 如 ‘b’, 转换为path = [‘b’]
7.2、然后调用模块收集器this._modules的register方法, 注册一个子模块b, 这个b模块此时会生成一个格式化后的模块叫module.newModule,
同时这个模块会挂载放在这个根模块下面,为了后面的命名空间取值一致性问题
7.3、安装这个新模块 传入path 和 格式化后的模块叫module.newModule,, 执行installModule,实际就是把wrapperGetters mutations actions 挂载在store上面,用命名空间做区分。
同时也改变了store.state的值
7.4、然后执行 resetVM(this, this.state); 从新传入新的state,创建一个新的vue实例,就能get新的state了,销毁旧的实例。
总的来说,注册模块就是把注册的模块生成格式化后的newModule 然后找到父级module,放在父级module里面,然后去安装,挂载所有 mutations actions等函数,和在父级放改模块state,然后store重新生成一个新的_vm.
8、vuex插件定义和使用
let store = new Vuex.Store({
// vuex持久化插件?
plugins: [
logger(),
persists() // 每次状态变化都可以存入到localStorage中
],
...
});
插件写法 要返回一个函数 方便传参store
function logger() {
return function(store) {
let prevState = JSON.stringify(store.state);
store.subscribe((mutation, state) => {
// 所有的更新操作都基于mutation (状态变化都是通过mutation的)
// 如果直接手动的更改状态 此scbscribe是不会执行 commit()
console.log('prevState:' + prevState);
console.log('mutation:' + JSON.stringify(mutation));
console.log('currentState:' + JSON.stringify(state));
prevState = JSON.stringify(state);
});
};
}
new Store时就会触发所有插件函数 传入store
if (options.plugins) { // 说明用户使用了插件
options.plugins.forEach(plugin => plugin(this))
}
主要做的就是订阅, store状态改变就会触发订阅函数
store.subscribe订阅处理 传入订阅的函数,里面执行逻辑就行 比如console 新旧的state
订阅函数触发的时机:commit数据后 就会主动触发订阅函数, 传入 { type: ns + key, payload }, store.state
store.mutations[ns + key].push((payload) => {
store._withCommittting(() => {
fn.call(store, getNewState(store, path), payload); // 先调用mutation 在执行subscirbe
})
store._subscribes.forEach(fn => fn({ type: ns + key, payload }, store.state));
})
持久化插件 就是缓存state 然后重新进入页面就replaceState
function persists() {
return function(store) {
// vuex-persists
let localState = JSON.parse(localStorage.getItem('VUEX:STATE'));
if (localState) {
store.replaceState(localState);
}
// 和 mutation挂钩的
store.subscribe((mutation, rootState) => {
// 状态变化了 想做一些其他事
// 状态发生变化就存localStorage中
// 防抖
localStorage.setItem('VUEX:STATE', JSON.stringify(rootState));
});
};
}
store.js里面定义了replaceState
replaceState(newState) { // 需要替换的状态
this._withCommittting(() => {
this._vm._data.$$state = newState; // 替换最新的状态, 赋予对象类型会被重新劫持
})
}
插件使用就是订阅commit事件,触发订阅函数,没别的了。
9、mapState、mapGetters、mapMutations、mapActions函数原理
使用案例
<!--
* @description:
* @author: steve.deng
* @Date: 2021-03-29 16:20:29
* @LastEditors: steve.deng
* @LastEditTime: 2021-05-06 15:54:37
-->
<template>
<div id="app">
学校的年龄{{ name }} 我的年龄是 {{ myAge }}
<button @click="changeAge(10)">更改年龄</button>
<button @click="changeAgeAsync(10)">异步年龄</button>
</div>
</template>
<script>
import store from './store';
// import { mapState, mapGetters, mapMutation, mapActions } from "vuex";
import { mapState, mapGetters, mapMutation, mapActions } from '@/vuex';
export default {
name: 'app',
methods: {
registerModule() {
store.registerModule('b', {
state: {
name: 'jiangwen',
age: 30
},
getters: {
bAge(state) {
return state.age + 1;
}
}
});
},
...mapMutation(['changeAge']),
// changAge(payload){
// this.$store.commit('changeAge', payload)
// },
changeAgeAsync(payload) {
this.$store.dispatch('changeAge', payload);
}
},
computed: {
...mapState(['name']), // mapXxxx返回的是对象
...mapGetters(['myAge'])
// name(){
// return this.$store.state.name
// },
// myAge(){
// return this.$store.getters.myAge
// }
}
};
</script>
<style></style>
源码逻辑
import install from './install';
import Store from './store'
function mapState(stateList) {
let obj = {};
for (let i = 0; i < stateList.length; i++) {
let stateName = stateList[i];
obj[stateName] = function () {
return this.$store.state[stateName];
};
}
return obj;
}
function mapGetters(gettersList) {
let obj = {};
for (let i = 0; i < gettersList.length; i++) {
let getterName = gettersList[i];
obj[getterName] = function () {
return this.$store.getters[getterName];
};
}
return obj;
}
function mapMutations(mutationList) {
let obj = {};
for (let i = 0; i < mutationList.length; i++) {
obj[mutationList[i]] = function (payload) {
this.$store.commit(mutationList[i], payload);
};
}
return obj;
}
function mapActions(actionList) {
let obj = {};
for (let i = 0; i < actionList.length; i++) {
obj[actionList[i]] = function (payload) {
this.$store.dispatch(actionList[i], payload);
};
}
return obj;
}
export default {
install,
Store,
mapState,
mapGetters,
mapMutations,
mapActions
}
mapState: 实际就是把传入参数[‘name’],组装一个对象 遍历这个数组参数,对象里面有个name函数,这个函数返回的是this.store.state.name;本质就是this.store.state.name; 本质就是this.store.state.name;本质就是this.store.state.name的简写
mapGetters: 同理 返回一个个函数 里面取值逻辑 this.$store.getters.myAge
mapMutations: 同理 ,也是返回一个个函数 里面是commit逻辑 this.$store.commit(mutationList[i], payload);
mapActions: 同理,也是返回一个个函数 里面是dispatch逻辑 this.$store.dispatch(actionList[i], payload);
10、总结
vuex本质就是维护一个store对象,store对象的state其实又是一个vue实例的state,故他们会有双向绑定的特点,把getters的取值放在了vue的computed里面取值,然后用户的commit, dispatch方法,直接放在store对象下面,只不过是按照命名空间,在key上做下文章区分而已,本质都是一个大store包含全部模块的方法。最终要修改state就依靠commit函数去触发用户定义的mutation函数,然后修改state, 集中式修改state,保证安全,插件的话只是用了订阅发布模式,mapMutations等方法就是mutations函数抽象后的处理。