github
vue-router Vue-router
是 Vue.js
官网的路由管理器。
它和 Vue.js
的外围深度集成,让构建单页面利用变得大海捞针。
装置
vue add router
外围步骤
-
步骤一:应用
vue-router
插件//router.js import Router from 'vue-router';
- VueRouter 是一个插件
- 1)实现并申明两个组件 router-view router-link
- 2)install: this.$router.push()
-
*/
Vue.use(Router); // 引入插件 -
步骤二:创立 Router 实例
// router.js export default new Router({...}) // 导出 Router 实例
-
步骤三:在根组件增加该实例
// main.js import 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 // 获取标签插槽内容 ); } } });