Bootstrap

实现vuex源码,手写

实现vuex源码,手写

Vuex 是专门为 Vue.js 应用程序开发的状态管理模式 + 库,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

第一步:定义初始化Store类

创建文件夹store/vuex.js

1.定义 Store 类

  • 创建一个名为 Store 的类,它接受一个 options 对象作为参数。
  • options 对象中,包含 state(应用的状态)、mutations(同步更改状态的方法)、actions(异步操作或包含任意异步操作的方法)、以及 getters(从 state 中派生出一些状态的方法)。
let Vue;
class Store{
 constructor(options) {
   
  }
}

2.初始化 Vue 实例

  • Store 类的构造函数中,使用 new Vue({ data: { $$state: options.state } }) 创建一个 Vue 实例,用于响应式地存储状态。这里使用 $$state 作为属性的名称是为了避免与 Vue 实例自身的 state 属性冲突,但这不是必须的,只是一个命名约定。
let Vue;
class Store{
 constructor(options) {
     this._vm = new Vue({
       data:{
         $$state:options.state
       }
     })
  }
}

3.存储 mutations 和 actions

  • options.mutationsoptions.actions 分别存储在 this._mutationsthis._actions 中。
let Vue;
class Store{
 constructor(options) {
     this._vm = new Vue({
       data:{
         $$state:options.state
       }
     })
     this._mutations = options.mutations
     this._actions = options.actions
  }
}

4.绑定 commit 和 dispatch 方法

  • 使用 Function.prototype.bind 方法将 commitdispatch 方法绑定到 Store 实例上,以确保在回调函数中 this 指向正确。
let Vue;
class Store{
 constructor(options) {
     this._vm = new Vue({
       data:{
         $$state:options.state
       }
     })
     this._mutations = options.mutations
     this._actions = options.actions
     this.commit = this.commit.bind(this)
     this.dispatch = this.dispatch.bind(this)
  }
}

5.初始化 getters

  • 创建一个空对象 this.getters 用于存储 getter 方法。
  • 如果 options.getters 存在,则调用 this.handleGetters(options.getters) 方法来初始化 getters。
let Vue;
class Store{
 constructor(options) {
     this._vm = new Vue({
       data:{
         $$state:options.state
       }
     })
     this._mutations = options.mutations
     this._actions = options.actions
     this.commit = this.commit.bind(this)
     this.dispatch = this.dispatch.bind(this)
     this.getters = {}
     options.getters && this.hanleGetters(options.getters)
  }
}

第二步:实现 handleGetters 方法和其他 Store 方法

1.实现 handleGetters 方法

  • handleGetters 方法中,遍历 getters 对象的键。
  • 使用 Object.definePropertythis.getters 对象上定义每个 getter 属性,其 get 方法返回 getters[key](this.state) 的结果。
let Vue;
class Store{
 constructor(options) {
     this._vm = new Vue({
       data:{
         $$state:options.state
       }
     })
     this._mutations = options.mutations
     this._actions = options.actions
     this.commit = this.commit.bind(this)
     this.dispatch = this.dispatch.bind(this)
     this.getters = {}
     options.getters && this.hanleGetters(options.getters)
  }
  handleGetters(getters){
    Object.key(getters).map((key)=>{
      Object.defineProperty(this.getters,key,
        get: () => getters[key](this.state)
      })
    })
  }
}

2.实现 state 的 getter 和 setter

  • 使用 get state() 方法来访问 Vue 实例中存储的状态。
  • 使用 set state(v) 方法来防止直接修改状态(虽然在这里,setter 只是打印了一个错误消息)。
let Vue;
class Store{
 constructor(options) {
     this._vm = new Vue({
       data:{
         $$state:options.state
       }
     })
     this._mutations = options.mutations
     this._actions = options.actions
     this.commit = this.commit.bind(this)
     this.dispatch = this.dispatch.bind(this)
     this.getters = {}
     options.getters && this.hanleGetters(options.getters)
  }
  handleGetters(getters){
    Object.key(getters).map((key)=>{
      Object.defineProperty(this.getters,key,
        get: () => getters[key](this.state)
      })
    })
  }
  //get set
  get state(){
    return this._vm.data.$$state
  }
  set state(v) {
    console.error("please provide");
  }
}

3.实现 commit 方法

  • commit 方法用于触发 mutations,它接受一个 type(mutation 的类型)和一个可选的 payload(传递给 mutation 的数据)。
  • 根据 typethis._mutations 中找到对应的 mutation 方法,并调用它,传入 this.statepayload
let Vue;
class Store{
 constructor(options) {
     this._vm = new Vue({
       data:{
         $$state:options.state
       }
     })
     this._mutations = options.mutations
     this._actions = options.actions
     this.commit = this.commit.bind(this)
     this.dispatch = this.dispatch.bind(this)
     this.getters = {}
     options.getters && this.hanleGetters(options.getters)
  }
  handleGetters(getters){
    Object.key(getters).map((key)=>{
      Object.defineProperty(this.getters,key,
        get: () => getters[key](this.state)
      })
    })
  }
  //get set
  get state(){
    return this._vm.data.$$state
  }
  set state(v) {
    console.error("please provide");
  }
  //commit
  commit(type,value){
   const entry = this._mutations[type]
   if(!entry){
   console.error("please provide");
   }
   entry(this.state,value)
  }
}

4.实现 dispatch 方法:

  • dispatch 方法用于触发 actions,它的工作原理与 commit 类似,但通常用于处理异步操作。
let Vue;
class Store{
 constructor(options) {
     this._vm = new Vue({
       data:{
         $$state:options.state
       }
     })
     this._mutations = options.mutations
     this._actions = options.actions
     this.commit = this.commit.bind(this)
     this.dispatch = this.dispatch.bind(this)
     this.getters = {}
     options.getters && this.hanleGetters(options.getters)
  }
  handleGetters(getters){
    Object.key(getters).map((key)=>{
      Object.defineProperty(this.getters,key,
        get: () => getters[key](this.state)
      })
    })
  }
  //get set
  get state(){
    return this._vm.data.$$state
  }
  set state(v) {
    console.error("unknown mutation type");
  }
  //commit
  commit(type,value){
   const entry = this._mutations[type]
   if(!entry){
   console.error("please provide");
   }
   entry(this.state,value)
  }
  //dispatch
  dispatch(type,value){
    const entry = this._actions[type]
    if(!entry){
       console.error("unknown action type")
    }
    entry(this.state,value)
  }
}

第三步:安装现在自定义vuex插件,需要一个install方法

  • 创建一个名为 install 的函数,它接受一个 Vue 构造函数作为参数。
  • install 函数中,将 Vue 构造函数存储在全局变量 Vue 中。
  • 使用 Vue.mixin 方法来全局注册一个 beforeCreate 钩子,该钩子会在每个 Vue 组件实例创建之前被调用。
  • 在 beforeCreate 钩子中,检查 this.$options.store 是否存在,如果存在,则将其赋值给 Vue.prototype.$store,这样在任何 Vue 组件中都可以通过 this.$store 访问到 store 实例。
let Vue;
class Store{
 constructor(options) {
     this._vm = new Vue({
       data:{
         $$state:options.state
       }
     })
     this._mutations = options.mutations
     this._actions = options.actions
     this.commit = this.commit.bind(this)
     this.dispatch = this.dispatch.bind(this)
     this.getters = {}
     options.getters && this.hanleGetters(options.getters)
  }
  handleGetters(getters){
    Object.key(getters).map((key)=>{
      Object.defineProperty(this.getters,key,
        get: () => getters[key](this.state)
      })
    })
  }
  //get set
  get state(){
    return this._vm.data.$$state
  }
  set state(v) {
    console.error("unknown mutation type");
  }
  //commit
  commit(type,value){
   const entry = this._mutations[type]
   if(!entry){
   console.error("please provide");
   }
   entry(this.state,value)
  }
  //dispatch
  dispatch(type,value){
    const entry = this._actions[type]
    if(!entry){
       console.error("unknown action type")
    }
    entry(this.state,value)
  }
}

Store.install = (_vue)=>{
  Vue = _vue
  Vue.mixin({
     beforeCreate(){
       if(this.$options.store){
         Vue.prototype.$store = this.$options.$store
       }
     }
  })
}

第四步:导出install,Store

let Vue;
class Store {
  constructor(options) {
    this._vm = new Vue({
      data: {
        $$state: options.state,
      },
    });
    this._mutations = options.mutations;
    this._actions = options.actions;
    this.commit = this.commit.bind(this);
    this.dispatch = this.dispatch.bind(this);
    this.getters = {};
    options.getters && this.handleGetters(options.getters);
  }
  handleGetters(getters) {
    console.log(Object.keys(getters))
    Object.keys(getters).map((key) => {
      Object.defineProperty(this.getters, key, {
        get: () => getters[key](this.state)
      });
    });
  }
  get state() {
    return this._vm._data.$$state;
  }
  set state(v) {
    console.error("please provide");
  }
  commit(type, payload) {
    console.log(type, payload)
    const entry = this._mutations[type];
    if (!entry) {
      console.error("unknown mutation type: " + type);
    }
    entry(this.state, payload);
  }

  dispatch(type, payload) {
    console.log(this._actions[type]);
    const entry = this._actions[type];
    if (!entry) {
      console.error("unknown mutation type: " + type);
    }
    entry(this.state, payload);
  }
}
const install = (_Vue) => {
  Vue = _Vue;
  Vue.mixin({
    beforeCreate() {
      if (this.$options.store) {
        Vue.prototype.$store = this.$options.store;
      }
    },
  });
};
export default {
  Store,
  install,
};

第五步:创建store/index.js

import Vue from 'vue'
// 引入自己的写的vuex,里面有一个对象{install},当你use时,会自动调用这个方法
import Vuex from './vuex.js'
Vue.use(Vuex)
//需要创建一个仓库并导出
//当new的时候,给Vuex.js中传入了一堆的东西
export default new Vuex.Store({
    state: {
        name: 1
    },
    //getters中虽然是一个方法,但是用时,可以把他当作属性
    getters: {   // 说白了,就是vue中data中的computed
        powerCount(state) {
            return state.name * 2
        },
    },
    // 改变状态:异步请求数据  事件 
    mutations: {
        add(state) {
            state.name++
        }
    },
    actions: {
        add(state) {
            setTimeout(() => {
                console.log(state)
                state.name = 30
            }, 1000);
        }
    }
})

第六步:在main中挂载store

/* eslint-disable vue/multi-word-component-names */
import Vue from 'vue'
import App from './App.vue'
import store from "./store.js"
Vue.config.productionTip = false

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

第七步:如何使用store

和vuex一样的用法,语法一样

<template>
  <div>
    {{ $store.state.name }}
    {{ $store.getters.powerCount }}
    <button @click="add">123</button>
  </div>
</template>
<script>
export default {
  name: "app",
  data() {
    return {
    }
  },
  methods:{
    add(){
      this.$store.commit('add')
      // this.$store.dispatch('add')
      console.log(this.$store.getters.powerCount)
      this.$store.handleGetters.powerCount
    }
  },
  mounted() { 
    console.log(this.$store.state.name)
   }
}
</script>
   {{ $store.state.name }}
    {{ $store.getters.powerCount }}
    <button @click="add">123</button>
  </div>
</template>
<script>
export default {
  name: "app",
  data() {
    return {
    }
  },
  methods:{
    add(){
      this.$store.commit('add')
      // this.$store.dispatch('add')
      console.log(this.$store.getters.powerCount)
      this.$store.handleGetters.powerCount
    }
  },
  mounted() { 
    console.log(this.$store.state.name)
   }
}
</script>
;