简单来说:Vuex是个状态仓库,存放着共用的状态信息(例如:用户信息)
与全局对象数据不同:
- Vuex的状态是响应式的,一处更改了,使用状态的组件也会更新
- 不能直接更改state中的值,需要通过mutations中的函数(commit)
着手实现
创建Vue项目,安装Vuex;在是src文件下的store文件夹中index.js文件能看到
Vue.use(Vuex);
export default new Vuex.Store({
...
});
- Vuex导出了一个Store类
- Vuex中存在install函数
新建一个XlVuex.js文件: 结构如下
class Store {
// options 是new Vuex.Store中的参数对象
constructor(options) {}
}
// Vue使用install,第一个参数为Vue对象
const install = (Vue) => {};
export default {
install,
Store,
};
Vuex是一个状态管理树,modules模板也会统一整理到主状态中;因此先把modules中的属性统一到主状态中。
完整代码
modules
模板之间也存在着嵌套,所以需要递归将所有被应用的模板添加进来
class Store {
constructor(options) {
this.getModules(options);
}
getModules(options) {
let modules = options.modules || {};
// 把模板中对象的状态合并到主支上
Object.keys(modules).forEach((module) => {
options.state[module] = modules[module].state || {};
options.getters[module] = modules[module].getters || {};
options.mutations[module] = modules[module].mutations || {};
options.actions[module] = modules[module].actions || {};
// 存在引用就开始递归
if(modules[module].modules) {
this.getModules(modules[module])
}
});
}
}
state
Vuex中只有state里面是数据,绑定到Vue的data里面才能达到双向绑定的效果 双向绑定需要将state状态树放入到Vue的data属性中
let _Vue; // 全局变量,获取到install传来的Vue对象
const install = (Vue) => {
_Vue = Vue;
_Vue.mixin({
// 组件创建前给大家挂载上$store仓库
beforeCreate() {
if (this.$options && this.$options.store) {
this.$store = this.$options.store;
} else {
// 子组件同样挂载
this.$store = this.$parent && this.$parent.$store;
}
},
});
};
class Store {
constructor(options) {
...
this._s = new _Vue({
data: {
state: options.state,
},
});
}
// 访问state的时候返回双向绑定中的state
get state() {
return this._s.state;
}
...
}
getters
getters对象中存放着映射函数,使用到对应属性执行对应函数即可
class Store {
constructor(options) {
...
this.getters = {}; //在vuex中挂载getters对象
// 没有定义getters就给个空,获取到实例传进来的getters
let getters = options.getters || {};
// 遍历获取的getters对象,并将对应方法绑定到挂载的 this.getters上
Object.keys(getters).forEach((getter) => {
Object.defineProperty(this.getters, getter, {
// 调用挂载的getters自动调用
get: () => {
// 箭头函数保证this指向当前对象
return getters[getter](this.state);
},
});
});
}
...
}
mutations
mutations同样存放着映射函数,不过与getters不同的是,需要使用commit指令进行操作
class Store {
constructor(options) {
...
this.mutations = {}; // 挂载
let mutations = options.mutations || {};
Object.keys(mutations).forEach((mutation) => {
// 给挂载的mutations添加属性对应的函数
this.mutations[mutation] = (payload) => {
mutations[mutation](this.state, payload);
};
});
...
}
...
// mutations 函数指令
commit(fnKey, payload) {
this.mutations[fnKey](payload);
}
}
actions
actions和mutations是类似的,不过actions是通过dispatch指令执行的
class Store {
constructor(options) {
...
this.actions = {};
let actions = options.actions || {};
for (let action in actions) {
this.actions[action] = (payload) => {
actions[action](this, payload);
};
}
...
}
...
// actions 函数指令
dispatch(fnKey, payload) {
this.actions[fnKey](payload);
}
}
实现效果
将每个模块整合在一起,一个假的vuex就诞生了;是不是很简单呢!
下面展示一下个人效果: 在views文件夹下的Home.vue中
<template>
<div class="home">
{{$store.state.BuyCart.user.name}}
<h1>{{$store.getters.ballCount}}</h1>
<button @click="add">加一个</button>
<button @click="asyncAdd">延迟加五个</button>
<ul>
<li v-for="item, i in $store.state.BuyCart.prodList" :key="i">{{item.name}}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'Home',
methods: {
add() {
this.$store.commit("increment")
},
asyncAdd() {
this.$store.dispatch('asyncIncrement', 5)
}
},
}
</script>
store文件夹下index.js
import Vue from "vue";
// import Vuex from "vuex";
import Vuex from "./XlVuex"
import BuyCart from "./buyCart"
Vue.use(Vuex);
export default new Vuex.Store({
state: {
count: 0,
},
getters: {
ballCount: state => state.count + "个球"
},
mutations: {
increment(state, payload=1) {
state.count += payload;
},
},
actions: {
asyncIncrement(state, payload) {
setTimeout(() => {
state.commit("increment", payload);
}, 1000);
},
},
modules: {
BuyCart
},
});
其他可以自行添加
效果图