手写Vuex源码

vuex基本使用方式

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    //统一状态管理
    state: {
        name: '李明'
    },
    //状态更改
    mutations: {},
    //异步提交更改
    actions: {},
    modules: {}
})

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')

App.vue

<template>
  <div id="app">
    {{$store.state}}
  </div>
</template>

<script>

export default {
  name: 'App',
  components: {
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

输出:
在这里插入图片描述

分析vuex结构

打印Vuex:
在这里插入图片描述
打印Vue实例:

import Vue from 'vue'
import App from './App.vue'
import store from './store'

Vue.config.productionTip = false

let vm = new Vue({
    store,
    render: h => h(App),
}).$mount('#app')

console.dir(vm)

在这里插入图片描述
可见,store绑定在$options中。

在这里插入图片描述
可见,$store绑定在Vue实例中,因此可以通过获取 $options 的store,绑定给$store

vuex源码实现

1、创建基本结构

vuex/index.js

const install = (_Vue) => {
    console.log(_Vue)
}

class Store {

}

export default {
    install,
    Store,
}

Vue.use(Vuex)会调用install,因此需要创建install方法。
store.js使用了new Vuex.Store,因此需要创建Store类。

输出_Vue(Vue.use在调用install方法时传入参数_Vue):
在这里插入图片描述
注意:
这里使用_Vue获取Vue实例而不是使用import,因为main.js也导入了Vue,如果使用import,会打包多次。

2、实现数据传递:state

const install = (_Vue) => {
    let Vue = _Vue;
    // console.dir(Vue)

    //给所有组件添加共同的功能
    Vue.mixin({
        //这里是为了在组件创建之前,给其传入数据
        beforeCreate() {
            //判断组件自身是否存在store
            if (this.$options && this.$options.store) this.$store = this.$options.store;
            //不存在则寻找父组件的$store(需要加$)
            else this.$store = this.$parent && this.$parent.$store;
        },
    })
}

class Store {
    constructor(options = {}) {
        //参数赋值
        this.state = options.state;
    }
}

export default {
    install,
    Store,
}

3、实现状态修改:mutations

export default new Vuex.Store({
    //统一状态管理
    state: {
        name: '李明',
        age: 18,
    },
    //状态更改
    mutations: {
        //年龄增加
        addAge(state, payload) {
            state.age += payload;
        }
    },
    //异步提交更改
    actions: {},
    modules: {}
})
<template>
  <div id="app">
    姓名:{{$store.state.name}}<br>
    年龄:{{$store.state.age}}<br>
    <button @click="add">长一岁</button>
  </div>
</template>

<script>

export default {
  name: 'App',
  components: {
  },
  methods:{
    add() {
      this.$store.commit('addAge',1);
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

class Store {
    constructor(options = {}) {
        //参数赋值
        this.state = options.state;
        this.mutations = options.mutations;
    }

    //提交修改
    commit(mutationName, payload) {

        //遍历mutations绑定的所有方法名称
        Object.keys(this.mutations).forEach(name => {
            //如果和传入的方法名称相同,则调用
            if (mutationName === name) this.mutations[mutationName](this.state, payload);
        });
    }
}

在这里插入图片描述
这里可以看到,虽然$store.state.age已经被修改,但是视图依然没有更新,因此可以将其添加到Vue的data中,实现双向绑定。

let Vue;

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

    //给所有组件添加共同的功能
    Vue.mixin({
        //这里是为了在组件创建之前,给其传入数据
        beforeCreate() {
            //判断组件自身是否存在store
            if (this.$options && this.$options.store) this.$store = this.$options.store;
            //不存在则寻找父组件的$store(需要加$)
            else this.$store = this.$parent && this.$parent.$store;
        },
    })
}

class Store {
    constructor(options = {}) {
        //实现双向绑定
        let v = new Vue({
            data() {
                return {
                    state: options.state,
                    mutations: options.mutations,
                }
            },
        });

        //参数赋值
        this.state = v.state;
        this.mutations = v.mutations;
    }

    //提交修改
    commit(mutationName, payload) {

        //遍历mutations绑定的所有方法名称
        Object.keys(this.mutations).forEach(name => {
            //如果和传入的方法名称相同,则调用
            if (mutationName === name) this.mutations[mutationName](this.state, payload);
        });
    }
}

export default {
    install,
    Store,
}

4、实现异步状态修改:action

import Vue from 'vue'
import Vuex from 'vuex'

//调用install
Vue.use(Vuex)

export default new Vuex.Store({
    //统一状态管理
    state: {
        name: '李明',
        age: 18,
    },
    //状态更改
    mutations: {
        //年龄增加
        addAge(state, payload) {
            state.age += payload;
        }
    },
    //异步提交更改
    actions: {
        //异步年龄增加
        asyncAddAge({ commit }, payload) {
            setTimeout(() => {
                commit('addAge', payload);
            }, 1000);
        }
    },
    modules: {}
})
<template>
  <div id="app">
    姓名:{{$store.state.name}}<br>
    年龄:{{$store.state.age}}<br><br>
    <button @click="add">长一岁</button><br><br>
    <button @click="asyncAdd">1秒后长一岁</button>
  </div>
</template>

<script>

export default {
  name: 'App',
  components: {
  },
  methods:{
    add() {
      this.$store.commit('addAge',1);
    },
    asyncAdd() {
      this.$store.dispatch('asyncAddAge',1);
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

let Vue;

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

    //给所有组件添加共同的功能
    Vue.mixin({
        //这里是为了在组件创建之前,给其传入数据
        beforeCreate() {
            //判断组件自身是否存在store
            if (this.$options && this.$options.store) this.$store = this.$options.store;
            //不存在则寻找父组件的$store(需要加$)
            else this.$store = this.$parent && this.$parent.$store;
        },
    })
}

class Store {
    constructor(options = {}) {
        //实现双向绑定
        let v = new Vue({
            data() {
                return {
                    state: options.state,
                    mutations: options.mutations,
                    actions: options.actions,
                }
            },
        });

        //参数赋值
        this.state = v.state;
        this.mutations = v.mutations;
        this.actions = v.actions;

    }

    //提交修改
    commit = (mutationName, payload) => {
        //遍历mutations绑定的所有方法名称
        Object.keys(this.mutations).forEach(name => {
            //如果和传入的方法名称相同,则调用
            if (mutationName === name) this.mutations[mutationName](this.state, payload);
        });
    }

    //异步提交修改
    dispatch = (actionName, payload) => {
         //遍历actions绑定的所有方法名称
        Object.keys(this.actions).forEach(name => {
            if (actionName === name) this.actions[actionName]({ commit: this.commit }, payload);
        })
    }
}

export default {
    install,
    Store,
}

注意:
这里方法格式需要写成xxx = () => {},否则会出现this指向underfined的问题。

5、实现数据获取:getters

store/index.js

getters: {
        getName(state) {
            return state.name;
        }
    }

App.vue

姓名:{{$store.getters.getName}}
let Vue;

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

    //给所有组件添加共同的功能
    Vue.mixin({
        //这里是为了在组件创建之前,给其传入数据
        beforeCreate() {
            //判断组件自身是否存在store
            if (this.$options && this.$options.store) this.$store = this.$options.store;
            //不存在则寻找父组件的$store(需要加$)
            else this.$store = this.$parent && this.$parent.$store;
        },
    })
}

class Store {
    constructor(options = {}) {
        //实现双向绑定
        let v = new Vue({
            data() {
                return {
                    state: options.state,
                    mutations: options.mutations,
                    actions: options.actions,
                    getters: options.getters,
                }
            },
        });

        //参数赋值
        this.state = v.state;
        this.mutations = v.mutations;
        this.actions = v.actions;
        this.getters = {};

		//定义getters
        Object.keys(v.getters).forEach(name => {
            Object.defineProperty(this.getters, name, {
                get: () => {
                    return v.getters[name](this.state);
                }
            })
        })
    }

    //提交修改
    commit = (mutationName, payload) => {
        //遍历mutations绑定的所有方法名称
        Object.keys(this.mutations).forEach(name => {
            //如果和传入的方法名称相同,则调用
            if (mutationName === name) this.mutations[mutationName](this.state, payload);
        });
    }

    //异步提交修改
    dispatch = (actionName, payload) => {
        //遍历actions绑定的所有方法名称
        Object.keys(this.actions).forEach(name => {
            if (actionName === name) this.actions[actionName]({ commit: this.commit }, payload);
        })
    }
}

export default {
    install,
    Store,
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值