miniVuex
小米糖糖 11/18/2020 vuexminivue
# VueX 原理
- 利用的是 vue 的数据响应
- 对 state 进行代理,并在更新时触发对应的依赖
- 封装 commit 控制更新 state 只能通过 mutation 从而监控数据流向
- 保证状态以一种可预测的方式发生变化
- 提供一系列工具函数
# mini 实现效果
# mini 实现代码
实现较为简单,并且只做了 一层 未做命名空间
# 使用
与正常的 Vuex 基本一致,不过不能使用命名空间模块化 mini 嘛 简单处理一下
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<!-- <script src="https://unpkg.com/vuex"></script> -->
<title>miniVuex</title>
</head>
<body>
<div id="app">
<h2>{{title}}</h2>
<hr>
<Demo1></Demo1>
<hr>
<Demo2></Demo2>
</div>
</body>
<script src="./vuexUtils.js"></script>
<script src="./miniVuex.js"></script>
<script src="./components.js"></script>
<script src="./store.js"></script>
<script src="./app.js"></script>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
components.js
const Demo1 = {
template: `<div>
<h4>使用$state commit dispatch</h4>
{{$store.getters.key1}}
<div>key1:{{$store.state.key1}}</div>
<button @click='commit'>Commit Key1</button>
<button @click='dispach'>dispach Key1</button>
</div>`,
methods: {
commit() {
this.commitCount = this.commitCount || 0
this.commitCount++
this.$store.commit('setKey1', `CommitKey1:${this.commitCount} Times`)
},
dispach() {
this.dispatchCount = this.dispatchCount || 0
this.dispatchCount++
this.$store.dispatch('setKey1', `dispatch1:${this.dispatchCount} Times`)
}
},
}
const { mapGetters: mapG, mapActions: mapA, mapMutations: mapM } = Vuex
const Demo2 = {
template: `<div>
<h4>使用 mapGetters mapActions mapMutations</h4>
<div>key1:{{key1}}</div>
<div>key2:{{key2}}</div>
<button @click='setKey1("mapMutations")'>Commit Key1</button>
<button @click='setKey2("mapActions")'>dispach Key2</button>
</div>`,
computed: {
...mapG(['key1', 'key2'])
},
methods: {
...mapM(['setKey1']),
...mapA(['setKey2'])
},
}
Vue.component('Demo1', Demo1)
Vue.component('Demo2', Demo2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
store.js
const store = new Vuex.Store({
state: { key1: 'key1', key2: 'key2' },
mutations: {
setKey1(state, v) {
state.key1 = v
},
setKey2(state, v) {
state.key2 = v
}
},
getters: {
key1(state) {
return state.key1
},
key2(state) {
return state.key2
},
},
actions: {
setKey1(store, data) {
setTimeout(() => {
store.commit('setKey1', data)
}, 1000)
},
setKey2(store, data) {
setTimeout(() => {
store.commit('setKey2', data)
}, 1000)
}
},
plugins: [logPlug]
})
// 插件,可以用于 持久化等操作
function logPlug(store) {
store.subscribe((store, type) => {
console.log('logger---start')
console.log(type)
console.log('当前快照')
console.log(store)
console.log('logger---end')
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
app.js
const app = new Vue({
el: "#app",
store,
data: {
title: "Vuex"
}
})
1
2
3
4
5
6
7
2
3
4
5
6
7
# mini 源码部分
vuexUtils.js
function mapGetters(keys = []) {
return keys.reduce((r, key) => {
r[key] = function () {
return this.$store.getters[key]
}
return r
}, {})
}
function mapState(keys = []) {
return keys.reduce((r, key) => {
r[key] = function () {
return this.$store.state[key]
}
return r
}, {})
}
function mapActions(keys) {
return keys.reduce((r, key) => {
r[key] = function (payload) {
return this.$store.dispatch(key, payload)
}
return r
}, {})
}
function mapMutations(keys) {
return keys.reduce((r, key) => {
r[key] = function (payload) {
return this.$store.commit(key, payload)
}
return r
}, {})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
miniVuex.js
class Store {
constructor(options) {
// 尝试自动install一次,如果在浏览器环境 不需要用户手动 use
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
this.$options = options
const { state, getters, actions, mutations, plugins = [] } = this.$options
this.$mutations = mutations
this.$actions = actions
// vuex响应式的原理 依赖于Vue 所以 其他框架不能使用vuex
this.$vm = new Vue({ data: state })
this.getters = {}
this.handleGetters(getters)
this._subscribe = []
// 处理插件 插件注册的回调函数会在每次commit之后执行
plugins.forEach(plugin => plugin(this))
}
get state() { return this.$vm }
// 不允许 直接修改state 只能使用commit 也就是mutation
// 此处只是简单处理,源码中提取了一个 withCommit统一处理
set state(val) {
throw new Error('不能直接赋值给state', val)
}
// getters特殊处理,可以使用computed 但是 一个变化触发全部更新可能不是一个好的方式
handleGetters(getters) {
Object.keys(getters).forEach(key => {
Object.defineProperty(this.getters, key, {
get: () => { return getters[key](this.state) }
})
})
}
// 订阅commit 提供给插件使用
subscribe(fn) { this._subscribe.push(fn) }
// 执行同步提交
commit(method, payload) {
let m = this.$mutations[method]
if (m) {
m(this.$vm, payload)
}
this._subscribe.forEach(f => f(this, { type: method, payload }))
}
// 执行异步提交
dispatch(method, payload) {
let m = this.$actions[method]
if (m) {
m(this, payload)
}
}
}
// 混入
function install(Vue) {
Vue.mixin({
beforeCreate() {
if (this.$options.store) {
Vue.prototype.$store = this.$options.store
}
}
})
}
const Vuex = {
Store, install, mapGetters, mapState, mapActions, mapMutations
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65