前言

通过上篇,我们知道前端理由的两种实现方法,Hash 路由与 History 路由,并且用它们分别实现了一个前端路由。

接下来我们就将 Vue 与 Hash 路由结合,实现一个非常简单的 vue-router 吧。

开始实现

想象一下,如果自己实现了一个 vue-router,会怎么去使用呢?参考 vue-router 官方的使用方式,看看 html 的使用:

<div id="app"> <p> <router-  to="#/">home</router- > <router-  to="#/book">book</router- > <router-  to="#/movie">movie</router- > </p> <router-view></router-view> </div> 复制代码

这里会有 router- router-view 两个组件需要我们来实现。再来看 js 的:

const Home = { template: '<div>home</div>' };const Book = { template: '<div>book</div>' };const Movie = { template: '<div>movie</div>' };const routes = [ { path: '/', component: Home }, { path: '/book', component: Book }, { path: '/movie', component: Movie }];const router = new VueRouter(Vue, { routes});new Vue({ el: '#app'});复制代码

这里会有我们自己定义的组件 Home、Book 和 Movie,并且有它们各自对应的路由。我们实现的 VueRouter 跟官方的有些区别,在 VueRouter 被 new 时是将 Vue 作为参数传入,而不是注入挂载到根实例下。

接下来就是 VueRouter 的实现了。

VueRouter

要怎么来实现 VueRouter 呢,先提供一下实现的思路:

  1. 绑定 hashchange 事件,实现前端路由;
  2. 将传入的路由和组件做一个路由映射,切换哪个路由即可找到对应的组件显示;
  3. 需要 new 一个 Vue 实例还做响应式通信,当路由改变的时候,router-view 会响应更新;
  4. 注册 router- router-view 组件。

先创建一个 VueRouter:

class VueRouter { constructor (Vue, options) { this.$options = options; }}复制代码

绑定事件

给 VueRouter 添加一个绑定事件的方法,一旦路由发生改变,会触发 onHashChange 方法。

constructor (Vue, options) { this.init();}// 绑定事件init () { window.addEventListener('load', this.onHashChange.bind(this), false); window.addEventListener('hashchange', this.onHashChange.bind(this), false);}复制代码

路由映射表

将传入的 options 设置成一张路由映射表,以便于通过路由查找到对应的组件。

constructor (Vue, options) { this.$options = options; this.routeMap = {}; this.createRouteMap(this.$options);}// 路由映射表createRouteMap (options) { options.routes.forEach(item => { this.routeMap[item.path] = item.component; });}复制代码

options 之中,路由与组件的关系:

const routes = [ { path: '/', component: Home }, { path: '/book', component: Book }, { path: '/movie', component: Movie }];复制代码

生成的路由映射表:

this.routeMap = { '/': Home, '/book': Book, '/movie': Movie};复制代码

响应

我们需要 new 一个新的 Vue 实例,将当前路由 current 储存在其 data 之中,当修改了 current 时,router-view 就会自己去更新视图。

constructor (Vue, options) { this.app = new Vue({ data: { current: '#/' } });}// 获取当前 hash 串getHash () { return window.location.hash.slice(1) || '/';}// 设置当前路径onHashChange () { this.app.current = this.getHash();}复制代码

只要在 router-view 里使用到了 this.app.current,一旦更新它,便会更新。

注册组件

router- 实际上就是一个 <a> 标签,点击它便能触发 hashchangerouter-view 会实现一个 render 方法,将当前路由对应的组件取出,进行渲染。

constructor (Vue, options) { this.initComponent(Vue);}// 注册组件initComponent (Vue) { Vue.component('router- ', { props: { to: String }, template: '<a :href="to"><slot></slot></a>' }); const _this = this; Vue.component('router-view', { render (h) { var component = _this.routeMap[_this.app.current]; return h(component); } });}复制代码

完整代码

至此,一个简单的 vue-router 就出来了,全部代码是这样的:

class VueRouter { constructor (Vue, options) { this.$options = options; this.routeMap = {}; this.app = new Vue({ data: { current: '#/' } }); this.init(); this.createRouteMap(this.$options); this.initComponent(Vue); } // 绑定事件 init () { window.addEventListener('load', this.onHashChange.bind(this), false); window.addEventListener('hashchange', this.onHashChange.bind(this), false); } // 路由映射表 createRouteMap (options) { options.routes.forEach(item => { this.routeMap[item.path] = item.component; }); } // 注册组件 initComponent (Vue) { Vue.component('router- ', { props: { to: String }, template: '<a :href="to"><slot></slot></a>' }); const _this = this; Vue.component('router-view', { render (h) { var component = _this.routeMap[_this.app.current]; return h(component); } }); } // 获取当前 hash 串 getHash () { return window.location.hash.slice(1) || '/'; } // 设置当前路径 onHashChange () { this.app.current = this.getHash(); }}复制代码

最后

将 Vue 与 Hash 路由结合,监听了 hashchange 事件,再通过 Vue 的 响应机制组件,便有了上面实现好了一个 vue-router。


原文发布时间:2018年06月29日

原文作者:cobish

本文来源掘金如需转载请紧急联系作者

收藏 打印