和尤雨溪一起进阶vue
1 初级版
借助vue的动态组件,可以实现一个简单的路由功能,如下
<script src="https:[email protected]/dist/vue.js"></script> <div id="app"> <a href="#foo">foo</a> <a href="#bar">bar</a> <component :is="url"></component> </div> <script> // 这是一个比较简单的解决方案,但是有一个问题,初始化的时候无法匹配 window.addEventListener('hashchange', () => { app.url = window.location.hash.slice(1) }); let app = new Vue({ el: '#app', data() { return { url: null } }, components: { foo: {template: `<div>foo-component</div>`}, bar: {template: `<div>bar-component</div>`} } }) </script>
2 改进版
解耦微改进一下,将路由提取到一个路由表中,
<script src="https:[email protected]/dist/vue.js"></script> <div id="app"> </div> <script> 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) }) let app = new Vue({ el:'#app', data() { return { url: window.location.hash.slice(1) } }, render(h) { return h('div', [ h('a', {attrs: {href: '#foo'}}, 'foo'), '|', h('a', {attrs: {href: '#bar'}}, 'bar'), h(routeTable[this.url] || NotFound), ]) } }) </script>
3 最终版
上面都是处理简单的url, 实际开发的时候,配置的路由都是多页面,多组件,路由的path也会比较长,如/a/b, /a/b/c, 还有动态路由,比如/a/:id,这个时候上面的方法就不能准确匹配了, 如果你是正则达人,你可以自己试试解析这些复杂的path,不是的话就使用别人封装好的第三方库吧,这里推荐path-to-regexp, 这个库的作用作者一句话就概述完了:
Turn a path string such as
/user/:nameinto a regular expression
大家可以点进去链接了解一下用法,这里我们介绍接下来要用的部分
const keys = [] const regexp = pathToRegexp('/foo/:id', keys) // regexp = /^\/foo\/((?:[^\/]+?))(?:\/(?=$))?$/i // keys = [{ delimiter: "/", name: "id", optional: false, partial: false, pattern: "[^\/]+?", prefix: "/", repeat: false}] // 得到了正则表达式regexp, 传入实际的url执行正则的exec方法 // 不匹配 const match1 = regexp.exec('/test/route'); // null const match3 = regexp.exec('/foo'); // null // 匹配 const match2 = regexp.exec('/foo/fooId'); // ["/foo/fooId", "fooId", index: 0, input: "/foo/fooId", groups: undefined] 复制代码
ok, 我们可以解析path了,来看看接下来如何实现
<script src="https:[email protected]/dist/vue.js"></script> // 这里是下载到本地同目录引入的 <script src='./path-to-regexp.js'></script> <div id="app"></div> <script> 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(path => { const dynamicSegments = [] const regex = pathToRegexp(path, dynamicSegments) const component = routeTable[path] compiledRoutes.push({ component, regex, dynamicSegments }) }) window.addEventListener('hashchange', () => { app.url = window.location.hash.slice(1); }) const app = new Vue({ el: '#app', data() { return { url: window.location.hash.slice(1) } }, // 渲染那个路由,路由属性 render(h) { const url = '/' + this.url let componentToRender let props = {} compiledRoutes.some(route => { const match = route.regex.exec(url) if (match) { componentToRender = route.component // 上一步已经可以匹配到url对应的组件了 // 这里多做一步,获取动态id作为props的属性传入组件 route.dynamicSegments.forEach((segment,index) => { props[segment.name] = match[index+1] }) } }) return h('div', [ h('a', { attrs: { href: '#foo/123' } }, 'foo123'), '|', h('a', { attrs: { href: '#foo/234' } }, 'foo234'), '|', h('a', { attrs: { href: '#bar' } }, 'bar'), h(componentToRender || NotFound, { props }) ]) } }) </script>
编辑:--史志成