作为一个学习者,应该要有记录笔记的习惯,和村长学习了vuex的简易实现方式,过两天就忘了岂不糟糕?
首先在测试项目中直接把vuex替换为自己写的简易版fvuex插件作为测试用例
// 1. 简单配置下store下面的index.js中的配置项
export default new Vuex.Store({
state: {
counter: 0
},
getters: {
doubleCounter: state => state.counter * 2
},
mutations: {
add (state) {
state.counter++
}
},
actions: {
add ({ commit }) {
setTimeout(() => {
commit('add')
}, 1000)
}
}
})
// 2. 在App.vue中写入测试代码,保证在使用正常的Vuex插件中可以正常反馈效果
<div @click="$store.commit('add')">counter: {{$store.state.counter}}</div>
<div @click="$store.dispatch('add')">async counter: {{$store.state.counter}}</div>
<div @click="$store.getters.doubleCounter++">doubleCounter: {{$store.getters.doubleCounter}}</div>
// 把vuex插件修改为简易版fvuex插件
// 3. 复制一份store文件夹,并命名为fstore,在该文件夹下创建一个fstore.js作为插件
// 然后在fstore下面的index.js中引入fstore.js并使用该插件,导出不变
import Vuex from './fstore.js'
Vue.use(Vuex)
// 4.在main.js中修改引入的store,并将之挂载到Vue实例中
import store from './fstore'
new Vue({
store,
render: h => h(App)
}).$mount('#app')
实现方式
创建js插件
- 实现Store类
- 实现一个响应式的state
- 实现getters派生对象
- 实现commit方法
- 实现dispatch异步方法
- 实现install方法挂载$store
- 导出Vuex对象,包含Store类与install方法
// fstore.js
let Vue; // 声明Vue变量方便全局使用
class Store {
constructor (options = {}) {
// 声明computed变量
const computed = {}
const store = this // 将this指向保存到store中
// 保存用户配置的getters选项
// {doubleCounter(state){}}
this.getters = options.getters || {}
Object.keys(this.getters).forEach(key => {
// 获取用户定义的getter,它是一个无参数的函数
const fn = store.getters[key]
// 将有参数的函数转换为computed可以使用的无参数形式
computed[key] = function () {
return fn(store.state)
}
Object.defineProperty(this.getters, key, {
get: () => {
// 此处必须用箭头函数来使this指向store实例
// computed中的属性可以被vue实例直接访问到
return store._vm[key]
},
set: () => {
console.error('请用commit方法修改state中的属性。')
}
})
})
// 通过vue的实例化中的data()把state变成响应式的属性
this._vm = new Vue({
data () {
return {
$$state: options.state // 隐藏state变量保存在类内部的_vm中的_data中的$$state中
// 防止用户直接通过this.state.xx访问或this.state.xx++修改
// 只允许用户通过单一的state方式访问和commit方法修改
}
},
computed
})
// 保存用户配置的mutations选项
this._mutations = options.mutations || {}
// 保存用户配置的actions选项
this._actions = options.actions || {}
// 并绑定commit和dispatch上下文,防止用户在setTimeout中调用这些方法时this指向出错
const {commit, dispatch} = store
store.commit = (type, payload) => {
commit.call(store, type, payload)
}
store.dispatch = (type, payload) => {
dispatch.call(store, type, payload)
}
}
get state () {
// 用户在此入口访问state中的属性
return this._vm._data.$$state
}
set state (value) {
// 不允许用户直接通过state.xx修改属性值
console.error('请用commit方法修改state中的属性。')
}
commit (type, payload) {
const entry = this._mutations[type]
if (!entry) {
return console.error(`unknown mutation type: ${type}`)
}
entry(this.state, payload)
}
dispatch (type, payload) {
const entry = this._actions[type]
if (!entry) {
return console.error(`unknown action type: ${type}`)
}
// 此处要传Store的实例作为dispatch的上下文
// 异步方法要return 一个 promise
return entry(this, payload)
}
}
function install (_Vue) {
// 将_Vue构造方法保存到Vue变量中
Vue = _Vue;
Vue.mixin({
beforeCreate () {
// 此处的this指的是组件实例
if (this.$options.store) {
Vue.prototype.$store = this.$options.store
}
}
})
}
export default { Store, install }