github
Vue-router
是Vue.js
官方的路由管理器。它和Vue.js
的核心深度集成,让构建单页面应用变得易如反掌。javascript
vue add router
步骤一:使用vue-router
插件vue
//router.js import Router from 'vue-router';
*/
Vue.use(Router); // 引入插件java
步骤二:建立Router实例git
// router.js export default new Router({...}) // 导出Router实例
步骤三:在根组件添加该实例github
// main.js import router from './router'; new Vue({ router // 添加到配置项 }).$mount("#app")
步骤四:添加路由视图vue-router
<!-- App.vue --> <router-view></router-view>
步骤五:导航api
<router-link to="/">Home</router-link> <router-link to="/about">About</router-link>
this.$router.push('/'); this.$router.push('/about')
单页面应用程序中,url
发生变化时候,不能刷新,显示对应视图app
#/about
/about
根据url
显示对应的内容函数
router-view
current
变量持有url
地址,一旦变化,动态执行render
实现一个插件优化
VueRouter
类url
变化install
方法$router
注册在Vue2.x
项目中的src
路径下,复制一份router
文件,重命名为ou-router
。
而后在ou-router
路径下新建一个ou-vue-router.js
文件,并将index.js
文件中的VueRouter
引入改成ou-vue-router.js
。
import VueRouter from './ou-vue-router'
同时将main.js
中的router
引入也修改一下。
import router from './ou-router'
关于Vue插件的建立:
function
实现,也可使用object
或class
实现;要求必须有一个install
方法,未来会被Vue.use()
使用
let Vue; // 保存Vue的构造函数,插件中须要用到 class VueRouter {}
*/
VueRouter.install = function (_Vue) {
Vue = _Vue; // 引用构造函数,VueRouter中要使用
}
export default VueRouter;
### 挂载`$router` 当咱们发现`vue-router`引入`vue`的时候,第一次是在`router/index.js`中使用了`Vue.use(Router)`,在这个时候也就会调用了`vue-router`的`install`方法;而第二次则是在`main.js`中,建立根组件实例的时候引入`router`,即`new Vue({router}).$mount("#app")`。 也就是说,当调用`vue-router`的`install`方法的时候,项目尚未建立`Vue`的根组件实例。所以咱们须要在`vue-router`的`install`方法使用全局混入,延迟到`router`建立完毕才执行挂载`$router`。
let Vue; // 保存Vue的构造函数,插件中须要用到
class VueRouter {}
/*
*/
VueRouter.install = function (_Vue) {
Vue = _Vue; // 引用构造函数,VueRouter中要使用 /* 挂载$router */ /* * 全局混入 * 全局混入的目的是为了延迟下面逻辑到router建立完毕而且附加到选项上时才执行 * */ Vue.mixin({ beforeCreate() { // 此钩子在每一个组件建立实例时都会调用 /* this.$options即建立Vue实例的第一个参数 */ if(this.$options.router){ // 只在根组件拥有router选项
Vue.prototype.$router = this.$options.router; // vm.$router
} } })
}
export default VueRouter;
### 注册全局组件`router-link`和`router-view` 首先在`install`方法中注册两个全局变量。
let Vue;
class VueRouter {}
VueRouter.install = function (_Vue) {
Vue = _Vue; Vue.mixin({ ... }) /* 注册全局组件router-link和router-view */ Vue.component('router-link',{ render(createElement){ return createElement('a','router-link'); // 返回虚拟Dom } }); Vue.component('router-view',{ render(createElement){ return createElement('div','router-view'); // 返回虚拟Dom } })
}
export default VueRouter;
router-view
是一个a
标签router-view
的to
属性设置到a
标签的herf
属性(先默认使用hash
方法)获取router-view
的插槽内容,插入a
标签中
Vue.component('router-link', { props: { to: { type: String, required: true } }, render(createElement) { // 返回虚拟Dom return createElement('a', { attrs: {href: '#' + this.to} // 设置a标签的href属性 }, this.$slots.default // 获取标签插槽内容 ); } });
router-view
router-view
实质上根据url
的变化,实时响应渲染对应的组件,而createElement
函数是能够传入一个组件参数的。
所以,咱们不进行渲染任何内容,后面实现监听url
变化后,从映射表获取到组件后,再来实现router-view
。
Vue.component('router-view', { render(createElement) { let component = null; return createElement(component); // 返回虚拟Dom } })
url
变化咱们在VueRouter
类的constructor
函数中监听url
的变化,这里咱们默认使用hash
方式。
并且,咱们须要将存入url
的变量设置为响应式数据,这样子当其发生变化的时候,router-view
的render
函数才可以再次执行。
class VueRouter { /* * options: * mode: 'hash' * base: process.env.BASE_URL * routes * */ constructor(options) { this.$options = options; // 将current设置为响应式数据,即current变化时router-view的render函数可以再次执行 const initial = window.location.hash.slice(1) || '/'; Vue.util.defineReactive(this, 'current',initial); // 监听hash变化 window.addEventListener('hashchange', () => { this.current = window.location.hash.slice(1); }) } }
所以,咱们能够来实现router-view
组件。
在render
函数中,this.$router
指向的是VueRouter
建立的实例,所以咱们能够经过this.$router.$option.routes
获取路由映射表,this.$router.current
获取当前路由,而后经过遍历匹配获取组件。
Vue.component('router-view', { render(createElement) { let component = null; // 获取当前路由对应的组件 const route = this.$router.$options.routes .find(route => route.path === this.$router.current); if (route) { component = route.component; } return createElement(component); // 返回虚拟Dom } })
history
模式前面的实现都默认为hash
模式,接下来简单实现一下history
模式。
首先将监听url
的代码优化一下,并判别mode
的值来设置current
的初始值,而history
模式下初始值为window.location.pathname
。
class VueRouter { /* * options: * mode: 'hash' * base: process.env.BASE_URL * routes * */ constructor(options) { this.$options = options; switch (options.mode) { case 'hash': this.hashModeHandle(); break; case 'history': this.historyModeHandle(); } } // Hash模式处理 hashModeHandle() { // 将current设置为响应式数据,即current变化时router-view的render函数可以再次执行 const initial = window.location.hash.slice(1) || '/'; Vue.util.defineReactive(this, 'current', initial); // 监听hash变化 window.addEventListener('hashchange', () => { this.current = window.location.hash.slice(1); }) } // History模式处理 historyModeHandle() { const initial = window.location.pathname || '/'; Vue.util.defineReactive(this, 'current', initial); } }
而后咱们来实现history
模式下的router-link
组件。
在history
模式下,当咱们点击router-link
时,即点下a
标签时,页面会从新刷新。因此咱们须要设置一下其点击事件,取消默认事件,而后经过history.pushState
去修改url
,而后重设current
的值。
Vue.component('router-link', { render(createElement) { // 返回虚拟Dom const self = this; const route = this.$router.$options.routes .find(route => route.path === this.to); return createElement('a', { attrs: {href: this.to}, // 设置a标签的href属性 on: { click(e) { e.preventDefault(); // 取消a标签的默认事件,即刷新页面 history.pushState({}, route.name, self.to); // 经过history.pushState来改变url self.$router.current = self.to; } } }, this.$slots.default // 获取标签插槽内容 ); } })
最后咱们将两种模式的router-link
组件进行一个合并。
Vue.component('router-link', { props: { to: { type: String, required: true } }, render(createElement) { // 返回虚拟Dom if(this.$router.$options.mode === 'hash'){ return createElement('a', { attrs: {href: '#' + this.to} // 设置a标签的href属性 }, this.$slots.default // 获取标签插槽内容 ); }else{ const self = this; const route = this.$router.$options.routes .find(route => route.path === this.to); return createElement('a', { attrs: {href: this.to}, // 设置a标签的href属性 on: { click(e) { e.preventDefault(); // 取消a标签的默认事件,即刷新页面 history.pushState({}, route.name, self.to); // 经过history.pushState来改变url self.$router.current = self.to; } } }, this.$slots.default // 获取标签插槽内容 ); } } });