丢到浏览器直接运行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>简易版Vuex</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.js"></script>
</head>
<body>
<div id="app">{{$store.state.count}} {{$store.getters.newCount}}</div>
<script>
class Store {
constructor(options) {
let state = options.state;
this.getters = {};
this.mutations = {};
this.actions = {}
this.state = Vue.observable(options.state) //变成响应试数据
if (options.getters) {
forEach(options.getters, (getterName, getterFn) => {
// 循环getters里面的每一项数据 给getters中的每一项添加响应式 并执行里面的函数, 只要在模板中取值时 以来的state改变 那么getter自然改变
Object.defineProperty(this.getters, getterName, {
get: () => {
// 和computed实现原理一样
return getterFn(state);
}
})
});
}
forEach(options.mutations, (mutationName, mutationFn) => {
// 包裹函数 等待commit()函数调用
this.mutations[mutationName] = () => {
// 改变this指向,指向store实例
mutationFn.call(this, state);
}
});
// 包裹函数 等待dispatch()函数调用函数调用
forEach(options.actions, (actionName, actionFn) => {
this.actions[actionName] = () => {
// 改变this指向 使其store实例
actionFn.call(this, this);
}
});
//切片编程 把原型上的方法存起来,内部调用 防止action时this丢失
let { commit, dispatch } = this;
this.commit = (type) => {
commit.call(this, type);
}
this.dispatch = (type) => {
dispatch.call(this, type);
}
}
commit(type) {
this.mutations[type]()
}
dispatch(type) {
this.actions[type]()
}
}
const forEach = (obj, callback) => {
Object.keys(obj).forEach(item => callback(item, obj[item]));
}
const install = (Vue) => {
Vue.mixin({
beforeCreate() {
// 根组件中增加$store属性
if (this.$options && this.$options.store) { //是否是根组件
this.$store = this.$options.store;
} else { // 子组件增加$store属性
this.$store = this.$parent && this.$parent.$store
}
}
})
}
const Vuex = {
install: install,
Store: Store
}
Vue.use(Vuex); // install 方法
const store = new Vuex.Store({
state: {
count: 100
},
getters: {
newCount(state) { // 200
return state.count + 100;
}
},
mutations: {
change(state) {
state.count += 10
}
},
actions: {
change({ commit }) {
setTimeout(() => {
// 此时会去调用原型上的commit, 但是commit方法上会丢失this,this此时指向undefined
commit('change');
}, 1000);
}
}
})
new Vue({
el: '#app',
store,
mounted() {
this.$store.dispatch('change')
}
})
</script>
</body>
</html>