0
点赞
收藏
分享

微信扫一扫

从零开始 - 50行代码实现一个Vuex状态管理器

回顾下Vuex

先vue-cli工具直接创建一个项目,勾选Vuex,其他随意:

从零开始 - 50行代码实现一个Vuex状态管理器_自动安装

创建完毕自动安装依赖,之后启动项目,熟悉的helloworld ~ 简单写个demo运行看看,后面会逐步实现一个myVuex,来达到相同的期望运行结果:

​src/store/index.js​​ :

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
state: {
age: 7
},
getters: { // 不用多说
getAge(state) { return state.age }
},
mutations: { // vuex约定了对state的操作函数都放在这里,使用commit方法触发
changeAge(state, data) {
state.age = data ? data : ++state.age
}
},
actions: { // vuex约定了异步类函数统一放在这里,dispatch方法触发
syncChangeAge({ state, commit }, data) {
state.age = 0
setTimeout(() => {
this.commit('changeAge', data) // 这里我还没弄懂待会怎么实现{commit}的读取,在真实的Vuex中这里不加this也是可以运行的
}, 1000);
}
},
modules: { /** vuex的模块化,先不实现modules功能,就不挖坑了 */

​src/App.vue​​ :

<template>
<div id="app">
{{ showMe }}
<button @click="$store.commit("changeAge")">increase</button>
<button @click="$store.dispatch("syncChangeAge", 7)">reset</button>
</div>
</template>

<script lang="js">
import Vue from "vue";
export default Vue.extend({
name: "App",
computed: {
showMe() { return `我今年${this.$store.getters.getAge || "..."}岁了`; },
}
});
</script>

运行效果如下:

从零开始 - 50行代码实现一个Vuex状态管理器_前端_02

说明:点击增加按钮加一岁,点击重置按钮进入loading状态1秒后又设置为7岁,现在,把stroe中引入的​​import Vuex from "vuex";​​改为自己的手动实现,达到跟这个demo一致的运行效果。

Ready Perfect

开始前还是先写出代码结构,创建 ​​Vuex​​ 文件夹,写入第一个index文件。

​src/Vuex/index.js​​ :

class Store {
constructor(parameters) { // vuex的核心四件套
this.state = {}
this.getters = {}
this.mutations = {}
this.actions = {}
}
get state() {
return this.state
}
commit(fName, data) {
this.mutations[fName].forEach(mutation mutation(data));
}
dispatch(fName, data) {
this.actions[fName].forEach(action action(data));
}
}

export default { Store

这样vuex的简单结构就写完了,接下来处理对实例传入的mutation和action的收集,然后提供commit和dispatch函数来执行。

创建 install

首先 store 中先是调用了 ​​Vue.use(Vuex)​​,让状态管理器注入到vue中,此时需要用到混入。

mixin 参考:​​vue全局混入​​

根据vue文档描述,使用use必须提供一个install函数,Vue会作为参数传入,参考:​​vueUse​​

​src/Vuex/index.js​​ :

class Store {
.....
}

const install = (Vue) => {
Vue.mixin({
beforeCreate() {
const { store = null } = this.$options
if (store) {
this.$store = store
} else {
this.$store = this.$parent && this.$parent.$store
}
}
})
}

export default { Store, install }

绑定 state

在上一步创建install时引入了Vue,将其挂载到全局来创建一个实例对象,利用Vue中数据双向绑定来实现state:

​src/Vuex/index.js​​ :

let _Vue

class Store {
constructor(parameters) {
const { state = { } } = parameters
this.$vue = new _Vue({ // new一个Vue实例接收用户传进的state
data: { state }
})
......
}
get state() { // 抛出Vue实例上挂载的 state
return this.$vue.state
}
......
}

const install = (Vue) => {
_Vue = Vue

处理 getter

继续上面的代码

....
class Store {
constructor(parameters) {
.....
bindInstall(this, parameters)
}
.....
}

const install = (Vue) => { .... }

const bindInstall = (store, options) => {
// 处理getters
const { getters } = options
if (getters) {
Object.keys(getters).forEach(key {
Object.defineProperty(store.getters, key, {
get() {
return getters[key](options.state)
}
})
})
}
}

export default { Store, install }

到这里,可以将 ​​src/store/index​​ 中的引入改成我们自己的了:

// import Vuex from "vuex";
import Vuex from "../Vuex";
.....

将例子运行,将看到已经成功拿到store中的getter,继续完善

处理 mutations 与 actions

继续完善刚才的bindInstall代码:

....
class Store { ..... }

const install = (Vue) => { .... }

const bindInstall = (store, options) => { // 两边收集都比较相似
const { getters, mutations, actions } = options
if (getters) { ... }
if (mutations) {
Object.keys(mutations).forEach(mutationName {
let storeMutations = store.mutations[mutationName] || []
storeMutations.push(data {
mutations[mutationName].call(store, store.state, data) // mutations中的函数第一个参数是state,第二个是值
})
store.mutations[mutationName] = storeMutations
})
}
if (actions) {
Object.keys(actions).forEach(actionName {
let storeActions = store.actions[actionName] || []
storeActions.push(data {
actions[actionName].call(store, store, data) // 这里我第一个参数先直接返回了实例对象,还不知道如何实现vuex中的效果
})
store.actions[actionName] = storeActions
})
}
}
export default { Store, install }

保存,运行测试 - 和最初的demo结果一致,至此实现了核心的vuex状态管理器

以下是 ​​Vuex/index.js​​ 完整代码

let _Vue
class Store {
constructor(parameters) {
const { state = {} } = parameters
this.$vue = new _Vue({ data: { state } })
this.getters = {}
this.mutations = {}
this.actions = {}
bindInstall(this, parameters)
}
get state() { return this.$vue.state }
commit(fName, data) { this.mutations[fName].forEach(mutation mutation(data)) }
dispatch(fName, data) { this.actions[fName].forEach(action action(data)) }
}
const install = (Vue) => {
_Vue = Vue
Vue.mixin({
beforeCreate() {
const { store = null } = this.$options
this.$store = store ? store : this.$parent ? this.$parent.$store : null
}
})
}
const bindInstall = (store, options) => {
const { getters, mutations, actions } = options
if (getters) {
Object.keys(getters).forEach(key {
Object.defineProperty(store.getters, key, {
get() { return getters[key](options.state) }
})
})
}
if (mutations) {
Object.keys(mutations).forEach(mutationName {
let storeMutations = store.mutations[mutationName] || []
storeMutations.push(data { mutations[mutationName].call(store, store.state, data) })
store.mutations[mutationName] = storeMutations
})
}
if (actions) {
Object.keys(actions).forEach(actionName {
let storeActions = store.actions[actionName] || []
storeActions.push(data { actions[actionName].call(store, store, data) })
store.actions[actionName] = storeActions
})
}
}
export default { Store, install }

举报

相关推荐

0 条评论