github
vue-router Vue-router
是Vue.js
官网的路由管理器。
它和Vue.js
的外围深度集成,让构建单页面利用变得大海捞针。
装置
vue add router
外围步骤
步骤一:应用
vue-router
插件//router.jsimport Router from 'vue-router';
- VueRouter是一个插件
- 1)实现并申明两个组件router-view router-link
- 2)install: this.$router.push()
*/
Vue.use(Router); // 引入插件步骤二:创立Router实例
// router.jsexport default new Router({...}) // 导出Router实例
步骤三:在根组件增加该实例
// main.jsimport router from './router';new Vue({ router // 增加到配置项}).$mount("#app")
步骤四:增加路由视图
<!-- App.vue --><router-view></router-view>
步骤五:导航
<router-link to="/">Home</router-link><router-link to="/about">About</router-link>
this.$router.push('/');this.$router.push('/about')
vue-router简略实现
需要剖析
单页面应用程序中,
url
发生变化时候,不能刷新,显示对应视图- hash:
#/about
- History api:
/about
- hash:
依据
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插件
对于Vue插件的创立:
- 实现
- 能够应用
function
实现,也能够应用object
或class
实现; 要求必须有一个
install
办法,未来会被Vue.use()
应用let Vue; // 保留Vue的构造函数,插件中须要用到class VueRouter {}
- 插件:实现install办法,注册$router
- 参数1是Vue.use()肯定会传入
*/
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 {}
/*- 插件:实现install办法,注册$router
- 参数1是Vue.use()肯定会传入
*/
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 // 获取标签插槽内容 ); } }});