前言
本文适宜应用过 Vuex 的人浏览,来理解下怎么本人实现一个 Vuex。
根本骨架
这是本我的项目的src/store/index.js
文件,看看个别 vuex 的应用
import Vue from 'vue'import Vuex from './myvuex' // 引入本人写的 vueximport * 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 = nullclass 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 = nullclass 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,给我一点激励持续写作吧~