『深入 VUE』路由

极简路由实现

需求:

  • 当 url 为 #foo 时显示 foo
  • 当 url 为 #bar 时显示 bar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="app">
<component :is="url"></component>
<a href="#foo">foo</a>
<a href="#bar">bar</a>
</div>

<script>
window.addEventListener('hashchange', () => {
app.url = window.location.hash.slice(1)
})

const app = new Vue({
el: '#app',
data: {
url: window.location.hash.slice(1)
},
components: {
foo: { template: `<div>foo</div>` },
bar: { template: `<div>bar</div>` }
}
})
</script>

实现一个路由表

页面展示路由表中对应的组件,若都不匹配则展示 NotFound

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<div id="app">
<component :is="matchedComponent"></component>
<a href="#foo">foo</a>
<a href="#bar">bar</a>
</div>

<script>
// '#/foo' -> Foo
// '#/bar' -> Bar
const Foo = { template: `<div>foo</div>` }
const Bar = { template: `<div>bar</div>` }
const NotFound = { template: `<div>not found!</div>` }

const routeTable = {
'foo': Foo,
'bar': Bar
}

window.addEventListener('hashchange', () => {
app.url = window.location.hash.slice(1)
})

const app = new Vue({
el: '#app',
data: {
url: 'foo'
},
computed: {
matchedComponent () {
return routeTable[this.url] || NotFound
}
}
})
</script>

实现匹配正则规则的路由

实现正则匹配,达到路由传参的目的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<script>
// '#/foo/123' -> foo with id: 123
// '#/bar' -> Bar
// '#/404' -> NotFound

// path-to-regexp usage:
// const regex = pathToRegexp(pattern)
// const match = regex.exec(path)

const Foo = {
props: ['id'],
template: `<div>foo with id: {{ id }}</div>`
}
const Bar = { template: `<div>bar</div>` }
const NotFound = { template: `<div>not found!</div>` }

const routeTable = {
'/foo/:id': Foo,
'/bar': Bar
}

const compiledRoutes = []
Object.keys(routeTable).forEach(key => {
const dynamicSegments = []
const regex = pathToRegexp(key, dynamicSegments)
const component = routeTable[key]
compiledRoutes.push({
component,
regex,
dynamicSegments
})
})

window.addEventListener('hashchange', () => {
app.url = window.location.hash.slice(1)
})

const app = new Vue({
el: '#app',
data: {
url: window.location.hash.slice(1)
},
render (h) {
const path = '/' + this.url

let componentToRender
let props = {}

compiledRoutes.some(route => {
const match = route.regex.exec(path)
componentToRender = NotFound
if (match) {
componentToRender = route.component
route.dynamicSegments.forEach((segment, index) => {
props[segment.name] = match[index + 1]
})
return true
}
})

return h('div', [
h(componentToRender, { props }),
h('a', { attrs: { href: '#foo/123' }}, 'foo 123'),
' | ',
h('a', { attrs: { href: '#foo/234' }}, 'foo 234'),
' | ',
h('a', { attrs: { href: '#bar' }}, 'bar'),
' | ',
h('a', { attrs: { href: '#garbage' }}, 'garbage')
])
}
})
</script>