手写一个vuex?

前言

vuex是一种专为Vue.js应用程序开发的状态管理模式,挂载在全局中,具有响应式特性

vuex的实现原理主要包括以下几个方面:

  • 是一个对象,vuex有两个属性,一个是Store类,一个是install方法。
  • Store类:用来创建store实例的,它接收一个对象作为参数,包含state, getters, mutations, actions等属性。
  • install方法:用来将store实例注入到每个Vue组件中的,它利用了Vue的mixin机制,在beforeCreate钩子中执行vuexInit方法,将store实例挂载到每个组件的$store属性上。
  • state:利用Vue的响应式data实现的,它将用户传入的state对象作为new Vue的data选项,从而实现了state的数据响应。
  • getters:利用Vue的计算属性computed实现的,它将用户传入的getters对象作为new Vue的computed选项,从而实现了getters的缓存和依赖追踪。
  • vuex的mutations是用来同步修改state的方法,它只能接收两个参数,一个是state,一个是payload,它必须是同步函数,不能包含异步操作。
  • actions:用来异步修改state的方法,它可以接收一个context对象作为参数,包含state, getters, commit, dispatch等属性,它可以包含异步操作,但最终还是要通过commit调用mutations来修改state。
  • vuex还提供了一些辅助函数,如mapState, mapGetters, mapMutations, mapActions等,用于简化组件中的store访问和操作。

下面是一个简单的代码示例,用于说明vuex的实现原理:

// vuex.js
let Vue // 保存Vue构造函数,插件中要使用

class Store {
  constructor(options) {
    // 保存选项
    this.$options = options

    // 定义响应式的state
    this._vm = new Vue({
      data: {
        $$state: options.state // 加两个$,Vue不做代理
      },
      computed: options.getters // 将getters定义为计算属性
    })

    // 定义commit和dispatch方法
    this.commit = this.commit.bind(this)
    this.dispatch = this.dispatch.bind(this)

    // 定义wrappedGetters
    this.wrappedGetters = {}

    // 实现getters,按照getters的定义挂载到store实例
    const computed = {}
    Object.keys(this.$options.getters).forEach(key => {
      // 获取用户定义的getter
      const fn = this.$options.getters[key]
      // 转换为computed可以使用无参数形式
      computed[key] = () => {
        return fn(this.state, this.getters)
      }
      // 为wrappedGetters定义只读属性
      Object.defineProperty(this.wrappedGetters, key, {
        get: () => this._vm[key]
      })
    })

    // 实现mutations
    this.mutations = {}
    Object.keys(this.$options.mutations).forEach(key => {
      this.mutations[key] = payload => {
        this.$options.mutationskey
      }
    })

    // 实现actions
    this.actions = {}
    Object.keys(this.$options.actions).forEach(key => {
      this.actions[key] = payload => {
        this.$options.actionskey
      }
    })
  }

  // 存取器,state只读
  get state() {
    return this._vm._data.$$state
  }

  set state(v) {
    console.error('不能直接修改state,请使用replaceState')
  }

  // 存取器,getters只读
  get getters() {
    return this.wrappedGetters
  }

  // commit,执行mutation
  commit(type, payload) {
    // 获取type对应的mutation
    const fn = this.mutations[type]

    if (!fn) {
      // 未定义的mutation
      console.error(`mutation ${type} 不存在`)
      return
    }

    // 传入state和负载
    fn(payload)
  }

  // dispatch,执行action
  dispatch(type, payload) {
    // 获取type对应的action
    const fn = this.actions[type]

    if (!fn) {
      // 未定义的action
      console.error(`action ${type} 不存在`)
      return
    }

    // 传入当前Store实例和负载
    return fn(payload)
  }
}

// install方法
function install(_Vue) {
  Vue = _Vue // 保存Vue构造函数

  // 混入
  Vue.mixin({
    beforeCreate() {
      // 此时,上下文已经是组件实例了
      // 如果this是根实例,则它的$options里面会有store实例
      if (this.$options.store) {
        Vue.prototype.$store = this.$options.store
        // 以后就能在组件中拿到 $store
      }
    }
  })
}

// 导出对象
export default { Store, install }

然后在app.js文件中引入使用

// 引入Vue和Vuex
import Vue from 'vue'
import Vuex from 'vuex'

// 使用Vuex插件
Vue.use(Vuex)

// 创建一个store实例
const store = new Vuex.Store({
  // 定义state
  state: {
    count: 0
  },
  // 定义getters
  getters: {
    doubleCount: state => state.count * 2
  },
  // 定义mutations
  mutations: {
    increment(state, payload) {
      state.count += payload
    }
  },
  // 定义actions
  actions: {
    incrementAsync(context, payload) {
      setTimeout(() => {
        context.commit('increment', payload)
      }, 1000)
    }
  }
})

// 在组件中使用store
new Vue({
  el: '#app',
  store,
  ...
})

你可能感兴趣的:(JavaScript学习记录,技术分享,vue.js,前端,javascript)