乐趣区

关于vue.js:手写简易的-Vuex

前言

本文适宜应用过 Vuex 的人浏览,来理解下怎么本人实现一个 Vuex。

根本骨架

这是本我的项目的 src/store/index.js 文件,看看个别 vuex 的应用

import Vue from 'vue'
import Vuex from './myvuex' // 引入本人写的 vuex
import * as getters from './getters'
import * as actions from './actions'
import state from './state'
import mutations from './mutations'

Vue.use(Vuex) // Vue.use(plugin)办法应用 vuex 插件

// vuex 导出一个类叫 Store,并传入对象作为参数
export default new Vuex.Store({
  state,
  mutations,
  actions,
  getters,
})

Vue.use的用法:

装置 Vue.js 插件。如果插件是一个对象,必须提供 install 办法。如果插件是一个函数,它会被作为 install 办法。install 办法调用时,会将 Vue 作为参数传入。这个办法的第一个参数是 Vue 结构器,第二个参数是一个可选的选项对象。

  • 该办法须要在调用 new Vue() 之前被调用。
  • 当 install 办法被同一个插件屡次调用,插件将只会被装置一次。

即是咱们须要在 ./myvuex.js 中导出 install办法,同时导出一个类Store,于是第一步能够写出代码:

let Vue = null

class Store {constructor(options) {}}

function install(_Vue) {Vue = _Vue // 下面 Store 类须要能获取到 Vue}

export default {
  Store,
  install,
}

install 办法

当咱们应用 vuex 的时候,每一个组件下面都有一个 this.$store 属性,外面蕴含了 state,mutations,actions,getters 等,所以咱们也须要在每个组件上都挂载一个 $store 属性, 要让每一个组件都能获取到,这里咱们应用Vue.mixin(mixin),用法介绍如下:

全局注册一个混入,影响注册之后所有创立的每个 Vue 实例。能够应用混入向组件注入自定义的行为,它将影响每一个之后创立的 Vue 实例。

function install(_Vue) {
  Vue = _Vue // install 办法调用时,会将 Vue 作为参数传入(下面 Store 类须要用到 Vue)// 实现每一个组件,都能通过 this 调用 $store
  Vue.mixin({beforeCreate() {// 通过 this.$options 能够获取 new Vue({参数}) 传递的参数
      if (this.$options && this.$options.store) {
        // 证实这个 this 是根实例,也就是 new Vue 产生的那个实例
        this.$store = this.$options.store
      } else if (this.$parent && this.$parent.$store) {
        // 子组件获取父组件的 $store 属性
        this.$store = this.$parent.$store
      }
    },
  })
}

state

因为 Vuex 是基于 Vue 的响应式原理根底,所以咱们要让数据扭转可刷新视图,则须要创立一个 vue 实例
因为

class Store {// options 即是 Vuex.Store({})传入的参数
  constructor(options) {
    // vuex 的外围就是借用了 vue 的实例,因为 vue 的实例数据变动,会刷新视图
    let vm = new Vue({
      data: {state: options.state,},
    })
    // state
    this.state = vm.state
  }
}

commit

咱们应用 vuex 扭转数据时,是触发 commit 办法,即是这样应用的:

this.$store.commit('eventName', '参数');

所以咱们要实现一个 commit 办法,把 Store 构造函数传入的 mutations 做下解决

class Store {constructor(options) {
    // 实现 state ...

    // mutations
    this.mutations = {} // 存储传进来的 mutations
    let mutations = options.mutations || {}
    // 循环取出事件名进行解决(mutations[事件名]: 执行办法)Object.keys(mutations).forEach(key => {this.mutations[key] = params => {mutations[key].call(this, this.state, params) // 修改 this 指向
      }
    })
  }

  commit = (key, params) => {
    // key 为要触发的事件名
    this.mutations[key](params)
  }
}

dispatch

跟下面的 commit 流程同理

class Store {constructor(options = {}) {
    // ...

    // actions
    this.actions = {}
    let actions = options.actions || {}
    Object.keys(actions).forEach(key => {this.actions[key] = params => {actions[key].call(this, this, params)
      }
    })
  }

  dispatch = (type, payload) => {this.actions[type](payload)
  }
}

getters

getters 理论就是返回 state 的值,在应用的时候是放在 computed 属性,每一个 getter 都是函数模式;

getters 是须要双向绑定的。但不须要双向绑定所有的 getters,只须要绑定我的项目中事件应用的 getters。

这里应用 Object.defineProperty() 办法,它会间接在一个对象上定义一个新属性,或者批改一个对象的现有属性,并返回此对象。

class Store {constructor(options = {}) {
    // ...

    // getters
    this.getters = {}
    let getters = options.getters || {}
    Object.keys(getters).forEach(key => {
      Object.defineProperty(this.getters, key, {get: () => {return getters[key].call(this, this.state)
        },
      })
    })
  }
}

到此为止,曾经能够应用咱们本人写的 vuex 做一些基本操作了,但只能通过 this.$store.xx 的模式调用,故须要再实现办法。

map 辅助函数

先来说说 mapState

没有 map 辅助函数之前这样应用:

computed: {count () {return this.$store.state.count}
}

当映射的计算属性的名称与 state 的子节点名称雷同时,给 mapState 传一个字符串数组。

computed: {
  // 应用对象开展运算符将此对象混入到内部对象中
  ...mapState(['count'])
}

咱们这里简略就只实现数组的状况

export const mapState = args => {let obj = {}
  args.forEach(item => {obj[item] = function () {return this.$store.state[item]
    }
  })
  return obj
}

之后几个 map 辅助函数都是相似

  • mapGetters
export const mapGetters = args => {let obj = {}
  args.forEach(item => {obj[item] = function () {return this.$store.getters[item]
    }
  })
  return obj
}
  • mapMutations
export const mapMutations = args => {let obj = {}
  args.forEach(item => {obj[item] = function (params) {return this.$store.commit(item, params)
    }
  })
  return obj
}
  • mapActions
export const mapActions = args => {let obj = {}
  args.forEach(item => {obj[item] = function (payload) {return this.$store.dispatch(item, payload)
    }
  })
  return obj
}

残缺代码

let Vue = null

class Store {constructor(options) {
    // vuex 的外围就是借用了 vue 的实例,因为 vue 的实例数据变动,会刷新视图
    let vm = new Vue({
      data: {state: options.state,},
    })
    // state
    this.state = vm.state

    // mutations
    this.mutations = {} // 存储传进来的 mutations
    let mutations = options.mutations || {}
    Object.keys(mutations).forEach(key => {this.mutations[key] = params => {mutations[key].call(this, this.state, params)
      }
    })

    // actions
    this.actions = {}
    let actions = options.actions || {}
    Object.keys(actions).forEach(key => {this.actions[key] = params => {actions[key].call(this, this, params)
      }
    })

    // getters
    this.getters = {}
    let getters = options.getters || {}
    Object.keys(getters).forEach(key => {
      Object.defineProperty(this.getters, key, {get: () => {return getters[key].call(this, this.state)
        },
      })
    })
  }

  commit = (key, params) => {this.mutations[key](params)
  }

  dispatch = (type, payload) => {this.actions[type](payload)
  }
}

export const mapState = args => {let obj = {}
  args.forEach(item => {obj[item] = function () {return this.$store.state[item]
    }
  })
  return obj
}

export const mapGetters = args => {let obj = {}
  args.forEach(item => {obj[item] = function () {return this.$store.getters[item]
    }
  })
  return obj
}

export const mapMutations = args => {let obj = {}
  args.forEach(item => {obj[item] = function (params) {return this.$store.commit(item, params)
    }
  })
  return obj
}

export const mapActions = args => {let obj = {}
  args.forEach(item => {obj[item] = function (payload) {return this.$store.dispatch(item, payload)
    }
  })
  return obj
}

function install(_Vue) {
  Vue = _Vue // install 办法调用时,会将 Vue 作为参数传入(下面 Store 类须要用到 Vue)// 实现每一个组件,都能通过 this 调用 $store
  Vue.mixin({beforeCreate() {// 通过 this.$options 能够获取 new Vue({参数}) 传递的参数
      if (this.$options && this.$options.store) {
        // 证实这个 this 是根实例,也就是 new Vue 产生的那个实例
        this.$store = this.$options.store
      } else if (this.$parent && this.$parent.$store) {
        // 子组件获取父组件的 $store 属性
        this.$store = this.$parent.$store
      }
    },
  })
}

export default {
  Store,
  install,
}

整个我的项目的源码地址:mini-vuex

  • ps:集体技术博文 Github 仓库,感觉不错的话欢送 star,给我一点激励持续写作吧~
退出移动版