vue3 - vue -router(一)

183 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第24天,点击查看活动详情

前端路由本质上维护了一个映射表,在该表中记录着不同的url地址对应页面所需要渲染的组件

在这个渲染流程中,我们只需要在第一次渲染的时候,向服务器请求静态资源的时候,将index.html下载下来

之后只要通过ajax去修改界面中组件即可,所以这种应用也被称之为SPA应用

路由模式

前端路由的核心是 当URL发生改变的时候,不进行页面的整体刷新,而是只改变页面的部分内容

目前常见的模式有两种: hash模式history模式

hash模式

hash模式是通过监听hashChange事件来实现的

URL的hash也就是锚点(#), 本质上是改变window.location的href属性

我们可以通过直接赋值location.hash来改变href, 但是页面不发生刷新

hash的优势就是兼容性更好,在老版IE中都可以运行,但是缺陷是有一个#,显得不像一个真实的路径

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>URL的hash</title>
</head>
<body>
  <div id="app">
    <!-- 路由 -->
    <a href="#/home">Home</a>
    <a href="#/about">About</a>

    <!-- 占位符 -->
    <div id="content">Home</div>
  </div>

  <script>
    const contentElem = document.getElementById('content')

    // 监听路由改变函数 --- 路由改变的时候,页面是不会刷新的
    window.addEventListener('hashchange', () => {
      // location.hash --- 可以获取到hash值,获取到的值是带#的
      switch(location.hash) {
        case '#/home':
          contentElem.innerHTML = 'Home'
          break

        case '#/about':
          contentElem.innerHTML = 'About'
          break
      }
    })
  </script>
</body>
</html>

history模式

history模式是调用history接口来实现的

使用history模式的页面,必须使用服务器打开,不支持file协议开头的URL

方法说明
replaceState方法替换原来的路径,不会有新的浏览记录产生
pushState方法使用新的路径,相当于新增了一条新的浏览记录
popstate事件当除因history.pushState()或history.replaceState()方法触发的活动历史记录条目更改时,将触发popstate事件
go方法向前或向后改变路径, 参数为数字,可以跳转到任何的页面
如果参数是0,表示的是刷新当前页面
如果前进和回退的数值大于现有的历史记录的数值时,不会进行任何的操作,也不报错
forward方法向前改变路径,相当于go(1)
back方法向后改变路径,相当于go(-1)
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <!-- 路由 -->
    <a href="/home">Home</a>
    <a href="/about">About</a>

    <!-- 占位符 -->
    <div id="content">Home</div>
  </div>

  <script>
    const contentElem = document.getElementById('content')

    const aEls = document.getElementsByTagName('a')

    const changeRouter = () => {
      switch(location.pathname) {
        case '/home':
          contentElem.innerHTML = 'Home'
          break

        case '/about':
          contentElem.innerHTML = 'About'
          break
      }
    }

    for (const aEl of aEls) {
      aEl.addEventListener('click', e => {
        // 阻止默认行为
        e.preventDefault()

        // 设置历史记录,并修改url --- 不会发生跳转
        /*
        	有三个参数:
        	1. 一个对象,可以用于存储一些需要在多个页面中传递的数据,
        		 可以使用history.state来获取,初始值是null,
        		 在使用pushState或replaceState后会变为所传入的值

        	2. 新页面的title,目前浏览器都不支持,保留属性
        	3. 新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址
                   (对应的拼接规则和URL的拼接规则一致)
        */
        history.pushState({}, '', aEl.getAttribute('href'))
        changeRouter()
      })
    }

    // 点击回退键和前进键的时候, 也需要重新渲染对应的内容
    /*
      1. 当活动历史记录条目更改时,将触发popstate事件
      2. 事件名都是小写的
      3. 这个事件是在window上的
    */
    window.addEventListener('popstate', changeRouter)
  </script>
</body>
</html>

Vue-router

Vue Router 是 Vue.js 的官方路由,与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用(SPA)变得非常容易

vue-router是基于路由和组件的,路由用于设定访问路径, 将路径和组件映射起来

在vue-router的单页面应用中, 页面的路径的改变就是组件的切换

npm install vue-router

基本使用

/routes/index.js

import { createRouter, createWebHistory } from 'vue-router'
import About from '../views/About'
import Home from '../views/Home'

const router = createRouter({
	// createWebHistory --- 创建history路由
	// createWebHashHistory --- 创建hash路由
	history: createWebHistory(),
	// 路由映射表
  // 当url发生改变的时候,vue-router会自动在路由表中查找对应组件
  // 并显示在合适的位置
	routes: [
    {
      path: '/', // 默认路径 重定向到 /home
      redirect: '/home'
    },
		{
			path: '/home',
			component: Home
		},
		{
			path: '/about',
			component: About
		}
	]
})

export default router

main.js

import { createApp } from 'vue'
import App from './App.vue'

import router from './routes'

const app = createApp(App)

// vue-router本质上是一个vue的官方插件
// 所以在注册路由的时候使用use方法
app.use(router)

app.mount('#app')

App.vue

<template>
	<div>
		<!-- router-link 是 vue-router内置的用于进行组件切换的内置组件 -->
    <router-link to="/home">home</router-link>
    <router-link to="/about">about</router-link>

		<!--
			router-view是vue-router内置组件
			因为组件是局部内容更新,所以需要占位符告诉vue-router对应组件需要被渲染到哪里
		-->
		<router-view />
	</div>
</template>

Router-link

属性说明
to是一个字符串,或者是一个对象
replace设置 replace 属性的话,当点击时,会调用 router.replace(),而不是 router.push()
active-class设置激活a元素后应用的class,默认是router-link-active
exact-active-class链接精准激活时,应用于渲染的 <a> 的 class,默认是router-link-exact-active

路由懒加载

当项目打包的时候,默认情况下,所有的逻辑代码都会被打包到app.js中

随着项目越来越大,app.js也会越来越大,会影响项目的首屏渲染速度

所以我们引入路由组件的时候,一般都是使用import函数进行引入,这样在打包的时候会进行分包处理

这样只有我们真正需要使用那些组件的时候,浏览器才会去加载那些组件

import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
	history: createWebHistory(),
	routes: [
		{
			path: '/home',
			// 启用路由懒加载
			// webpackChunkName 是魔法注释 这类注释在打包的时候,会被webpack所读取,所以这是有意义的注释
			// webpackChunkName 用于指定 打包后的chunk名,默认为[hash].[hash].js
			// 指定后就会变成 [name].[hash].js
			component: () => import(/* webpackChunkName: home */'../views/Home')
		},
		{
			name: 'about', // 路由记录独一无二的名称
			path: '/about',
			component: () => import('../views/About'),
			// meta属性用于设置自定义数据
			meta: {
				name: 'Klaus',
				age: 23
			}
		}
	]
})

export default router

动态路由

有些路由会携带动态参数,例如/user/1111, /user/2222等,但是他们都需要展示user组件

那么就需要使用动态路由,所谓动态路由,就是在路径中使用一个动态字段来接收对应的参数

而这个动态字段就被称之为路径参数

{
  path: '/user/:id', // 可以匹配 /user/1222 /user/aaa 但是无法匹配 /user
  component: () => import('../views/User.vue')
}
<template>
	<div>
		<!--
			 在template中可以使用$route来获取当前路由对象
			 注意: 是$route 不是 $router $router是当前路由表对象
		-->
		id: {{ $route.params.id }}
	</div>
</template>

<script setup>
	// 在options api中可以通过 this.$route 来获取当前的路由对象

	// 在composition api中 获取对应的路由对象,需要使用useRoute这个hook函数
	import { useRoute, onBeforeRouteUpdate } from 'vue-router'

	// useRoute hook 必须在setup语法糖顶层被调用和执行
	const route = useRoute()

	console.log(route.params.id)

  // 当动态参数改变,且当前组件被复用的时候 会回调的路由守卫函数
	// 参数to 表示 前往的那个路由的路由对象
	// 参数from 表示 当前的那个路由的路由对象
	onBeforeRouteUpdate((to, from) => {
		console.log(route.params.id)
	})
</script>

not found

对于哪些没有匹配到的路由,我们通常会匹配到固定的某个页面, 比如not found页面

这个时候我们可编写一个动态路由用于匹配所有的页面

import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
	history: createWebHistory(),
	routes: [
		{
			path: '/home',
			component: () => import('../views/Home')
		},
		{
			path: '/about',
			component: () => import('../views/About'),
		},
		{
			path: '/user/:id',
			component: () => import('../views/User.vue')
		},
		// vue-router在匹配路由表的时候,会从上到下依次进行匹配
		// 如果匹配到了对应的路由,vue-router就会加载对应组件并停止匹配
		// 所以not found页面一般会放置到整个路由表的最后边
		{
			// 在路由最后的小括号中 是对应动态参数运行匹配的正则表达式
			// 动态参数 pathMatch 是自定义的变量名
			// 在正则匹配后边加上* 表示的时候 按照 / 进行分割
			// 例如 /aaa/bbb/ccc
			// 		 -> /:pathMatch(.*) => /aaa/bbb/ccc
			// 		 -> /:pathMatch(.*)* => ['aaa', 'bbb', 'ccc']
			path: '/:pathMatch(.*)*',
			component: () => import('../views/NotFound.vue')
		}
	]
})

export default router

image.png