基础内容
`const routes 中的内容`
{
path:"",//路径
name:"",//命名
component:()=>import(""),//组件
meta:{},//元对象
children:[
// 子路由
]
// 生命周期钩子函数
}
`RouterView 组件可以使 Vue Router 知道你想要在哪里渲染当前 URL 路径对应的路由组件`
SPA 单页面引用
SSR 服务端渲染
没有客户端,完全访问服务端,服务端返回html页面
`优势`:获取页面速度快,只需要向服务器做一次请求,可以做SEO搜索引擎优化,SSR渲染意味着有若干个页面,每个页面都可以做SEO优化
`劣势`:点击跳转内容时,需要重新刷新页面
CSR 客户端渲染
从服务器中先获取客户端的页面,当点击时访问服务器获取对应的数据,然后根据数据重新渲染当前页面的部分内容
`优势`:不需要刷新页面,局部更新部分内容
`劣势`:先从服务器获取基础页面,然后再次访问服务器获取部分数据,然后再基础页面中渲染,因为只有单页面,所以可以做SEO搜索引擎
优化的部分特别少,基本上无法达到引流的目的
实现简单的跳转
1.App.js 文件还是负责引用文件
2.view下 BaseView.vue 负责视图效果
3.router下 放入对应的路由文件 BaseRouter.ts 用来引入子组件 并且写入routes内容
4.router下 index.js 需要引入BaseRouter.ts 这个路由,并且解构获取BaseRouter的内容
5.components下 还是用来放在文件夹 用来存放子组件
// BaseView.vue
<template>
<div>
<router-link to="/">首页</router-link>
<hr />
<router-link to="/a">ViewA</router-link>
<hr />
<router-link to="/b">ViewB</router-link>
<router-view />
</div>
</template>
<script>
</script>
// BaseRouter.ts
import ChildA from '@/components/base/ChildA.vue'
import ChildB from '@/components/base/ChildB.vue'
import MainView from '@/components/base/MainView.vue'
export default [
{
path: "/",
name: "main",
component: MainView
},
{
path: "/a",
name: "a",
component: ChildA
},
{
path: "/b",
name: "b",
component: ChildB
}
]
// index.js
import BaseRouter from "@/router/BaseRouter.ts"
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
const routes = [
...BaseRouter
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
// 导出创建的 router
export default router
在组合式和选项式中略有不同
选项式中
1.this.$route 当前路由中带过来的数据
2.this.$router 用来路由跳转的
3.this.$router.push("") 给里面设置路径,就会跳转到对应的路径路由的组件,会添加历史记录
4.this.$router.replace("") 跳转到路径设置的组件,但是不会添加历史记录
5.this.$router.push({name:""}) 设置一个对象,对象里是name,根据name跳转到对应的页面
<script>
// 选项式中
export default {
name: "App",
methods: {
clickHandler1() {
// this.$router 用来路由跳转的
console.log(this.$router)
// this.$route 当前路由中带过来的数据
console.log(this.$route)
// 给里面设置路径,就会跳转到对应的路径路由的组件,会添加历史记录
this.$router.push("/")
// 设置一个对象,对象里是name,根据name跳转到对应的页面
this.$router.push({name:"main"})
// 跳转到路径设置的组件,但是不会添加历史记录
this.$router.replace("/")
},
clickHandler2() {
// this.$router.push("/a")
// this.$router.push({name:"a"})
this.$router.replace("/a")
},
clickHandler3() {
// this.$router.push("/b")
this.$router.push({name:"b"})
}
}
}
</script>
组合式中
<script setup>
// 组合式中
// 可以先直接导入router文件 但是这样获取不到值还时要引入一些内容
// import router from "@/router";
import { useRouter, useRoute, Router, RouteLocationNormalizedLoaded } from "vue-router";
// 获取当前的路由跳转对象
const router = useRouter();
const route=useRoute();
function clickHandler1(){
router.push("/")
console.log(route)
}
function clickHandler2(){
router.push("/a")
}
function clickHandler3(){
router.push("/b")
}
</script>
路由传参
params传参( 动态路由传参)
`方式1`
// 使用router-link标签 params传参
<router-link to="/">首页</router-link>
<hr />
<router-link to="/a">ViewA</router-link>
<hr />
<router-link to="/b/123/nihao">ViewB</router-link>
<router-link :to="{ name: 'b-child', params: { id: '123', name: 'xietian' } }">ViewB</router-link>
`方式2`
1.在路由文件中的 path里要写入传参对应的值的名字 path: "/b/:id/:name",
2.在vue文件中 在需要传参的路由的 router.push中 写传入的值 router.push("/b/123/小波")
3.在当前组件中写入对应文件
// BaseView.vue中
<script setup>
// 组合式中
<button @click="clickHandler3">ViewB</button>
import { useRouter, useRoute, Router, RouteLocationNormalizedLoaded } from "vue-router";
// 获取当前的路由跳转对象
const router = useRouter();
const route = useRoute();
function clickHandler3() {
// 第一种方式
router.push("/b/123/小波")
// 第二种方式
router.push({ name: "b-child", params: { id: "123", name: "xiaocha" } })
}
// BaseRouter.ts中(路由文件)
import ChildA from '@/components/base/ChildA.vue'
import ChildB from '@/components/base/ChildB.vue'
import MainView from '@/components/base/MainView.vue'
export default [
{
path: "/",
name: "main",
component: MainView
},
{
path: "/a",
name: "a",
component: ChildA
},
{
path: "/b",
name: "b",
component: ChildB
},
{
path: "/b/:id/:name",
name: "b-child",
component: ChildB
}
]
// ChildB.vue中
<template>
<div>ChildB</div>
</template>
// params传参
<script setup>
import { useRoute, RouteLocationNormalizedLoaded } from "vue-router";
// 获取到需要传入的值
const route = useRoute();
console.log(route.params);
</script>
`注意点`
1.params传参 只能用来传参字符串
2.因为路由跳转,会将router-view中的组件卸载后,加载一个新的组件放在router-view,这意味着原有的那个组件会被销毁,重新放入时是新的组件
query传参
// 使用query传参,传的参数只能是字符串,对象和数组是无法进行传参
// 若是要传入对象和数组 要转换为JSON字符串,但是一般不会传对象和数组
`方式1`
// 使用router-link 进行 query传参
<RouterLink to="/">首页</RouterLink>
<RouterLink :to="{ name: 'a', query: { name: 'xietian', age: 20} }">按钮1</RouterLink>
<RouterLink to="/b?name=xietian$age=30">按钮2</RouterLink>
1.在vue文件中 在需要传参的路由的 router.push中 写传入的值 router.push("/b/123/小波")
2.在当前组件中写入对应文件 不需在路由文件中的 path里要写入传参对应的值的名字
`方式2`
// QueryView.vue中
<template>
<div>
<div>
<button @click="clickHandler1">首页</button>
<button @click="clickHandler2">按钮1</button>
<button @click="clickHandler3">按钮2</button>
</div>
<RouterView />
</div>
</template>
<script setup>
import { RouterView } from 'vue-router';
import router from "@/router"
function clickHandler1() {
router.push("/")
}
function clickHandler2() {
// router.push("/a")
router.push({ name: "a", query: { name: "lyf", age: 24 } })
}
function clickHandler3() {
// router.push("/b")
router.push("/b?name=xt$age=35")
}
</script>
// QueryRouter.ts中(路由文件)
import ChildA from "@/components/query/ChildA.vue"
import ChildB from "@/components/query/ChildB.vue"
import MainView from "@/components/query/MainView.vue"
export default [
{
path: "/",
name: "main",
component: MainView
},
{
path: "/a",
name: "a",
component: ChildA
},
{
path: "/b",
name: "b",
component: ChildB
}
]
// 子组件中(任取一个举例)
<template>
<div>ChildA</div>
</template>
<script setup>
import { useRoute, RouteLocationNormalizedLoaded } from "vue-router";
const route = useRoute();
console.log(route)
</script>
路由命名
// 可以为任何路由提供 name
`优点`:没有硬编码的 URL(没写路由的)
params 的自动编码/解码
防止你在 url 中出现打字错误。
绕过路径排序(如显示一个)
命名视图路由
可以在一个路由中修改多个不同的视图容器内容,放入不同的命名路由视图中
// ViewNameView.vue中
<template>
<div>
<div>
<button @click="clickHandler1">首页</button>
<button @click="clickHandler2">页面1</button>
</div>
<div class="div1">
<RouterView name="left" />
</div>
<div class="div1">
<RouterView name="right" />
</div>
<div class="div1">
<RouterView />
</div>
</div>
</template>
<script setup>
import { RouterView } from 'vue-router';
import router from '@/router';
function clickHandler1() {
router.push("/")
}
// 命名视图路由之后 跳转到/a 可以同时显示出多个路由
function clickHandler2() {
router.push("/a")
}
</script>
<style lang="stylus">
.div1
width:300px;
height:300px;
border:1px solid #000;
float:left ;
</style>
// viewName.ts中
import MainView from "@/components/viewName/MainView.vue"
import ChildA from "@/components/viewName/ChildA.vue"
import ChildB from "@/components/viewName/ChildB.vue"
import ChildC from "@/components/viewName/ChildC.vue"
export default [
{
path: "/",
name: "main",
component: MainView
},
{
path: "/a",
// name: "a" 路由命名
name: "a",
// 这里放在了不同的命名路由视图中 left就是router-view中的name
// left: ChildA 就是命名路由视图
components: {
left: ChildA,
right: ChildB,
default: ChildC
}
}
]
嵌套路由
`使用过程中一般嵌套两层`
// 在ChildA中 嵌套 ChildB、ChildC
//NextView.vue中
<template>
<div>
<div>
<button @click="clickHandler1">首页</button>
<button @click="clickHandler2">页面A</button>
</div>
<div class="div1">
<RouterView></RouterView>
</div>
</div>
</template>
<script setup>
import { RouterView } from 'vue-router';
import router from '@/router';
function clickHandler1() {
router.push("/")
}
function clickHandler2() {
router.push("/a")
}
</script>
<style lang="stylus" scoped>
.div1
width:500px;
height:500px;
border:1px solid #000;
margin:0 auto
</style>
// ChildA.vue中
<template>
<div>
<button @click="clickHandler1">按钮B</button>
<button @click="clickHandler2">按钮C</button>
<div class="div2">
<RouterView></RouterView>
</div>
</div>
</template>
<script setup>
import router from '@/router';
// 跳转到/a 下面的 b、c
function clickHandler1(){
router.push("/a/b")
}
function clickHandler2(){
router.push("/a/c")
}
</script>
<style lang="stylus" scoped>
.div2
width:100%;
height:300px;
margin-top:50px;
background-color:rgba(255,0,0,0.2)
</style>
// NextRouter.ts中
import MainView from "@/components/nextView/MainView.vue"
import ChildA from "@/components/nextView/ChildA.vue"
import { RouteLocationNormalized } from "vue-router"
export default [
{
path: "/",
name: "main",
component: MainView
},
{
path: "/a",
name: "a",
component: ChildA,
// 子路由中的内容会放在父组件的ChildA中routerView中,而不是放在顶层的routerView中
children: [
{
// /a /的作用是根路由
// path:"/a/b" 与下面的写法一样
path: "b",
name: "b",
component: ChildB
},
{
path: "c",
name: "c",
component: ChildC
}
]
}
]
路由重定向
{
path: "/b",
name: "b",
// 路由重定向
redirect:(to)=>{
// 重定向直接跳转转到/a 下的b中,并传递数据
return {path:"/a/b",query:to.query}
}
}
路由守卫
路由活动的生命周期,都会通过执行一个函数来完成工作,函数都是当时间触发时回调执行的,所以这些函数又叫做钩子函数
路由的生命周期钩子函数,主要在于 当触发路由前,触发路由后执行的,所以叫守卫
`三大类`
1.全局守卫 在router主文件中写入
全局路由前置守卫 beforeEach 用来决定是否跳转
//当进入这个路由时,跳出之前触发,必须返回要跳转的路由对象
全局解析守卫 beforeResolve 用来解析数据
//与beforeEach类似,在beforeEach之后执行
//所有组件内守卫和异步路由组件被解析之后,解析守卫就被正确调用
//一般我们可以在这里处理一些数据,比如元数据
全局后置钩子守卫 afterEach
//路由后置钩子守卫没有next,执行return true
//当所有跳转完成后执行这个钩子函数
//它们对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用
2.路由独享守卫 beforeEnter 在各自的路由配置中写入
// 在路由前置守卫和路由解析守卫之间执行,还没有跳转路由
// 这个位置是针对当前这一个路由去执行守卫函数,只针对当前路由完成部分的数据处理等内容
3.组件内守卫 在组件内执行路由守卫
组件内进入守卫 beforeRouteEnter
组件内更新守卫 beforeRouteUpdate
组件内离开守卫 beforeRouteLeave
`to和from中的内容`
fullPath //全路径 包括路由路径 hash 和 query
hash //hash历史记录是才有 #后面的内容
href //与全路径相似
matched //当前路由父子关系数组,包括父级路由,子级路由
meta //元数据,用来针对某个路由存储的数据
name //路由名,可以用来跳转路由
params //params参数,动态路由的参数
path //路径,仅有路由路径
query //query参数,在路由地址?后面的内容
redirectedFrom //重定向来源路由,从什么路由重定向来的
`使用`
// 全局守卫 在router主文件中写入
1.全局路由前置卫
// 当进入这个路由时,跳转之前触发,必须返回要跳转的路由对象
// to 就是路由将要跳转到的目标路由对象
// from 从什么路由开始跳转,也就是当前还没有跳转的路由对象
// next 是一个函数,当执行next将会继续向目标路由跳转
// 使用场景:当在页面中没有登录的情况下,进入了某些子项,例如购物车,这时候不能跳转到购物车页面,因为没有登录,需要判断当前是否登录,如果没有登录,这时候跳转回登录页面,如果登录过了,就会跳转到购物车页面
// 每次路由跳转,不管任何路由都会进入一次
router.beforeEach((to, from, next) => {
// next();
// 如果写成下面的内容,就会当除了跳转a以外的任何路由都是禁止的
if (to.name !== "a") router.replace("/a")
else next()
// 根据以上内容 可以用来在不满足条件时只让页面跳转到登录页面
})
2.全局解析守卫
// 用来解析数据,将数据传递给新的路由中
router.beforeResolve((to, from, next) => {
console.log("beforeResolve");
// console.log(to.meta);
// 在这里进行新数据添加和解析
if (!to.meta.abc) {
to.meta.abc = "b"
}
console.log(to, from);
next();
})
3.全局后置钩子守卫
// 路由后置钩子守卫没有next,执行return true
router.afterEach((to, from) => {
console.log("afterEatch")
// 表示正常可以跳转到路由部分,我们可以设置一些页面信息,修改title等等
document.title = to.meta.title
return true;
})
4.路由独享守卫 放在路由中
beforeEnter: (to, from, next) => {
// 在路由前置守卫和路由解析守卫之间执行,还没有跳转路由
// 这个位置是针对当前这一个路由去执行守卫函数,只针对当前路由完成部分的数据处理等内容
console.log("beforeEnter");
console.log(to, from, next);
next();
}
5.组件内进入守卫 beforeRouteEnter
// 组件进入时触发
beforeRouteEnter(to, from, next) {
console.log("beforeRouteEnter");
next();
}
6.组件内更新守卫 beforeRouteUpdate
// 当组件时动态路由,路由跳转,页面不刷新时,更新进入 /a/:id /a/123 --> /a/234
// 由a页面下的123页面 跳转至 a页面下的234页面
beforeRouteUpdate(to, from, next) {
console.log("beforeRouterUpdate");
next()
}
7.组件内离开守卫 beforeRouteLeave
// 导航离开该组件的对应路由时调用,通常用来禁止用户还未保存修改前突然离开
// 当希望阻止用户离开当前页面时,可以调用next(false)
beforeRouteLeave(to, from, next) {
console.log("beforeRouterLeave");
next();
}
完整的路由守卫触发流程
1.导航被触发。
2.在失活的组件里调用 beforeRouteLeave 守卫。
3.调用全局的 beforeEach 守卫。
4.在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
5.在路由配置里调用 beforeEnter。
6.解析异步路由组件。
7.在被激活的组件里调用 beforeRouteEnter。
8.调用全局的 beforeResolve 守卫(2.5+)。
9.导航被确认。
10.调用全局的 afterEach 钩子。
11.触发 DOM 更新。
12.调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
路由元信息meta
// 对于某个路由存储信息,存储数据,可以任意信息附加到路由上,如过渡名称、谁可以访问路由等
// 可以在路由地址和导航守卫上都被访问到
// 可以用来权限设置
{
path: "/login",
name: "login",
component: ChildB,
` meta: { login: true, user: ["A", "B"] }`
}
router.beforeEach((to,from,next)=>{
`if(to.meta.login){
console.log("这是登录跳转的");
}`
next();
})
过渡路由
路由和过渡标签配合使用
`transition不能直接使用在router-view`,因此我们给router-view使用插槽,将返回的组件放在一个动态组件中
// 让每个路由的组件有不同的过渡,你可以将元信息和动态的 name 结合在一起,放在<transition> 上
<router-view #default="{ Component, route }">
// 获取route内 meta中的transition
// 通过名称不同组件显示不同效果
<transition :name="route.meta.transition">
// 绑定动态组件
<component :is="Component"></component>
</transition>
</router-view>
// meta中存储的transition用于过渡动画的名称,这个名称可以让不同的组件显示时有不同的动画效果
export default [
{
path: "/",
name: "main",
component: ChildA,
meta: { transition: "a" }
},
{
path: "/login",
name: "login",
component: ChildB,
meta: { transition: "b" }
},
{
path: "/c",
name: "c",
component: ChildC,
meta: { transition: "c" }
},
]
路由懒加载
`原因`
如果直接导入,这时候页面开始的时候就会将所有的组件都导入,将会降低首页显示速度,因此,
// 除了第一个默认路径(根路径),其他所有路由都使用懒加载
{
path: "/c",
name: "c",
// 懒加载
`component: () => import("@/components/metaView/ChildC.vue")`,
meta: { transition: "c" }
}
动态创建路由
1.第一步
// 动态创建路由时
// 在全局内写入这个数组
const routeList = [
'/info/ChildB',
'/info/ChildC',
'/sys/ChildD',
'/film/ChildE'
]
2.`第二步 需要在主路由文件内写以下内容`
router.beforeEach((to, from, next) => {
// 若
if (!to.meta.bool) {
// 获取routeList数组中的每一项-->都是地址
// path 就是地址 对应每一项item
// name 通过截取item值获取
// component 组件名需要使用懒加载 通过item截取获取
routeList.forEach(item => {
router.addRoute({
path: item,
name: item.split("/").pop(),
component: () => import(`@/components/dynamicView/${item.split("/").pop()}`)
})
})
to.meta.bool = true
}
next()
})
3.第三步 获取到创建的动态路由并使用
<template>
<div>
<div>
// value.path 是要跳转的路径
<button v-for="(value, key) in list" :key="key" @click="router.push(value.path)">{{ value.name }}</button>
</div>
<router-view></router-view>
</div>
</template>
<script setup>
import router from "@/router/index";
import { reactive } from "vue"
const list = reactive([
{ name: "按钮1", path: "/info/ChildB" },
{ name: "按钮2", path: "/info/ChildC" },
{ name: "按钮3", path: "/sys/ChildD" },
{ name: "按钮4", path: "/film/ChildE" },
])
</script>
路由滚动条
// 写在主路由文件下
// 固定写法
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
// 这个触发的条件就是当点击回退或者前进时才会回退,重新点击跳转路由时并不会
`scrollBehavior: (to, from, pos)` => {
// 这里的pos就是上次的滚动条位置
if (pos) {
return pos;
}
return { top: 0 }
}
})
配置404页面
// 在主路由文件内写以下内容
const routes = [
// ...MetaRouter
...DynamicRouter,
// 配置404页面
`{ path: "/:pathMatch(.*)", component: () => import("@/views/404Page.vue") }`
]