Cached Getters - complete
by Michael Gallagher
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.7/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.0/vuex.min.js"></script>
<script src="https://unpkg.com/vue-pursue@0.1.3/dist/vue-pursue.js"></script>
<script type="text/x-template" id="comp">
<div>{{ task.name }}<br/>
<pre>{{ Date.now() }}</pre>
</div>
</script>
<div id="app" />
<div class="output" />
JavaScript
let store;
let hits = 0;
let misses = 0;
// recreation of a computed property
const computed = (vm, func) => {
vm.$watch(
func,
null, // callback not used for lazy
{ lazy: true }, // lazy means we can just flag as dirty
);
// eslint-disable-next-line no-underscore-dangle
const watcher = vm._watchers[vm._watchers.length - 1];
// add an accessor
watcher.lazyValue = () => {
if (watcher.dirty) {
misses += 1;
watcher.evaluate();
} else {
hits += 1;
}
console.log(`H:${hits} M:${misses}`);
watcher.depend(); // means any calling computed will have transitive deps
return watcher.value;
};
return watcher;
};
// Cache Wrapper for Method-style Getter
const methodStyleCache = (getVM, getter) => {
let watchers = {};
return (...args) => {
// Remove old watchers if the getter is rebuilt
Object.values(watchers).forEach((w) => w.teardown());
watchers = {};
const innerMethod = getter.call(null, ...args);
return (id) => {
let watcher = watchers[id];
if (!watcher) {
watcher = computed(getVM(), () => innerMethod(id));
watchers[id] = watcher;
}
return watcher.lazyValue();
};
};
};
// Cacher that can be attached as a plugin to get a store reference
const methodGetterCacher = () => {
let store;
const getVM = () => store._vm;
return {
plugin: (st) => { store = st; },
cache: (getter) => methodStyleCache(getVM, getter),
};
}
const { plugin, cache } = methodGetterCacher();
store = new Vuex.Store({
plugins: [plugin],
state: {
tasks: {
1: {
id: 1,
name: 'Setup'
},
2: {
id: 2,
name: 'Impl'
},
3: {
id: 3,
name: 'Testing'
},
},
},
getters: {
mappedTask: cache((state) => (id) => ({
name: state.tasks[id].name.toUpperCase(),
})),
},
mutations: {
changeOne: (state) => {
state.tasks[1].name = 'Preparation';
},
...