一,前言
上篇,介绍了路由变化时视图更新的实现,主要涉及以下内容:
更新当前路由的匹配结果;
路由更新前的查重;
路由的响应式实现;
本篇,$route
、$router
与 router-link
的实现;
二,$route
和 $router
的实现
1,前文回顾
在《【VueRouter 源码学习】第二篇 - 路由的配置和使用》中介绍过:
通过 Vue.use 注册 Router 插件时,
将在 Vue 注册两个全局组件:router-link 和 router-view;
同时,为实例提供两个原型属性:$router 和 $route;
复制代码
2,定义原型方法 $route
在 Vue 原型上添加 $route 属性,属性值为当前匹配的路由 current 对象;
// install.js
/**
* 在 Vue 原型上添加 $route 属性 -> current 对象
* $route:包含了路由相关的属性
*/
Object.defineProperty(Vue.prototype, '$route', {
get() {
// this指向当前实例;所有实例上都可以拿到_routerRoot;
// 所以,this._routerRoot._route 就是根实例上的 _router
// 即:处理根实例时,定义的响应式数据 -> this.current 对象
return this._routerRoot._route; // 包含:path、matched等路由相关属性
}
})
复制代码
3,定义原型方法 $router
在 Vue 原型上添加 $router 属性,值为当前 router 实例;
// install.js
/**
* 在 Vue 原型上添加 $router 属性 -> router 实例
* $router:包含了路由相关的方法
*/
Object.defineProperty(Vue.prototype, '$router', {
get() {
// this._routerRoot._router 就是当前 router 实例;
// router 实例中,包含 matcher、push、go、repace 等方法;
return this._routerRoot._router;
}
});
复制代码
vue-router 官方文档:路由对象&路由对象属性
三,<router-link>
组件的实现
1,创建 Link 组件对象
在 vue-router 的组件目录下,创建 Link 组件对象,模拟返回 a 标签:
// vue-router/components/link.js
export default {
name:'routerLink',
render(){
return <a></a>
}
}
复制代码
在 install 阶段注册全局组件时,引入并使用 Link 组件对象:
// install.js
import Link from './components/link';
// 注册 Vue 全局组件
Vue.component('router-link', Link);
复制代码
2,<router-link>
组件的功能
每次点击<router-link>
组件时,都会进行 hash 值的切换;
<router-link>
组件可以接收来自外部的传参;
<router-link>
组件渲染后返回的内容包括:元素标签 + 点击跳转事件 + 插槽等;
3,<router-link>
组件的实现
// vue-router/components/link.js
export default {
// 组件名称
name: 'routerLink',
// 接收来自外部传入的属性
props: {
to: { // 目标路径
type: String,
required: true
},
tag: { // 标签名,默认 a
type: String,
default: 'a'
}
},
methods: {
handler(to) {
// 路由跳转:内部调用 history.push
this.$router.push(to);
}
},
render() {
let { tag, to } = this;
// JSX:标签 + 点击跳转事件 + 插槽
return <tag onClick={this.handler.bind(this, to)}>{this.$slots.default}</tag>
}
}
复制代码
4,this.$router.push
方法的实现
this.$router.push
:当前路由实例下的 push 方法:
index.js
class VueRouter {
push(to) {
this.history.push(to); // 子类对应的push实现
}
}
复制代码
备注:router 实例中的 history 为当前路由模式对应的子类实现,所以 push 方法也是子类对应的 push 实现;
5,this.history.push
方法的实现
实现 HashHistory 子类中的 push 方法:
// history/HashHistory.js
class HashHistory extends History {
push(location) {
// 跳转路径,并在跳转完成后更新 hash 值;
// transitionTo内部会查重:hash 值变化虽会再次跳转,但不会更新current属性;
this.transitionTo(location, () => {
window.location.hash = location;// 更新hash值
})
}
}
复制代码
hash 变化时,进行 transitionTo 跳转,transitionTo 的内部实现:
// history/HashHistory.js
/**
* 路由跳转方法:
* 每次跳转时都需要知道 from 和 to
* 响应式数据:当路径变化时,视图刷新
* @param {*}} location
* @param {*} onComplete
*/
transitionTo(location, onComplete) {
// 根据路径进行路由匹配;route :当前匹配结果
let route = this.router.match(location);
// 查重:如果前后两次路径相同,且路由匹配的结果也相同,那么本次无需进行任何操作
if (location == this.current.path && route.matched.length == this.current.matched.length) { // 防止重复跳转
return
}
// 使用当前路由route更新current,并执行其他回调
this.updateRoute(route);
onComplete && onComplete();
}
复制代码
根据当前路径进行一次路由匹配、更新前的查重、更新 current 为最新一次匹配到 route 对象,并执行window.location.hash = location
更新 hash;
6,视图的更新
这样,点击 <router-link>
组件后,将完成路径的切换,但页面没有渲染,需要<router-view>
组件的支持;
四,结尾
本篇,介绍了$route
、$router
与 router-link
组件的实现,主要涉及以下内容:
定义原型方法 $route
和 $router
;
<router-link>
组件的功能和实现;
下一篇,<router-view>
组件的实现;
评论