Skip to content

Commit 31ea250

Browse files
author
Nathan Koterba
committed
Return an ‘unsync’ Fn from the sync method to provie a way to un-sync (vuejs#64)
Currently, vuex-router-sync only publicly exposes a single method: ‘sync’. This method creates a store.watch as well as a router.beforeHook. No publicly available method existed to effectively ‘unsync’ vuex-router-sync and remove the watch and hook, which created ‘dangling’ refrences and a potential memory leak. Users of this library may need an ‘unsync’ function if they, for example, only use Vue.js in specific portions of their webapp (e.g. with React, Angular, etc.) and when users navigate away from that portion they want to remove/clean-up all Vue.js resources and components. Includes a unit test. Had to bump vue-router to version 2.5.0 which was the first version to allow de-registering hook functions.
1 parent fb42e8e commit 31ea250

File tree

4 files changed

+58
-7
lines changed

4 files changed

+58
-7
lines changed

README.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@ import { sync } from 'vuex-router-sync'
1717
import store from './vuex/store' // vuex store instance
1818
import router from './router' // vue-router instance
1919

20-
sync(store, router) // done.
20+
const unsync = sync(store, router) // done. Returns an unsync callback fn
2121

2222
// bootstrap your app...
23+
24+
// During app/Vue teardown (e.g., you only use Vue.js in a portion of your app and you
25+
// navigate away from that portion and want to release/destroy Vue components/resources)
26+
unsync() // Unsyncs store from router
2327
```
2428

2529
You can optionally set a custom vuex module name:
@@ -28,7 +32,6 @@ You can optionally set a custom vuex module name:
2832
sync(store, router, { moduleName: 'RouteModule' } )
2933
```
3034

31-
3235
### How does it work?
3336

3437
- It adds a `route` module into the store, which contains the state representing the current route:

index.js

+17-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ exports.sync = function (store, router, options) {
1515
var currentPath
1616

1717
// sync router on store change
18-
store.watch(
18+
const storeUnwatch = store.watch(
1919
function (state) { return state[moduleName] },
2020
function (route) {
2121
if (route.fullPath === currentPath) {
@@ -32,14 +32,29 @@ exports.sync = function (store, router, options) {
3232
)
3333

3434
// sync store on router navigation
35-
router.afterEach(function (to, from) {
35+
const afterEachUnHook = router.afterEach(function (to, from) {
3636
if (isTimeTraveling) {
3737
isTimeTraveling = false
3838
return
3939
}
4040
currentPath = to.fullPath
4141
store.commit(moduleName + '/ROUTE_CHANGED', { to: to, from: from })
4242
})
43+
44+
return function unsync() {
45+
// On unsync, remove router hook
46+
if (afterEachUnHook != null) {
47+
afterEachUnHook()
48+
}
49+
50+
// On unsync, remove store watch
51+
if (storeUnwatch != null) {
52+
storeUnwatch();
53+
}
54+
55+
// On unsync, unregister Module with store
56+
store.unregisterModule(moduleName)
57+
}
4358
}
4459

4560
function cloneRoute (to, from) {

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"vuex": "^2.1.0"
3333
},
3434
"peerDependencies": {
35-
"vue-router": "^2.0.0",
35+
"vue-router": "^2.5.0",
3636
"vuex": "^2.1.0"
3737
}
3838
}

test/test.js

+35-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const run = (originalModuleName, done) => {
3232
]
3333
})
3434

35-
sync(store, router, {
35+
const unsync = sync(store, router, {
3636
moduleName: originalModuleName
3737
})
3838

@@ -56,7 +56,7 @@ const run = (originalModuleName, done) => {
5656

5757
Vue.nextTick(() => {
5858
expect(app.$el.textContent).toBe('/c/d?n=1#hello c d')
59-
done()
59+
done(unsync)
6060
})
6161
}
6262

@@ -67,3 +67,36 @@ test('default usage', done => {
6767
test('with custom moduleName', done => {
6868
run('moduleName', done)
6969
})
70+
71+
test('desync', done => {
72+
const store = new Vuex.Store()
73+
spyOn(store, "watch").and.callThrough()
74+
75+
const router = new VueRouter()
76+
77+
const moduleName = 'testDesync'
78+
const unsync = sync(store, router, {
79+
moduleName: moduleName
80+
})
81+
82+
expect(unsync).toBeInstanceOf(Function)
83+
84+
// Test module registered, store watched, router hooked
85+
expect(store.state[moduleName]).toBeDefined()
86+
expect(store.watch).toHaveBeenCalled()
87+
expect(store._watcherVM).toBeDefined()
88+
expect(store._watcherVM._watchers).toBeDefined()
89+
expect(store._watcherVM._watchers.length).toBe(1)
90+
expect(router.afterHooks).toBeDefined()
91+
expect(router.afterHooks.length).toBe(1)
92+
93+
// Now unsync vuex-router-sync
94+
unsync()
95+
96+
// Ensure router unhooked, store-unwatched, module unregistered
97+
expect(router.afterHooks.length).toBe(0)
98+
expect(store._watcherVm).toBeUndefined()
99+
expect(store.state[moduleName]).toBeUndefined()
100+
101+
done()
102+
})

0 commit comments

Comments
 (0)