一,前言
上篇,介绍了路由变化时视图更新的实现,主要涉及以下内容:
更新当前路由的匹配结果;
路由更新前的查重;
路由的响应式实现;
本篇,$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>组件的实现;
评论