Skip to content

Commit 0c01cb7

Browse files
committed
make getters globally cached
1 parent 4877cb4 commit 0c01cb7

File tree

6 files changed

+83
-25
lines changed

6 files changed

+83
-25
lines changed

examples/chat/components/MessageSection.vue

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,14 @@
1515
<script>
1616
import Message from './Message.vue'
1717
import { sendMessage } from '../vuex/actions'
18+
import { currentThread, currentMessages } from '../vuex/getters'
1819
1920
export default {
2021
components: { Message },
2122
vuex: {
2223
getters: {
23-
thread ({ currentThreadID, threads }) {
24-
return currentThreadID ? threads[currentThreadID] : {}
25-
},
26-
messages ({ messages }) {
27-
const messageIds = this.thread.messages
28-
return messageIds && messageIds.map(id => messages[id])
29-
}
24+
thread: currentThread,
25+
messages: currentMessages
3026
},
3127
actions: {
3228
sendMessage

examples/chat/components/Thread.vue

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<li
33
class="thread-list-item"
4-
:class="{ active: isCurrentThread }"
4+
:class="{ active: thread.id === currentThreadID }"
55
@click="switchThread(thread.id)">
66
<h5 class="thread-name">{{ thread.name }}</h5>
77
<div class="thread-time">
@@ -20,9 +20,7 @@ export default {
2020
props: ['thread'],
2121
vuex: {
2222
getters: {
23-
isCurrentThread ({ currentThreadID }) {
24-
return this.thread.id === currentThreadID
25-
}
23+
currentThreadID: state => state.currentThreadID
2624
},
2725
actions: {
2826
switchThread

examples/chat/vuex/getters.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export function currentThread (state) {
2+
return state.currentThreadID
3+
? state.threads[state.currentThreadID]
4+
: {}
5+
}
6+
7+
export function currentMessages (state) {
8+
const thread = currentThread(state)
9+
return thread.messages
10+
? thread.messages.map(id => state.messages[id])
11+
: []
12+
}

src/index.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { mergeObjects, deepClone } from './util'
1+
import { mergeObjects, deepClone, getWatcher } from './util'
22
import devtoolMiddleware from './middlewares/devtool'
33
import override from './override'
44

55
let Vue
6+
let uid = 0
67

78
class Store {
89

@@ -22,6 +23,7 @@ class Store {
2223
middlewares = [],
2324
strict = false
2425
} = {}) {
26+
this._getterCacheId = 'vuex_store_' + uid++
2527
this._dispatching = false
2628
this._rootMutations = this._mutations = mutations
2729
this._modules = modules
@@ -184,12 +186,7 @@ class Store {
184186
*/
185187

186188
_setupMutationCheck () {
187-
// a hack to get the watcher constructor from older versions of Vue
188-
// mainly because the public $watch method does not allow sync
189-
// watchers.
190-
const unwatch = this._vm.$watch('__vuex__', a => a)
191-
const Watcher = this._vm._watchers[0].constructor
192-
unwatch()
189+
const Watcher = getWatcher(this._vm)
193190
/* eslint-disable no-new */
194191
new Watcher(this._vm, '$data', () => {
195192
if (!this._dispatching) {

src/override.js

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { getWatcher, getDep } from './util'
2+
13
export default function (Vue) {
24
// override init and inject vuex init procedure
35
const _init = Vue.prototype._init
@@ -38,28 +40,58 @@ export default function (Vue) {
3840
if (getters) {
3941
options.computed = options.computed || {}
4042
for (let key in getters) {
41-
options.computed[key] = makeBoundGetter(getters[key])
43+
defineVuexGetter(this, key, getters[key])
4244
}
4345
}
4446
// actions
4547
if (actions) {
4648
options.methods = options.methods || {}
4749
for (let key in actions) {
48-
options.methods[key] = makeBoundAction(actions[key])
50+
options.methods[key] = makeBoundAction(actions[key], this.$store)
4951
}
5052
}
5153
}
5254
}
5355

54-
function makeBoundGetter (getter) {
55-
return function vuexBoundGetter () {
56-
return getter.call(this, this.$store.state)
56+
function defineVuexGetter (vm, key, getter) {
57+
Object.defineProperty(vm, key, {
58+
enumerable: true,
59+
configurable: true,
60+
get: makeComputedGetter(vm.$store, getter)
61+
})
62+
}
63+
64+
function makeComputedGetter (store, getter) {
65+
const id = store._getterCacheId
66+
// cached
67+
if (getter[id]) {
68+
return getter[id]
69+
}
70+
const vm = store._vm
71+
const Watcher = getWatcher(vm)
72+
const Dep = getDep(vm)
73+
const watcher = new Watcher(
74+
vm,
75+
state => getter(state),
76+
null,
77+
{ lazy: true }
78+
)
79+
const computedGetter = () => {
80+
if (watcher.dirty) {
81+
watcher.evaluate()
82+
}
83+
if (Dep.target) {
84+
watcher.depend()
85+
}
86+
return watcher.value
5787
}
88+
getter[id] = computedGetter
89+
return computedGetter
5890
}
5991

60-
function makeBoundAction (action) {
92+
function makeBoundAction (action, store) {
6193
return function vuexBoundAction (...args) {
62-
return action.call(this, this.$store, ...args)
94+
return action.call(this, store, ...args)
6395
}
6496
}
6597

src/util.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,26 @@ export function deepClone (obj) {
4747
return obj
4848
}
4949
}
50+
51+
/**
52+
* Hacks to get access to Vue internals.
53+
* Maybe we should expose these...
54+
*/
55+
56+
let Watcher
57+
export function getWatcher (vm) {
58+
if (!Watcher) {
59+
const unwatch = vm.$watch('__vuex__', a => a)
60+
Watcher = vm._watchers[0].constructor
61+
unwatch()
62+
}
63+
return Watcher
64+
}
65+
66+
let Dep
67+
export function getDep (vm) {
68+
if (!Dep) {
69+
Dep = vm._data.__ob__.dep.constructor
70+
}
71+
return Dep
72+
}

0 commit comments

Comments
 (0)