import { applyMixin } from './install'
const forEachValue = (obj, cb) => {
Object.keys(obj).forEach(key => cb(obj[key], key))
}
export let Vue
export class Store {
constructor(options) {
let computed = {}
this.getters = {}
// 在这里执行是为了让computed先赋值后调用
forEachValue(options.getters, (value, key) => {
// this.getters[key] = value // value现在是function,但是getters是属性 在组件中是这样使用的 -> $store.getters.getCount
// 不这样写的原因是因为这样直接将value执行的结果赋值给了getters,再次取值后值不会变
// this.getters[key] = value(this.state)
// 这样写可以将getters变成属性,直接调用
// Object.defineProperty(this.getters, key, {
// // 这种方式每次取值都会重新执行一次,如果在页面中多次调用同一个getters的值,也要执行多次,性能不高
// get:() => {
// return value.call(this, this.state )
// }
// })
computed[key] = () => { // 通过 computed 属性做了优化,计算属性也会被放到当前实例上
return value.call(this, this.state)
}
Object.defineProperty(this.getters, key, {
get:() => {
return this._vm[key] // 取 computed 的key属性
}
})
})
// 把数据变成响应式的
this._vm = new Vue({
data: {
// 不加 $ 这样写内部会使用代理操作,把所有属性代理给 this._vm,Vue不会对 $ 符开头的属性进行代理操作,这样写可以避免代理节省性能
$$state: options.state
},
computed
})
this.actions = {}
this.mutations = {}
forEachValue(options.mutations, (fn, key) => {
this.mutations[key] = (payload) => fn.call(this, this.state, payload)
})
forEachValue(options.actions, (fn, key) => {
this.actions[key] = (payload) => fn.call(this, this, payload)
})
console.log('this._vm',this._vm)
}
get state() {
return this._vm._data.$$state
}
// 使用箭头函数绑定this指向,因为如果通过store.commit调动,this指向的是store,但是直接调用commit,this就有可能发生了变化
commit = (type, payload) => {
this.mutations[type](payload)
}
dispatch = (type, payload) => {
this.actions[type](payload)
}
}
export const install = (_Vue) => {
Vue = _Vue
applyMixin(Vue)
}
<template>
<div id="app">
<div>
count:{{$store.state.count}}
</div>
<!-- getters里面的未发生变化的情况下,多次调用getters里面的值,只需执行一次,节省性能 -->
<div>
gettter:{{$store.getters.getCount}}
</div>
<div>
gettter:{{$store.getters.getCount}}
</div>
<div>
gettter:{{$store.getters.getCount}}
</div>
<button @click="$store.state.count = 10">不合规改变state</button>
<!-- 不要直接修改state的值,因为一个state的值可能被多个组件使用,如果在某个组件里直接修改这个state的值,会影响到其他组件,不利于查找数据的改变来源。 -->
<button @click="$store.state.count = 10">不合规改变state</button>
<button @click="$store.commit('changeCount', 10)">同步加{{$store.state.count}}</button>
<button @click="$store.dispatch('changeCount', 100)">异步加{{$store.state.count}}</button>
</div>
</template>
<script>
export default {
mounted() {
console.log(this.$store)
}
}
</script>