【Vue.js 3.0源码】Vue Router:如何实现一个前端路由?

自我介绍:大家好,我是吉帅振的网络日志(其他平台账号名字相同),互联网前端开发工程师,工作5年,去过上海和北京,经历创业公司,加入过阿里本地生活团队,现在郑州北游教育从事编程培训。

一、前言

相信对有一定基础的前端开发工程师来说,路由并不陌生,它最初源于服务端,在服务端中路由描述的是 URL 与处理函数之间的映射关系。而在 Web 前端单页应用 SPA 中,路由描述的是 URL 与视图之间的映射关系,这种映射是单向的,即 URL 变化会引起视图的更新。相比于后端路由,前端路由的好处是无须刷新页面,减轻了服务器的压力,提升了用户体验。目前主流支持单页应用的前端框架,基本都有配套的或第三方的路由系统。相应的,Vue.js 也提供了官方前端路由实现 Vue Router,那么这节课我们就来学习它的实现原理。

二、路由的基本用法

<div id="app">
  <h1>Hello App!</h1>
  <p>
    <router-link to="/">Go to Home</router-link>
    <router-link to="/about">Go to About</router-link>
  </p>
  <router-view></router-view>
</div>

RouterLink 表示路由的导航组件,我们可以配置 to 属性来指定它跳转的链接,它最终会在页面上渲染生成 a 标签。RouterView 表示路由的视图组件,它会渲染路径对应的 Vue 组件,也支持嵌套。

import { createApp } from 'vue'
import { createRouter, createWebHashHistory } from 'vue-router'
// 1. 定义路由组件
const Home = { template: '<div>Home</div>' }
const About = { template: '<div>About</div>' }
// 2. 定义路由配置,每个路径映射一个路由视图组件
const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
]
// 3. 创建路由实例,可以指定路由模式,传入路由配置对象
const router = createRouter({
  history: createWebHistory(),
  routes
})
// 4. 创建 app 实例
const app = createApp({
})
// 5. 在挂载页面 之前先安装路由
app.use(router)
// 6. 挂载页面
app.mount('#app')

首先需要定义一个路由配置,这个配置主要用于描述路径和组件的映射关系,即什么路径下 RouterView 应该渲染什么路由组件。接着创建路由对象实例,传入路由配置对象,并且也可以指定路由模式,Vue Router 目前支持三种模式,hash 模式,HTML5 模式和 memory 模式,我们常用的是前两种模式。最后在挂载页面前,我们需要安装路由,这样我们就可以在各个组件中访问路由对象以及使用路由的内置组件 RouterLink 和 RouterView 了。

三、路由对象的创建

function createRouter(options) {
  // 定义一些辅助方法和变量 
  
  // ...
  
  // 创建 router 对象
  const router = {
    // 当前路径
    currentRoute,
    addRoute,
    removeRoute,
    hasRoute,
    getRoutes,
    resolve,
    options,
    push,
    replace,
    go,
    back: () => go(-1),
    forward: () => go(1),
    beforeEach: beforeGuards.add,
    beforeResolve: beforeResolveGuards.add,
    afterEach: afterGuards.add,
    onError: errorHandlers.add,
    isReady,
    install(app) {
      // 安装路由函数
    }
  }
  return router
}

我们省略了大部分代码,只保留了路由对象相关的代码,可以看到路由对象 router 就是一个对象,它维护了当前路径 currentRoute,且拥有很多辅助方法。

四、路由的安装

Vue Router 作为 Vue 的插件,当我们执行 app.use(router) 的时候,实际上就是在执行 router 的 install 方法来安装路由,并把 app 作为参数传入,来看它的定义:

const router = {
  install(app) {
    const router = this
    // 注册路由组件
    app.component('RouterLink', RouterLink)
    app.component('RouterView', RouterView)
    // 全局配置定义 $router 和 $route
    app.config.globalProperties.$router = router
    Object.defineProperty(app.config.globalProperties, '$route', {
      get: () => unref(currentRoute),
    })
    // 在浏览器端初始化导航
    if (isBrowser &&
      !started &&
      currentRoute.value === START_LOCATION_NORMALIZED) {
      // see above
      started = true
      push(routerHistory.location).catch(err => {
        warn('Unexpected error when starting the router:', err)
      })
    }
    // 路径变成响应式
    const reactiveRoute = {}
    for (let key in START_LOCATION_NORMALIZED) {
      reactiveRoute[key] = computed(() => currentRoute.value[key])
    }
    // 全局注入 router 和 reactiveRoute
    app.provide(routerKey, router)
    app.provide(routeLocationKey, reactive(reactiveRoute))
    let unmountApp = app.unmount
    installedApps.add(app)
    // 应用卸载的时候,需要做一些路由清理工作
    app.unmount = function () {
      installedApps.delete(app)
      if (installedApps.size < 1) {
        removeHistoryListener()
        currentRoute.value = START_LOCATION_NORMALIZED
        started = false
        ready = false
      }
      unmountApp.call(this, arguments)
    }
  }
}

全局注册 RouterView 和 RouterLink 组件——这是你安装了路由后,可以在任何组件中去使用这俩个组件的原因,如果你使用 RouterView 或者 RouterLink 的时候收到提示不能解析 router-link 和 router-view,这说明你压根就没有安装路由。通过 provide 方式全局注入 router 对象和 reactiveRoute 对象,其中 router 表示用户通过 createRouter 创建的路由对象,我们可以通过它去动态操作路由,reactiveRoute 表示响应式的路径对象,它维护着路径的相关信息。

五、路径的管理

const START_LOCATION_NORMALIZED = {
  path: '/',
  name: undefined,
  params: {},
  query: {},
  hash: '',
  fullPath: '/',
  matched: [],
  meta: {},
  redirectedFrom: undefined
}

路由想要发生变化,就是通过改变路径完成的,路由对象提供了很多改变路径的方法,比如 router.push、router.replace,它们的底层最终都是通过 pushWithRedirect 完成路径的切换,我们来看一下它的实现:

function pushWithRedirect(to, redirectedFrom) {
  const targetLocation = (pendingLocation = resolve(to))
  const from = currentRoute.value
  const data = to.state
  const force = to.force
  const replace = to.replace === true
  const toLocation = targetLocation
  toLocation.redirectedFrom = redirectedFrom
  let failure
  if (!force && isSameRouteLocation(stringifyQuery$1, from, targetLocation)) {
    failure = createRouterError(16 /* NAVIGATION_DUPLICATED */, { to: toLocation, from })
    handleScroll(from, from, true, false)
  }
  return (failure ? Promise.resolve(failure) : navigate(toLocation, from))
    .catch((error) => {
      if (isNavigationFailure(error, 4 /* NAVIGATION_ABORTED */ |
        8 /* NAVIGATION_CANCELLED */ |
        2 /* NAVIGATION_GUARD_REDIRECT */)) {
        return error
      }
      return triggerError(error)
    })
    .then((failure) => {
      if (failure) {
        // 处理错误
      }
      else {
        failure = finalizeNavigation(toLocation, from, true, replace, data)
      }
      triggerAfterEach(toLocation, from, failure)
      return failure
    })
}

我省略了一部分代码的实现,这里主要来看 pushWithRedirect 的核心思路,首先参数 to 可能有多种情况,可以是一个表示路径的字符串,也可以是一个路径对象,所以要先经过一层 resolve 返回一个新的路径对象,它比前面提到的路径对象多了一个 matched 属性,它的作用我们后续会介绍。得到新的目标路径后&#x

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue.js源码全方位深入解析最新下载地址.rar Vue.js源码全方位深入,帮你更深入了解vue 第1章 准备工作 介绍了 Flow、Vue.js源码目录设计、Vue.js源码构建方式,以及从入口开始分析了 Vue.js 的初始化过程。 第2章 数据驱动 详细讲解了模板数据到 DOM 渲染的过程,从 new Vue 开始,分析了 mount、render、update、patch 等流程。 第3章 组件化 分析了组件化的实现原理,并且分析了组件周边的原理实现,包括合并配置、生命周期、组件注册、异步组件。 第4章 深入响应式原理(上) 详细讲解了数据的变化如何驱动视图的变化,分析了响应式对象的创建,依赖收 集、派发更新的实现过程,一些特殊情况的处理,并对比了计算属性和侦听属性的实现,最后分析了组件更新的过程。 第5章 深入响应式原理(下) 详细讲解了数据的变化如何驱动视图的变化,分析了响应式对象的创建,依赖收集、派发更新的实现过程,一些特殊情况的处理,并对比了计算属性和侦听属性的实现,最后分析了组件更新的过程。 第6章 -编译(上) 从编译的入口函数开始,分析了编译的三个核心流程的实现:parse -> optimize -> codegen。 第7章 -编译(下) 从编译的入口函数开始,分析了编译的三个核心流程的实现:parse -> optimize -> codegen。 第8章 -扩展(上) 详细讲解了 event、v-model、slot、keep-alive、transition、transition-group 等常用功能的原理实现,该章节作为一个可扩展章节,未来会分析更多 Vue 提供的特性。 第9章 -扩展(中) 详细讲解了 event、v-model、slot、keep-alive、transition、transition-group 等常用功能的原理实现,该章节作为一个可扩展章节,未来会分析更多 Vue 提供的特性。 第10章 -扩展(下) 详细讲解了 event、v-model、slot、keep-alive、transition、transition-group 等常用功能的原理实现,该章节作为一个可扩展章节,未来会分析更多 Vue 提供的特性。 第11章 Vue-Router 分析了 Vue-Router实现原理,从路由注册开始,分析了路由对象、matcher,并深入分析了整个路径切换的实现过程和细节。 第12章 Vuex 分析了 Vuex 的实现原理,深入分析了它的初始化过程,常用 API 以及插件部分的实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值