VueRouter路由使用

简单的介绍😏

这一部分了解、理解一下,知道有就行

后端路由阶段:

  • 服务器渲染好整个页面,并且将页面返回给客户端
  • 不需要单独加载任何的js和css, 可以直接交给浏览器展示, 这样也有利于SEO的优化
  • 不利于维护

前后端分离阶段:

  • 后端只提供API来返回数据,前端通过Ajax获取数据,并且可以通过JavaScript将数据渲染到页面中
  • 前后端责任清晰,后端专注于数据上,前端专注于交互和可视化上

前端路由是如何做到URL和内容进行映射呢?监听URL的改变

URL的hash

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

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

(页面一旦刷新,又会向服务器发请求)

<div id="app">
<a href="#/home">home</a>
<a href="#/about">about</a>

<div class="content">Default</div>
</div>

<script>
const contentEl = document.querySelector('.content')
window.addEventListener("hashchange", () => {
switch(location.hash) {
case "#/home":
contentEl.innerHTML = "Home"
break
case "#/about":
contentEl.innerHTML = "About"
break
}
})

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

image.png

HTML5的History

history接口是HTML5新增的, 它有六种模式改变URL而不刷新页面:

replaceState:替换原来的路径

pushState:使用新的路径(压栈)

popState:路径的回退(弹栈)

go:向前或向后改变路径 go(1)前进1步,go(2),前进两步,go(-1),后退一步

forward:向前一步

back:后退一步

<div id="app">
<a href="/home">home</a>
<a href="/about">about</a>

<div class="content">Default</div>
</div>

<script>
const contentEl = document.querySelector('.content')

const changeContent = () => {
switch(location.pathname) {
case "/home":
contentEl.innerHTML = "Home"
break
case "/about":
contentEl.innerHTML = "About"
break
default:
contentEl.innerHTML = "Default";
}
}

const aEls = document.getElementsByTagName("a")
for (let aEl of aEls) {
aEl.addEventListener("click", e => {
e.preventDefault() // 阻止默认行为

const href = aEl.getAttribute("href")
// history.replaceState({}, "", href)
history.pushState({}, "", href)
changeContent()
})
}
window.addEventListener("popstate", changeContent)
</script>
image.png

vue-router📱

Vue Router 是 Vue.js 的官方路由,便于构建单页面应用

使用vue-router

  • 安装 Vue Router
npm install vue-router
  • 路由的使用步骤
  1. 创建路由组件

  2. 配置路由映射、创建路由对象

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

    // 配置映射关系
    const routes = [
    {
    path: '/home',
    component: Home
    },
    {
    path: '/about',
    component: About
    }
    ]

    // 创建一个路由对象router
    const router = createRouter({
    routes,
    // 使用路由的模式,hash / history
    history: createWebHistory()
    })

    // 最后导出
    export default router
  3. 在全局main.js中注册路由

    import router from './router'
    // 注册路由
    vue.use(router)
  4. 使用路由:<router-link> <router-view>

    这种<router-link to>声明式导航

    <router-link to="/home">首页</router-link>
    <router-link to="/about">关于</router-link>

    <router-view></router-view>

路由重定向

对 ‘/’ 这个路径,重定向到’/home’

{
path: '/',
// 重定向
redirect: '/home'
},

路由还有其他属性

  • name:给某个路由起独一无二的名字
  • meta:配置一些参数,被一起带到router对象中

路由懒加载

把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件

好处:提高首屏的渲染效率

实际就是webpack的分包,而Vue Router默认就支持动态来导入组件

  • 这是因为component可以传入一个组件,也可以接收一个函数,该函数需要返回一个Promise
  • 而import函数就是返回一个Promise

不采用直接import Home from '../pages/Home.vue'的方式

另外还可以对分包命名:/*webpackChunkName: "xxx"*/

{
path: '/home',
component: () => import("../pages/Home.vue")
},
{
path: '/about',
// 还可以对分包命名
component: () => import(/*webpackChunkName: "ahout-chunk" */"../pages/About.vue")
},

打包后的效果

image.png

router-link的属性

  • to:字符串(‘/home’)或者对象(配置路径+参数)

  • replace:替代

    设置 replace 属性的话,当点击时,会调用 router.replace(),而不是 router.push()

  • active-class属性:可以在这个class加样式啥的

    设置激活a元素后应用的class,默认是router-link-active(一般 不去修改)

  • exact-active-class属性

    链接精准激活时,应用于渲染的 的 class,默认是router-link-exact-active

    在嵌套路由中,例如有一个嵌套路由是:/home/message 那么这个class只会在message那个子路由中

动态路由

动态,即这个路由的路径是动态改变的

在Vue Router中,我们可以在路径中使用一个动态字段来实现,我们称之为路径参数

{
path: '/user/:username/id/:id',
component: () => import("../pages/User.vue")
}

:username? 加?表示可有可无,否则必须有

image.png

如何获取动态路由的值?

在template中:$route.params

在created中:this.$route.params

在setup中:要使用 vue-router库给我们提供的一个hook useRoute

import {useRoute} from 'vue-router'
setup() {
// 直接调用useRoute(),会返回一个路由对象
const route = useRoute()
console.log(route);
console.log(route.params);
}

NotFound

对于那些没有匹配到的路由,我们通常会匹配到固定的某个页面

可编写一个动态路由用于匹配所有的页面

{
path: '/:pathMath(.*)', // 用到正则 .*匹配任意字符0或多个
component: () => import("../pages/NotFound.vue")
}

可以通过$route.params.pathMath获取传入的参数

image.png

还有一种写法

{
path: '/:pathMath(.*)*' // 这里多加一个*
component: () => import("../pages/NotFound.vue")
}

区别在于解析的时候,是否解析 /:

把路径中的/…/ 分成一个个的

image.png

路由的嵌套

某个路由页面本身也存在路由的来回切换

配置子路由,同样在home中使用router-link,router-view

{
path: '/home',
component: () => import("../pages/Home.vue"),
children: [
{
path: 'message', // 这里不需要/
component: () => import("../pages/Message.vue")
},
{
path: 'goods',
component: () => import("../pages/Goods.vue")
}
]
}

编程式路由

不通过router-link的方式实现跳转,比如点击的是一个按钮等

setup() {
// 获取router对象
const router = useRouter()
const gotoMessage = () => {
router.push('/home/message') // 可以直接传字符串
}
const gotoGoods = () => {
router.push({
path: '/home/goods' // 也可以是对象
})
}
}

另外还有 router.replace、router.go、back、forward

vue2中:this.$router.push

query(查询)方式的参数

传递query参数:

router.push({
path: '/home/goods',
query:{
name: 'hello',
age: 18
}
})

在对应页面中获取query参数:

<h2>{{$route.query.name}}---{{$route.query.age}}</h2>

router-link的v-slot

用到查文档就好

<!-- v-slot可以拿到router-link给我们传递过来的属性(可以自己命名 props)
custom:表示自定义router-link,外部不会给我们包裹a元素了
props.navigate:所以当我们想点击某个元素跳转的时候可以使用navigate(当然用编程式导航也是可以的)
props.route:路由对象
props.href: 解析后的url
props: isActive 是否当前处于活跃的状态 (true,false)
props: isExactActive 是否当前处于精确的活跃状态
-->
<router-link to="/home" v-slot="props" custom>
<button>首页</button>
<p>href: {{props.href}}</p>
<button @click="props.navigate">跳转home</button>
<p>{{props.route}}</p>
<span :class="{'active': props.isActive}"></span>
<span :class="{'active': props.isActive}">{{props.isExactActive}}</span>
</router-link>

router-view的v-slot

router-view也提供给我们一个插槽,可以用于<transition><keep-alive>组件来包裹你的路由组件(用于加动画,缓存)

Component:要渲染的组件

route:解析出的标准化路由对象

<router-view v-slot="props">
<transition name="abc">
<!-- 可以拿到router-view给我们传过来的组件 -->
<keep-alive>
<component :is="props.Component"></component>
</keep-alive>
</transition>
</router-view>

动态添加路由

比如说我们要根据不同的权限来决定是否添加路由

路由对象router上有方法:addRoute()动态添加路由

// 定义要添加的动态路由(可以先定义
const momentRoute = {
path: '/moment',
component: () => import("../pages/HomeMoment.vue")
}
router.addRoute(momentRoute)

// 也可以添加的时候作为addRoute的第二个参数)
// 给home动态添加子路由cart 路径:/home/cart
router.addRoute('home', {
path: 'cart',
component: () => import("../pages/Cart.vue")
})

动态删除路由

三种方式:

  • 添加一个同名路由,相当于覆盖
  • 通过removeRoute方法,传入路由的名称
  • 通过addRoute方法的返回值回调
router.addRoute({path: '/about', name: 'about', component: About})
// 方式1
router.addRoute({path: '/other', name: 'about', component: Other})
// 方式2
router.removeRoute('about')
// 方式3
const removeRoute = router.addRoute({path: '/about', name: 'about', component: About})
removeRoute()

路由其他方法补充

  • router.hasRoute():检查路由是否存在。
  • router.getRoutes():获取一个包含所有路由记录的数组

路由导航守卫👮

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航

全局前置守卫beforeEach

在导航触发时会被回调

它有两个参数:

  • to:即将进入的路由对象
  • from:即将离开的Route对象

返回值:

  • false:取消当前导航
  • 不返回或者返回undefined:进行默认导航
  • 返回一个路由地址:
    • 路径字符串:‘/home’
    • 对象:类似于 router.push({path: “/login”, query: …})

小案例:如果没登录就跳转到登录页面

router.beforeEach((to, from) => {
if (to.path !== '/login') {
const token = window.localStorage.getItem('token')
if(!token) {
return "/login"
}
}
})

更多导航守卫看官网

https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

完整的导航解析流程

image.png

其他补充

historyApiFallback

开发中一个非常常见的属性,主要作用是解决SPA页面在路由跳转之后,进行页面刷新时,返回404的错误

如果设置为true,那么在刷新时,返回404错误时,会自动返回 index.html 的内容

因为我们使用的是前端路由,刷新页面时访问的资源在服务端找不到,因为vue-router设置的路径不是真实存在的路径。

注:我们使用 vue-cli的时候,默认已经帮我们设置为true了

module.exports = {
//...
devServer: {
historyApiFallback: true,
}
};

https://webpack.js.org/configuration/dev-server/#devserverhistoryapifallback