前言
先随便聊聊 vue-router 中的一些知识点,一些考点。
-
router 初始化以及配置,就不过多赘述,但自己要牢记于心。
-
前几天和大家提到的 路由守卫, 比如router.brforeEach 等一些常用的api。
-
路由懒加载。
css
{
path: '/about',
component: () => import('./views/About.vue')
}
进行路由匹配的时候,这样使用 import 函数引入,只有匹配到才会进行加载,可以更快的看到首页。所以首页就需要直接在外边 import 关键字导入。
- router-link router-view 。点击 router-link 后,可以跳到其他页面,好像和 a 标签一样。但是它可比 a 强多了,是个组件。router-view 也是个组件,根据相应的路由地址,把对应的组件显示到自己所在的位置。
它们两个是全局组件,但不是 vue 自带的,是需要在 main.js 中 .use 一下才可以使用。这个 .use 到底是怎么回事?今天就来一起看看。
基本准备
1. 项目搭建
xml
<!-- about页面 -->
<template>
<div>
about
</div>
</template>
<!-- home页面 -->
<template>
<div>
home
</div>
</template>
创建页面级别的组件 Home.vue 以及 About.vue,写入这样一个简单的内容。
接着就是基本的创建 router文件夹下的 index.js 来配置这两个组件的路由,About 记得使用懒加载,这里不写出来了。
xml
<template>
<header>
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</nav>
</header>
<main>
<router-view></router-view>
</main>
</template>
最后在 App.vue 中用起来就行。
2. 了解 vue 组件
思考 router-link 组件是如何工作的呢?像 router -link 这样不需要引入的组件称为 全局组件 ,注册后可在任何地方直接访问。除了这个还有我们最熟悉的 自定义组件 ,就是我们一般会写在 components 文件夹下的一些组件,需要什么,写一个就行,但是需要显式引入并在组件中注册。最后还有像 transition 这样的内置组件,是 vue 自己提供的。
在 vue 中,使用什么东西来让组件变成为全局组件呢?这是组件的一种声明方式,使用 app.component('router-link', RouterLink)
当我们把 app.use(router) 注释掉后,发现没有了 router-link这个组件了,但是却还是可以看得到 router-link 中的 Home 以及 About 。这又是为什么呢?
当一个组件没有引入,不被支持的时候, dom 会把它当成一般标签来解析。
所以我们要搞明白 .use 到底做了什么?
vue 只负责 组件思想, mvvm 响应式 等核心,其他的交给生态系统,一起开源。vue-router 是vue 生态系统中的路由模块, vue 和它生态的对接,就是这个use方法
手写 router
javascript
import { createRouter } from './grouter/index'
现在我们就自己来写 router ,理解 router 。将原来的引入改为 grouter 文件夹下的 idnex ,在这来实现 router 中的 api 。
创建 RouterLink 和 RouterView 这俩个 vue 文件。
1. RouterLink.vue
xml
<!-- RouterLink.vue -->
<template>
<a :href="'#' + $props.to">
<!-- 插槽 -->
<slot />
</a>
</template>
<script setup>
const props = defineProps({
to: {
type: String,
required: true
}
})
</script>
因为 router-link 本来渲染到页面也是 a 标签,所以使用 a
元素来创建一个超链接。href
属性通过 :
简写绑定动态值,它将一个井号 #
和 to
的值进行拼接,这样创建一个动态的链接。在单页应用中,当用户点击链接时,不会触发页面刷新,而是通过 JavaScript 更改 URL 并显示新的内容。
在子组件模板中,<slot>
标签表示了一个占位符,任何在子组件标签内部定义的内容都将被渲染到这个占位符的位置。这使得父组件可以根据需要自定义链接的显示内容,而无需修改子组件的代码。
2. RouterView.vue
xml
<!-- RouterView.vue -->
<template>
<component :is="component"></component>
</template>
<script setup>
import { computed } from 'vue'
import { useRouter } from './index.js';
const router = useRouter()
// console.log(router);
// router-view 动态组件 展示 依赖于 url 的变化
const component = computed(() => {
const route = router.routes.find(
(route) => route.path == router.current.value
)
return router? route.component : null
})
</script>
使用 <component>
标签,这是一个特殊的 Vue 标签,允许我们根据传入的 is
属性动态地渲染不同的组件。is
属性会返回当前应该渲染的组件。计算属性根据当前 URL 路径来决定渲染哪个组件。通过查找与当前 URL 路径匹配的路由配置,返回找到的路由配置中指定的组件。
RouterView.vue
负责根据 URL 渲染正确的页面。<a>
链接组件提供了一种简单的方式来创建导航链接。结合这两个组件,我们就可以构建一个基本的手写路由系统,它允许用户通过点击链接来导航到不同的页面,并且页面会根据 URL 路径的变化动态更新。
3. index.js
javascript
<!-- index.js -->
import RouterLink from './RouterLink.vue'
import RouterView from './RouterView.vue'
import { ref, inject } from 'vue'
// 单例的责任
export const createRouter = (options) => {
return new Router(options)
}
export const createWebHashHistory = () => {
function bindEvents(fn) {
window.addEventListener('hashchange', fn)
}
// history 对象
return {
url: window.location.hash.slice(1) || '/',
bindEvents
}
}
// 标记一下,router 要向全局暴露
const ROUTER_KEY = '__router__'
// use 开头的是一派 hooks 函数式编程
export const useRouter = () => {
return inject(ROUTER_KEY)
}
class Router {
constructor(options) {
this.history = options.history
this.routes = options.routes
this.current = ref(this.history.url)
this.history.bindEvents(() => {
// console.log('//////////')
this.current.value = window.location.hash.slice(1) || '/'
})
}
// use 调用 插件install
install(app) {
// 全局声明一个router 全局使用的对象
app.provide(ROUTER_KEY, this)
console.log('准备与vue 对接', app)
app.component('router-link', RouterLink)
app.component('router-view', RouterView)
}
}
createWebHashHistory
函数创建一个哈希历史模式的路由历史管理器,它负责监听浏览器的 hashchange
事件并获取当前 URL 的哈希值。
ROUTER_KEY
是一个符号,用于在 Vue 应用程序上下文中标识路由器实例。这在 Vue 组件之间共享路由器实例,以便各个组件可以访问到相同的路由信息和相关的方法。确保整个应用中的所有组件都可以访问同一个路由器实例,而不需要在每个组件中显式传递路由器。
useRouter
函数利用 inject
函数从 Vue 组件的上下文中获取路由器实例。
Router
类是一个构造函数,用于创建路由管理器实例。constructor
方法接收 history
和 routes
作为参数,并初始化路由状态。install
方法用于将路由器实例注入到 Vue 应用程序中,并注册 RouterLink
和 RouterView
组件。
思路巩固
讲了这么些,最后总的来看看 vue-router 到底做了些什么事情。
1. 创建路由组件
RouterLink 负责渲染带有特定 href
的 <a>
标签,其中 href
包含当前路由路径。用户点击时,会改变浏览器 URL 的 hash 部分,触发页面内的导航。
RouterView 负责根据当前 URL 路径动态渲染与之对应的组件。它通过计算属性确定展示哪个组件。
2. 设置路由逻辑
createWebHashHistory 创建一个简单的哈希历史管理器,用于监听 hashchange
事件,并获取当前 URL 的哈希部分作为路由路径。
Router 类 负责管理路由状态,包括当前路径、注册的路由规则等。它还包含一个 install
方法,用于将路由器实例注入到 Vue 应用程序中,并注册全局组件 RouterLink
和 RouterView
。
3. 集成到 Vue 应用
使用 createRouter
工厂函数创建路由器实例,并通过 app.use
方法安装到 Vue 应用中。这一步会将路由器实例注册到 Vue 的上下文中,并注册全局组件。
总结
我们首先回顾了一些关于 Vue Router 的基础知识,例如初始化配置、路由守卫、路由懒加载、router-link
和 router-view
的使用等。接着详细介绍了如何手动实现一个简化版的 Vue 路由器。手写一个简易版的 Vue Router 不仅有助于深入理解 Vue Router 的设计原理,还能加深对 Vue 框架的理解。 看完希望对你有帮助,一起加油。