vuex-cache
Advanced tools
Comparing version 1.2.0 to 1.3.0
@@ -9,8 +9,30 @@ import Vue from 'vue' | ||
const result = [1, 2, 3] | ||
let store, spy | ||
let store, listSpy, moduleASpy | ||
beforeEach(() => { | ||
spy = jest.fn(() => { | ||
listSpy = jest.fn(() => { | ||
return Promise.resolve(result) | ||
}) | ||
moduleASpy = jest.fn(() => { | ||
return Promise.resolve() | ||
}) | ||
const moduleA = { | ||
state: { | ||
members: [], | ||
}, | ||
mutations: { | ||
MODULEA_ADD_MEMBER(state, payload) { | ||
state.members.push(payload) | ||
}, | ||
}, | ||
actions: { | ||
MODULEA_ADD_MEMBER({ commit }, value) { | ||
return moduleASpy().then(() => { | ||
commit('MODULEA_ADD_MEMBER', value) | ||
}) | ||
}, | ||
}, | ||
} | ||
store = new Vuex.Store({ | ||
@@ -28,10 +50,14 @@ state: { | ||
}, | ||
NAME(state, payload) { | ||
state.name = payload | ||
NAME(state, { name }) { | ||
state.name = name | ||
}, | ||
}, | ||
modules: { | ||
moduleA, | ||
}, | ||
actions: { | ||
LIST({ commit }) { | ||
spy().then(list => { | ||
listSpy().then(list => { | ||
commit('LIST', list) | ||
@@ -47,2 +73,6 @@ }) | ||
afterEach(() => { | ||
jest.restoreAllMocks() | ||
}) | ||
it('cache action', done => { | ||
@@ -52,9 +82,8 @@ const dispatchSpy = jest.spyOn(store, 'dispatch') | ||
expect(dispatchSpy).toHaveBeenCalledWith('LIST', 1, 2) | ||
expect(spy.mock.calls).toHaveLength(1) | ||
expect(listSpy.mock.calls).toHaveLength(1) | ||
store.cache.dispatch('LIST') | ||
expect(spy.mock.calls).toHaveLength(2) | ||
expect(listSpy.mock.calls).toHaveLength(2) | ||
Vue.nextTick(() => { | ||
expect(store.state.list).toEqual(result) | ||
dispatchSpy.mockRestore() | ||
done() | ||
@@ -66,9 +95,9 @@ }) | ||
store.cache.dispatch('LIST') | ||
expect(spy.mock.calls).toHaveLength(1) | ||
expect(listSpy.mock.calls).toHaveLength(1) | ||
expect(store.cache.delete('LIST')).toBe(true) | ||
expect(store.cache.delete('LIST')).toBe(false) | ||
store.cache.dispatch('LIST') | ||
expect(spy.mock.calls).toHaveLength(2) | ||
expect(listSpy.mock.calls).toHaveLength(2) | ||
store.cache.dispatch('LIST') | ||
expect(spy.mock.calls).toHaveLength(2) | ||
expect(listSpy.mock.calls).toHaveLength(2) | ||
}) | ||
@@ -81,9 +110,10 @@ | ||
it('clear all cache', () => { | ||
const name = 'abc' | ||
store.cache.dispatch('LIST') | ||
store.cache.dispatch('NAME', 'abc') | ||
store.cache.dispatch('NAME', { name }) | ||
expect(store.cache.has('LIST')).toBe(true) | ||
expect(store.cache.has('NAME', 'abc')).toBe(true) | ||
expect(store.cache.has('NAME', { name })).toBe(true) | ||
store.cache.clear() | ||
expect(store.cache.has('LIST')).toBe(false) | ||
expect(store.cache.has('NAME', 'abc')).toBe(false) | ||
expect(store.cache.has('NAME', { name })).toBe(false) | ||
}) | ||
@@ -94,13 +124,13 @@ | ||
type: 'LIST', | ||
page: 1, | ||
payload: 1, | ||
}) | ||
store.cache.dispatch({ | ||
type: 'LIST', | ||
page: 1, | ||
payload: 1, | ||
}) | ||
store.cache.dispatch({ | ||
type: 'LIST', | ||
page: 2, | ||
payload: 2, | ||
}) | ||
expect(spy.mock.calls).toHaveLength(2) | ||
expect(listSpy.mock.calls).toHaveLength(2) | ||
}) | ||
@@ -113,3 +143,3 @@ | ||
}) | ||
expect(spy.mock.calls).toHaveLength(1) | ||
expect(listSpy.mock.calls).toHaveLength(1) | ||
expect( | ||
@@ -131,3 +161,3 @@ store.cache.delete({ | ||
store.cache.dispatch('LIST', 1) | ||
expect(spy.mock.calls).toHaveLength(1) | ||
expect(listSpy.mock.calls).toHaveLength(1) | ||
expect(store.cache.has('LIST', 1)).toBe(true) | ||
@@ -177,7 +207,85 @@ expect(store.cache.delete('LIST', 1)).toBe(true) | ||
store.cache.dispatch('LIST', { page: 1 }) | ||
expect(spy.mock.calls).toHaveLength(1) | ||
expect(listSpy.mock.calls).toHaveLength(1) | ||
store.cache.dispatch('LIST', { page: 2 }) | ||
store.cache.dispatch('LIST', { page: 2 }) | ||
expect(spy.mock.calls).toHaveLength(2) | ||
expect(listSpy.mock.calls).toHaveLength(2) | ||
}) | ||
it('test cache dispatch for module', done => { | ||
expect(store.state.moduleA.members).toEqual([]) | ||
Promise.all([ | ||
store.cache.dispatch('MODULEA_ADD_MEMBER', 1), | ||
store.cache.dispatch('MODULEA_ADD_MEMBER', 1), | ||
store.cache.dispatch('MODULEA_ADD_MEMBER', 1), | ||
]).then(() => { | ||
expect(store.state.moduleA.members).toEqual([1]) | ||
expect(moduleASpy).toHaveBeenCalledTimes(1) | ||
done() | ||
}) | ||
}) | ||
describe('add timeout configuration', () => { | ||
const sleep = time => new Promise(resolve => setTimeout(resolve, time)) | ||
it('object format param', async () => { | ||
await store.cache.dispatch({ | ||
type: 'LIST', | ||
timeout: 100, | ||
}) | ||
await store.cache.dispatch({ | ||
type: 'LIST', | ||
timeout: 100, | ||
}) | ||
expect(listSpy).toHaveBeenCalledTimes(1) | ||
await sleep(110) | ||
await store.cache.dispatch({ | ||
type: 'LIST', | ||
timeout: 100, | ||
}) | ||
await store.cache.dispatch({ | ||
type: 'LIST', | ||
timeout: 100, | ||
}) | ||
expect(listSpy).toHaveBeenCalledTimes(2) | ||
await sleep(90) | ||
await store.cache.dispatch({ | ||
type: 'LIST', | ||
timeout: 100, | ||
}) | ||
await store.cache.dispatch({ | ||
type: 'LIST', | ||
timeout: 100, | ||
}) | ||
expect(listSpy).toHaveBeenCalledTimes(2) | ||
}) | ||
it('three param', async () => { | ||
await store.cache.dispatch('LIST', null, { | ||
timeout: 100, | ||
}) | ||
await store.cache.dispatch('LIST', null, { | ||
timeout: 100, | ||
}) | ||
expect(listSpy).toHaveBeenCalledTimes(1) | ||
await sleep(110) | ||
await store.cache.dispatch('LIST', null, { | ||
timeout: 100, | ||
}) | ||
await store.cache.dispatch('LIST', null, { | ||
timeout: 100, | ||
}) | ||
expect(listSpy).toHaveBeenCalledTimes(2) | ||
await sleep(90) | ||
await store.cache.dispatch('LIST', null, { | ||
timeout: 100, | ||
}) | ||
await store.cache.dispatch('LIST', null, { | ||
timeout: 100, | ||
}) | ||
expect(listSpy).toHaveBeenCalledTimes(2) | ||
}) | ||
}) | ||
}) |
@@ -19,2 +19,7 @@ module.exports = { | ||
], | ||
env: { | ||
development: { | ||
plugins: ['@babel/plugin-transform-runtime'], | ||
}, | ||
}, | ||
} |
29
index.js
@@ -15,7 +15,36 @@ 'use strict'; | ||
// parse timeout prop in option | ||
const getTimeout = args => { | ||
if (args.length === 1 && args[0].timeout) { | ||
return args[0].timeout | ||
} | ||
if (args.length === 3 && args[2].timeout) { | ||
return args[2].timeout | ||
} | ||
return 0 | ||
}; | ||
var index = store => { | ||
const cache = new Map(); | ||
// use another map to store timeout for each type | ||
const timeoutCache = new Map(); | ||
cache.dispatch = (...args) => { | ||
const type = argsToString(args); | ||
const timeout = getTimeout(args); | ||
if (timeout) { | ||
const now = Date.now(); | ||
if (!timeoutCache.has(type)) { | ||
timeoutCache.set(type, now); | ||
} else { | ||
const timeoutOfCurrentType = timeoutCache.get(type); | ||
// console.log(now - timeout, timeoutOfCurrentType) | ||
if (now - timeout > timeoutOfCurrentType) { | ||
cache.delete(type); | ||
timeoutCache.delete(type); | ||
} | ||
} | ||
} | ||
if (!cache.has(type)) { | ||
@@ -22,0 +51,0 @@ cache.set(type, store.dispatch.apply(store, args)); |
@@ -13,7 +13,36 @@ // convert string or obj to string | ||
// parse timeout prop in option | ||
const getTimeout = args => { | ||
if (args.length === 1 && args[0].timeout) { | ||
return args[0].timeout | ||
} | ||
if (args.length === 3 && args[2].timeout) { | ||
return args[2].timeout | ||
} | ||
return 0 | ||
}; | ||
var index = store => { | ||
const cache = new Map(); | ||
// use another map to store timeout for each type | ||
const timeoutCache = new Map(); | ||
cache.dispatch = (...args) => { | ||
const type = argsToString(args); | ||
const timeout = getTimeout(args); | ||
if (timeout) { | ||
const now = Date.now(); | ||
if (!timeoutCache.has(type)) { | ||
timeoutCache.set(type, now); | ||
} else { | ||
const timeoutOfCurrentType = timeoutCache.get(type); | ||
// console.log(now - timeout, timeoutOfCurrentType) | ||
if (now - timeout > timeoutOfCurrentType) { | ||
cache.delete(type); | ||
timeoutCache.delete(type); | ||
} | ||
} | ||
} | ||
if (!cache.has(type)) { | ||
@@ -20,0 +49,0 @@ cache.set(type, store.dispatch.apply(store, args)); |
@@ -21,7 +21,22 @@ (function (global, factory) { | ||
return type; | ||
}; // parse timeout prop in option | ||
var getTimeout = function getTimeout(args) { | ||
if (args.length === 1 && args[0].timeout) { | ||
return args[0].timeout; | ||
} | ||
if (args.length === 3 && args[2].timeout) { | ||
return args[2].timeout; | ||
} | ||
return 0; | ||
}; | ||
var index = (function (store) { | ||
var cache = new Map(); | ||
var cache = new Map(); // use another map to store timeout for each type | ||
var timeoutCache = new Map(); | ||
cache.dispatch = function () { | ||
@@ -33,3 +48,19 @@ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
var type = argsToString(args); | ||
var timeout = getTimeout(args); | ||
if (timeout) { | ||
var now = Date.now(); | ||
if (!timeoutCache.has(type)) { | ||
timeoutCache.set(type, now); | ||
} else { | ||
var timeoutOfCurrentType = timeoutCache.get(type); // console.log(now - timeout, timeoutOfCurrentType) | ||
if (now - timeout > timeoutOfCurrentType) { | ||
cache.delete(type); | ||
timeoutCache.delete(type); | ||
} | ||
} | ||
} | ||
if (!cache.has(type)) { | ||
@@ -36,0 +67,0 @@ cache.set(type, store.dispatch.apply(store, args)); |
{ | ||
"name": "vuex-cache", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "cache vuex action", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -8,2 +8,41 @@ # vuex cache action | ||
#### 2018-10-11 | ||
##### NEW FEATUE, add `timeout` option | ||
```javascript | ||
store.cache.dispatch({ | ||
type: ACTION_NAME, | ||
timeout: 100, | ||
payload: { ... } | ||
}) | ||
// or | ||
store.cache.dispatch(ACTION_NAME, payload, { | ||
timeout: 100, | ||
}) | ||
``` | ||
after the `timeout` time, run `store.cache.dispatch(...)` will rerun the real dispatch code. | ||
##### NOTICE for `timeout` option | ||
Because the js execution also spends time, so the `timeout` maybe not very accurate for computer time. For example | ||
cache for 100 millisecond | ||
```javascript | ||
store.cache.dispatch({ | ||
type: ACTION_NAME, | ||
timeout: 100, | ||
}) | ||
``` | ||
... after 99 millisecond | ||
```javascript | ||
store.cache.dispatch({ | ||
type: ACTION_NAME, | ||
timeout: 100, | ||
}) | ||
``` | ||
In logic, the cache is not out of time, and the real dispatch should not run. But the `cache.dispatch` may cost 1 or more milliseconds for executing, so the real dispatch may or may __not__ execute. | ||
For human time, the precision should be enough. | ||
### Compatibility | ||
@@ -94,2 +133,3 @@ - Any Vue version, since `vuex-cache` just deals with Vuex | ||
``` | ||
remove cached action, will **NOT** remove the data in store. when call cacheDispatch with same type, the request in that action will run again. | ||
@@ -107,2 +147,3 @@ | ||
``` | ||
return bool if ACTION\_NAME has been cached | ||
@@ -113,2 +154,3 @@ | ||
``` | ||
clear all cached keys |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
18758
472
153
0