写点什么

【VueRouter 源码学习】第八篇 - $route、$router 与 router-link 组件的实现

用户头像
Brave
关注
发布于: 6 小时前
【VueRouter 源码学习】第八篇 - $route、$router 与 router-link 组件的实现

一,前言


上篇,介绍了路由变化时视图更新的实现,主要涉及以下内容:


  • 更新当前路由的匹配结果;

  • 路由更新前的查重;

  • 路由的响应式实现;


本篇,$route$routerrouter-link 的实现;


二,$route$router 的实现

1,前文回顾


在《【VueRouter 源码学习】第二篇 - 路由的配置和使用》中介绍过:

通过 Vue.use 注册 Router 插件时,将在 Vue 注册两个全局组件:router-link 和 router-view;同时,为实例提供两个原型属性:$router 和 $route;
复制代码


  • $route:包含了路由相关的属性;如:name、hash、meta 等;

  • $router:包含了路由相关的方法;如:history API(push、replace、go);

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>组件的实现


  • 接收外部传参 props;包含:目标路径 to 与标签名 tag;

  • 跳转方法:handler;当前路由实例下的 push 方法;

  • 渲染函数 render 方法,jsx 模板;

// 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$routerrouter-link 组件的实现,主要涉及以下内容:


  • 定义原型方法 $route$router

  • <router-link>组件的功能和实现;


下一篇,<router-view>组件的实现;

用户头像

Brave

关注

还未添加个人签名 2018.12.13 加入

还未添加个人简介

评论

发布
暂无评论
【VueRouter 源码学习】第八篇 - $route、$router 与 router-link 组件的实现