Skip to content

Commit e8eef6d

Browse files
committed
feat(api): Add dynamic routing support: removeRoute, replaceRoutes
1 parent ddc6bc7 commit e8eef6d

File tree

8 files changed

+522
-1
lines changed

8 files changed

+522
-1
lines changed

examples/edit-routes/app.js

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import Vue from 'vue'
2+
import VueRouter from 'vue-router'
3+
4+
// 1. Use plugin.
5+
// This installs <router-view> and <router-link>,
6+
// and injects $router and $route to all router-enabled child components
7+
Vue.use(VueRouter)
8+
9+
// 2. Define route components
10+
const Home = { template: '<div>home</div>' }
11+
const Old = { template: '<div>old</div>', name: 'Old' }
12+
const New = { template: '<div>new</div>', name: 'New' }
13+
14+
// 3. Create the router
15+
const router = new VueRouter({
16+
mode: 'history',
17+
base: __dirname,
18+
routes: [
19+
{ path: '/', component: Home },
20+
{ path: '/test', component: Old }
21+
]
22+
})
23+
24+
// 4. Create and mount root instance.
25+
// Make sure to inject the router.
26+
// Route components will be rendered inside <router-view>.
27+
new Vue({
28+
router,
29+
data: () => ({ component: Old, message: null }),
30+
template: `
31+
<div id="app">
32+
<h1>Edit routes</h1>
33+
<ul>
34+
<li><router-link to="/">/</router-link></li>
35+
<li><router-link to="/test">/test</router-link></li>
36+
</ul>
37+
<button id="replace-btn" @click="replaceRoute">Switch /test</button>
38+
<button id="remove-btn" @click="removeRoute">Remove /test</button>
39+
<pre> Current component for <b>/test</b>: <span id="name">{{ component.name }}</span></pre>
40+
<pre style="color: red;" id="message"> {{message}} </pre>
41+
<router-view class="view"></router-view>
42+
</div>
43+
`,
44+
45+
methods: {
46+
replaceRoute () {
47+
try {
48+
const component = this.component === Old ? New : Old
49+
this.$router.replaceRoutes({
50+
path: '/test',
51+
component: this.component
52+
})
53+
this.component = component
54+
if (this.$route.path !== '/') {
55+
this.$router.replace('/')
56+
}
57+
} catch (e) {
58+
this.message = e.message
59+
}
60+
},
61+
removeRoute () {
62+
try {
63+
this.$router.removeRoute('/test')
64+
} catch (e) {
65+
this.message = e.message
66+
}
67+
}
68+
}
69+
}).$mount('#app')

examples/edit-routes/index.html

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<!DOCTYPE html>
2+
<link rel="stylesheet" href="/global.css">
3+
<a href="/">&larr; Examples index</a>
4+
<div id="app"></div>
5+
<script src="/__build__/shared.chunk.js"></script>
6+
<script src="/__build__/edit-routes.js"></script>

src/create-matcher.js

+15-1
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@ import { createRoute } from './util/route'
77
import { fillParams } from './util/params'
88
import { createRouteMap } from './create-route-map'
99
import { normalizeLocation } from './util/location'
10+
import { removeRouteMap } from './remove-route-map'
11+
import { replaceRouteMap } from './replace-route-map'
1012

1113
export type Matcher = {
1214
match: (raw: RawLocation, current?: Route, redirectedFrom?: Location) => Route;
1315
addRoutes: (routes: Array<RouteConfig>) => void;
16+
removeRoute: (location: string) => void;
17+
replaceRoutes: (routes: Array<RouteConfig>) => void;
1418
};
1519

1620
export function createMatcher (
@@ -23,6 +27,14 @@ export function createMatcher (
2327
createRouteMap(routes, pathList, pathMap, nameMap)
2428
}
2529

30+
function removeRoute (location) {
31+
removeRouteMap(location, pathList, pathMap, nameMap)
32+
}
33+
34+
function replaceRoutes (routes) {
35+
replaceRouteMap(routes, pathList, pathMap, nameMap)
36+
}
37+
2638
function match (
2739
raw: RawLocation,
2840
currentRoute?: Route,
@@ -166,7 +178,9 @@ export function createMatcher (
166178

167179
return {
168180
match,
169-
addRoutes
181+
addRoutes,
182+
removeRoute,
183+
replaceRoutes
170184
}
171185
}
172186

src/index.js

+14
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,20 @@ export default class VueRouter {
239239
this.history.transitionTo(this.history.getCurrentLocation())
240240
}
241241
}
242+
243+
removeRoute (location: string) {
244+
if (!location) {
245+
return
246+
}
247+
this.matcher.removeRoute(location)
248+
}
249+
250+
replaceRoutes (routes: Array<RouteConfig> | RouteConfig) {
251+
this.matcher.replaceRoutes(Array.isArray(routes) ? routes : [routes])
252+
if (this.history.current !== START) {
253+
this.history.transitionTo(this.history.getCurrentLocation())
254+
}
255+
}
242256
}
243257

244258
function registerHook (list: Array<any>, fn: Function): Function {

src/remove-route-map.js

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/* @flow */
2+
import { cleanPath } from './util/path'
3+
import { assert } from './util/warn'
4+
5+
export function removeRouteMap (
6+
location: string,
7+
oldPathList?: Array<string>,
8+
oldPathMap?: Dictionary<RouteRecord>,
9+
oldNameMap?: Dictionary<RouteRecord>
10+
): Dictionary<RouteRecord> {
11+
if (process.env.NODE_ENV !== 'production') {
12+
assert(oldPathList && oldPathMap, `route definition is empty`)
13+
}
14+
15+
// the path list is used to control path matching priority
16+
const pathList: Array<string> = oldPathList || []
17+
// $flow-disable-line
18+
const pathMap: Dictionary<RouteRecord> = oldPathMap || Object.create(null)
19+
// $flow-disable-line
20+
const nameMap: Dictionary<RouteRecord> = oldNameMap || Object.create(null)
21+
22+
const normalizedPath = normalizePath(location)
23+
24+
// remove routes which path and parent match normalizedPath
25+
// pending paths to remove
26+
const pathToRemove: Array<string> = [normalizedPath]
27+
28+
const removedRoutes = {}
29+
30+
while (pathToRemove.length) {
31+
const target = pathToRemove.pop()
32+
objIterator(pathMap, (path, route) => {
33+
const { name, parent } = route
34+
35+
// sub items
36+
if (parent && parent.path === target && pathToRemove.indexOf(path) === -1) {
37+
pathToRemove.push(path)
38+
}
39+
40+
if (path !== target) {
41+
// $flow-disable-line
42+
return
43+
}
44+
45+
removedRoutes[path] = route
46+
47+
// remove from pathList
48+
const idx = pathList.indexOf(path)
49+
if (idx !== -1) {
50+
pathList.splice(idx, 1)
51+
}
52+
53+
// remove from pathMap
54+
delete pathMap[path]
55+
56+
// remove from nameMap
57+
if (nameMap && name) {
58+
delete pathMap[name]
59+
}
60+
})
61+
}
62+
63+
if (process.env.NODE_ENV !== 'production') {
64+
assert(Object.keys(removedRoutes).length, `route path not found: ${location}`)
65+
}
66+
67+
return removedRoutes
68+
}
69+
70+
function objIterator (obj, handler) {
71+
for (const key in obj) {
72+
handler(key, obj[key])
73+
}
74+
}
75+
76+
function normalizePath (
77+
path: string,
78+
parent?: RouteRecord,
79+
strict?: boolean
80+
): string {
81+
if (!strict) path = path.replace(/\/$/, '')
82+
if (path[0] === '/') return path
83+
if (parent == null) return path
84+
return cleanPath(`${parent.path}/${path}`)
85+
}

0 commit comments

Comments
 (0)