效果图如下:

在这里插入图片描述

1. 准备好环境

使用 vue/cil 初始化项目配置:

npm install -g @vue/cli //全局安装@vue/cli
vue create demo-vue //创建项目

yarn add vuex安装vuex创建一个store文件夹并使用:
在这里插入图片描述

2. 实现目的

stroe/index.js内容如下:(我们的目的将引入自写的vuex实现vuex基础功能)

import Vue from 'vue'
import Vuex from 'vuex'  
// import Vuex from './myvuex'      //我们实现的 青铜版vuex
// import Vuex from './myvuexplus'  //我们实现的 白银版vuex

Vue.use(Vuex)  //执行install方法 将new Vuex.Store挂载到this.$store

export default new Vuex.Store({
  state: {
    counter: 0,
    userData: {
      name: '时间',
      age: 18
    }
  },
  getters: {
    name(state) {
      return state.userData.name
    }
  },
  mutations: {
    add(state) {
      state.counter++
    },
    updateName(state, payload) {
      state.userData.name = payload
    }
  },
  actions: {
    add({ commit }) {
      setTimeout(() => {
        commit('add')
      }, 1000);
    }
  }
})

青铜版vuexmyvuex.js代码如下:

let Vue
class Store {
  constructor(options) {
    this._vm = new Vue({
      data: {
        state: options.state
      }
    })

    let getters = options.getters
    this.getters = {}
    Object.keys(getters).forEach((val) => {
      Object.defineProperty(this.getters, val, { //getters响应式
        get: () => {
          return getters[val](this.state)
        }
      })
    })

    this._actions = Object.assign({}, options.actions)
    this._mutations = Object.assign({}, options.mutations)
  }

  // get/set state目的:防止外部直接修改state
  get state() {
    return this._vm.state
  }
  set state(value) {
    console.error('please use replaceState to reset state')
  }

  commit = (funName, params) => { //this执行问题
    // 在mutations中找到funName中对应的函数并且执行
    this._mutations[funName](this.state, params)
  }

  dispatch(funName, params) {
    this._actions[funName](this, params)
  }
}

function install(vue) {
  Vue = vue
  vue.mixin({
    beforeCreate () {
      // 将 new Store() 实例挂载到唯一的根组件 this 上
      if (this.$options?.store) {
        this.$store = this.$options.store
      } else {
        this.$store = this.$parent && this.$parent.$store
      }
    }
  })
}

export default {
  Store,
  install
}

青铜版vuex this.$stroe:
在这里插入图片描述

白银版vuexmyvuexplus.js代码如下:


let _Vue
const install = function(Vue, opts) {
  _Vue = Vue
  _Vue.mixin({ // 因为我们每个组件都有 this.$store这个东西,所以我们使用混入模式
    beforeCreate () { // 从根组件向子组件遍历赋值,这边的 this 就是每个 Vue 实例
      if (this.$options && this.$options.store) { // 这是根节点
        this.$store = this.$options.store
      } else {
        this.$store = this.$parent && this.$parent.$store
      }
    }
  })
}

class ModuleCollection {
  constructor(opts) {
    this.root = this.register(opts)
  }

  register(module) {
    let newModule = {
      _raw: module,
      _state: module.state || {},
      _children: {}
    }
    Object.keys(module.modules || {}).forEach(moduleName => {
      newModule._children[moduleName] = this.register(module.modules[moduleName])
    })
    return newModule
  }
}

class Store {
  constructor(opts) {
    this.vm = new _Vue({
      data () {
        return {
          state: opts.state // 把对象变成响应式的,这样才能更新视图
        }
      }
    })

    this.getters = {}
    this.mutations = {}
    this.actions = {}

    // 先格式化传进来的 modules 数据
    // 嵌套模块的 mutation 和 getters 都需要放到 this 中
    this.modules = new ModuleCollection(opts)
    console.log(this.modules)

    Store.installModules(this, [], this.modules.root)
  }

  commit = (mutationName, value) => { // 这个地方 this 指向会有问题,这其实是挂载在实例上
    this.mutations[mutationName].forEach(f => f(value))
  }

  dispatch(actionName, value) {
    this.actions[actionName].forEach(f => f(value))
  }

  get state() {
    return this.vm.state
  }
}
Store.installModules = function(store, path, curModule) {
  let getters = curModule._raw.getters || {}
  let mutations = curModule._raw.mutations || {}
  let actions = curModule._raw.actions || {}
  let state = curModule._state || {}

  // 把子模块的状态挂载到父模块上,其他直接挂载到根 store 上即可
  if (path.length) {
    let parent = path.slice(0, -1).reduce((pre, cur) => {
      return pre[cur]
    }, store.state)
    _Vue.set(parent, path[path.length - 1], state)
  }

  Object.keys(getters).forEach(getterName => {
    Object.defineProperty(store.getters, getterName, {
      get: () => {
        return getters[getterName](state)
      }
    })
  })

  Object.keys(mutations).forEach(mutationName => {
    if (!(store.mutations && store.mutations[mutationName])) store.mutations[mutationName] = []
    store.mutations[mutationName].push(value => {
      mutations[mutationName].call(store, state, value)
    })
  })

  Object.keys(actions).forEach(actionName => {
    if (!(store.actions && store.actions[actionName])) store.actions[actionName] = []
    store.actions[actionName].push(value => {
      actions[actionName].call(store, store, value)
    })
  })

  Object.keys(curModule._children || {}).forEach(module => {
    Store.installModules(store, path.concat(module), curModule._children[module])
  })


}

// computed: mapState(['name'])
// 相当于 name(){ return this.$store.state.name }
const mapState = list => { // 因为最后要在 computed 中调用
  let obj = {}
  list.forEach(stateName => {
    obj[stateName] = () => this.$store.state[stateName]
  })
  return obj
}

const mapGetters = list => { // 因为最后要在 computed 中调用
  let obj = {}
  list.forEach(getterName => {
    obj[getterName] = () => this.$store.getters[getterName]
  })
  return obj
}

const mapMutations = list => {
  let obj = {}
  list.forEach(mutationName => {
    obj[mutationName] = (value) => {
      this.$store.commit(mutationName, value)
    }
  })
  return obj
}

const mapActions = list => {
  let obj = {}
  list.forEach(actionName => {
    obj[actionName] = (value) => {
      this.$store.dispatch(actionName, value)
    }
  })
  return obj
}

export default {
  install,
  Store,
  mapState,
  mapGetters,
  mapMutations,
  mapActions
}

白银版vuex this.$stroe:
在这里插入图片描述

3. App.vue 内使用自写的vuex:

<template>
  <div id="app">
    <button @click="$store.commit('add')">$store.commit('add'): {{$store.state.counter}}</button>
    <br>
    <button @click="$store.commit('updateName', new Date().toLocaleString())">$store.commit('updateName', Date): {{$store.getters.name}}</button>
    <br>
    <button @click="$store.dispatch('add')">async $store.dispatch('add'): {{$store.state.counter}}</button>
  </div>
</template>
<script>
...
Logo

前往低代码交流专区

更多推荐