手搓 Vuex
来自 知乎 的文章 手写Vuex核心原理
这里只放最终代码,了解详细内容请访问原文章,原作者写的肥肠好,顶顶顶
// store/myVuex.js
import Vue from "vue"
class Store {
constructor(options) {
this.vm = new Vue({
data: {
state: options.state || {}
}
})
// 用户提供的 getters 函数
const userGetters = options.getters || {}
// Store 实例上的 getters 属性
this.getters = {}
// 遍历用户提供的 getter 函数,并为每个 getter 创建访问器属性
Object.keys(userGetters).forEach(getterName => {
Object.defineProperty(this.getters, getterName, {
get: () => {
return userGetters[getterName](this.state)
}
})
})
// 用户提供的 mutations 函数
const userMutations = options.mutations || {}
// Store 实例上的 mutations 属性
this.mutations = {}
// 遍历用户提供的 mutations 函数,并为每个 mutation 创建访问器属性
Object.keys(userMutations).forEach(mutationName => {
this.mutations[mutationName] = arg => {
userMutations[mutationName](this.state, arg)
}
})
const userActions = options.actions || {}
this.actions = {}
Object.keys(userActions).forEach(actionName => {
this.actions[actionName] = arg => {
userActions[actionName](this, arg)
}
})
}
dispatch(method, arg) {
this.actions[method](arg)
}
// 在执行 dispatch 调用 commit,this undefined 改为箭头函数
commit = (method, arg) => {
this.mutations[method](arg)
}
get state() {
return this.vm.state
}
}
const install = function(Vue) {
Vue.mixin({
beforeCreate() {
if(this.$options && this.$options.store) { // 如果是根组件
this.$store = this.$options.store
} else { // 如果是子组件
this.$store = this.$parent && this.$parent.$store
}
}
})
}
const Vuex = {
Store,
install
}
export default Vuex
// store/index.js
import Vue from "vue"
import Vuex from './myVuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
num: 0
},
getters: {
getNum: state => {
return state.num
}
},
mutations: {
incre(state, arg) {
state.num += arg
}
},
actions: {
asyncIncre({commit}, arg) {
setTimeout(() => {
commit('incre', arg)
}, 1000)
}
},
modules: {}
})
// App.vue
<script lang="ts">
export default {
name: "App",
computed: {},
methods: {
add() {
this.$store.commit("incre", 1);
},
asyncAdd() {
this.$store.dispatch("asyncIncre", 2);
},
},
};
</script>
<template>
<div id="app">
123
<p>state:{{ this.$store.state.num }}</p>
<p>getter:{{ this.$store.getters.getNum }}</p>
<button @click="add">+1</button>
<button @click="asyncAdd">异步+2</button>
</div>
</template>
<style lang="scss" scoped>
</style>
// main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
store,
render: h => h(App),
}).$mount('#app')