admin管理员组文章数量:1633927
目录
- vue-router@4 使用
- 1.下载安装
- 2.路由搭建
- main.ts
- router-link
- 3.响应路由参数变化
- 在同一个页面使用动态路由id占位,不会触发页面生命周期函数
- 4.嵌套路由
- 5.编程式导航
- 6.props
- 7.导航首位
- 7.1全局前置守卫-beforeEach
- 7.2 全局解析路由-beforeResolve
- 7.3 beforeEach和beforeResolve区别
- 7.3 全局后置路由-afterEach
- 7.4 路由独享的守卫-beforeEnter
- 7.5 组件内的守卫
- beforeRouteEnter
- beforeRouteUpdate
- beforeRouteLeave
- 完整的导航解析流程
- 8.路由元信息
- TypeScript
- 9.组合式api
- 9.1setup当中没有this,使用useRouter与useRoute
- 9.2组件内导航守卫
- onBeforeRouteUpdate:
- onBeforeRouteLeave
- 10.keep-alive/rouer-view/component/trasition相关
- 1.router-view+component--->4.x版本使用
- 2.router-view ----->4.x版本不再使用
- 3.router-view+transition+keep-alive
- HTML
- STYLE
- 补充:
- 11.addRoute
- 例子:
- 12.history打包踩坑
- 坑:
- 调整:
- nginx
- 补充:
vue-router@4 使用
1.下载安装
npm install vue-router@next --save
2.路由搭建
//需要使用*引入
import * as VueRouter from 'vue-router'
//页面组件引入
import FirstPage from '@/view/firstPage.vue'
import SeconPage from '@/view/secondPage.vue'
import ThirdPage from '@/view/thirdPage.vue'
//定义路由
const routes=[
{
path:'/',
component:FirstPage
},
{
path:'/secondPage',
component:SecondPage
},
{
path:'/thirdPage/:id',//:id占位 动态路由 页面中使用params进行获取 刷新不丢失
component:ThirdPage
}
]
//创建路由
const router = VueRouter.createRouter({
history:VueRouter.createWebHashHistory(),//hash模式 history模式
routes,
})
export default router
main.ts
import {createApp} from "vue"
import router from "@/router/index"
import App from "./App.vue"
const app = createApp(App)
//确保 _use_ 路由实例使
//整个应用支持路由。
app.use(router)
app.mount('#app')
router-link
<router-link to="/router"></router-link>
3.响应路由参数变化
在同一个页面使用动态路由id占位,不会触发页面生命周期函数
//route是一个reactive对象
//watch 只能监听一个getter/effect 或则ref reactive对象 或则 包含这些数据类型的数组
//所以此处使用函数return的形式 监听reactive对象中的params属性 或则监听无效
watch(()=>route.params, (v, o) => {
console.log(v,o)
});
//使用路由守卫可以获取params
onbeforeRouterUpdate((to,form)=>{
console.log(to.params)
})
4.嵌套路由
const routes=[
{
path:'/',
component:FirstPage
},
{
path:'/secondPage',
component:SecondPage,
redirect:'/secondPage/child1',//默认重定向到child1子路由
//redirect 可以是一个字符串,也可以是一个函数返回值
children:[
{
path:'child1',//子路由规则不需要/
component:Child1
},
{
path:'child2',
component:Child2
}
]
},
{
path:'/thirdPage/:id',//:id占位 动态路由 页面中使用params进行获取 刷新不丢失
component:ThirdPage
}
]
5.编程式导航
<template>
<div>user</div>
<!-- 刷新不丢失 -->
<button @click="jump1">path+query跳转1(刷新不丢失)</button>
<!-- 刷新不丢失 -->
<button @click="jump2">name+query跳转2(刷新不丢失)</button>
<!-- 刷新丢失 -->
<button @click="jump3">name+params跳转1(刷新丢失)</button>
<!-- params不能与path一起使用 -->
<button @click="jump4">path+params跳转2(不能path+params)</button>
<br>
<router-link :to="{path:'userList',query:{index:5}}">{path:'userList',query:{index:5}不丢失</router-link>
<br>
<router-link :to="{name:'userList',query:{index:6}}">{name:'userList',query:{index:6}不丢失</router-link>
<br>
<router-link :to="{name:'userList',params:{index:7}}">{name:'userList',params:{index:7}丢失</router-link>
</template>
<script lang="ts" setup>
import { useRouter } from 'vue-router';
const router=useRouter()
const jump1=()=>{
router.push({path:'/userList',query:{index:1}})
}
const jump2=()=>{
router.push({name:'userList',query:{index:2}})
}
const jump3=()=>{
router.push({name:'userList',params:{index:3}})
}
const jump4=()=>{
router.push({path:'/userList',params:{index:4}})
}
</script>
6.props
//
const routes=[
{
path:'/',
component:FirstPage
},
{
path:'/secondPage',
component:SecondPage,
redirect:'/secondPage/child1',//默认重定向到child1子路由
//redirect 可以是一个字符串,也可以是一个函数返回值
children:[
{
path:'child1',//子路由规则不需要/
component:Child1,
name:'Child1',
//props设置为true
props:true,
//props 可以是对象
props:{index:'1'},
//props可以是函数 可以获取query参数
props:route=>({...route.query})
},
{
path:'child2',
component:Child2
}
]
},
{
path:'/thirdPage/:id',//:id占位 动态路由 页面中使用params进行获取 刷新不丢失
component:ThirdPage
}
]
//路由跳转使用params
//{name:'Child1',params:{index:1}}
const {index}=defineProps<{index:string}>()
onMounted(()=>{
console.log(index)//1
})
7.导航首位
7.1全局前置守卫-beforeEach
beforeEach((to,from,next)=>{
//to 去向路由
//from 来源路由
//next next()
//next 可被多次调用 需要根据条件 确定被调用一次
})
7.2 全局解析路由-beforeResolve
router.beforeResolve((to,from,next)=>{
})
7.3 beforeEach和beforeResolve区别
都会在路由跳转前就执行 参数都是 to from next
不同:beforeEach是在路由规则循环之前执行,beforeResolve是在组件被解析之后调用
beforeEach早于beforeResolve
7.3 全局后置路由-afterEach
对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用
afterEach((to,from,failure)=>{
})
7.4 路由独享的守卫-beforeEnter
beforeEnter
守卫 只在进入路由时触发,不会在 params
、query
或 hash
改变时触发。例如,从 /users/2
进入到 /users/3
或者从 /users/2#info
进入到 /users/2#projects
。它们只有在 从一个不同的 路由导航时,才会被触发。
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: (to, from) => {
// reject the navigation
return false
},
},
]
7.5 组件内的守卫
beforeRouteEnter
在导航确认前被创建,组件还未被创建,还没有this
beforeRouteEnter((to,from,next)=>{
})
//next((vm)=>{})
//可通过next传入一个回调函数 访问组件实例 导航被确认的时候执行回调,并且把组件实例作为回调方法的参数
beforeRouteEnter
是支持给 next
传递回调的唯一守卫。对于 beforeRouteUpdate
和 beforeRouteLeave
来说,this
已经可用了,所以不支持 传递回调,因为没有必要了
beforeRouteUpdate
beforeRouteUpdate((to,from)=>{})
beforeRouteLeave
这个 离开守卫 通常用来预防用户在还未保存修改前突然离开。该导航可以通过返回 false
来取消。
beforeRouteLeave (to, from) {
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (!answer) return false
}
完整的导航解析流程
导航被触发=>
失活的组件中调用beforeRouteLeave=>
全局beforeEach=>
再重用的组件中调用beforeRouteUpdate=>
路由配置里调用beforeEnter=>
解析异步路由组件=>
在被激活的组件中beforeRouteEnter=>
调用全局解析守卫beforeResolve=>
导航被确认=>
全局路由后置守卫afterEach=>
触发dom更新=>
调用beforeRouteEnter中的next的回调函数
8.路由元信息
const routes = [
{
path: '/posts',
component: PostsLayout,
children: [
{
path: 'new',
component: PostsNew,
// 只有经过身份验证的用户才能创建帖子
meta: { requiresAuth: true }
},
{
path: ':id',
component: PostsDetail
// 任何人都可以阅读文章
meta: { requiresAuth: false }
}
]
}
]
beforeEach((to,from,next)=>{
//访问meta属性
console.log(to.meta)
})
console.log(this.$route.meta)//访问meta
TypeScript
可以通过扩展 RouteMeta
接口来输入 meta 字段:
// typings.d.ts or router.ts
import 'vue-router'
declare module 'vue-router' {
interface RouteMeta {
// 是可选的
isAdmin?: boolean
// 每个路由都必须声明
requiresAuth: boolean
}
}
9.组合式api
9.1setup当中没有this,使用useRouter与useRoute
import { useRouter, useRoute } from 'vue-router'
<script setup lang='ts'>
const router = useRouter()
const route = useRoute()
router.push(...)
</script>
route
对象是一个响应式对象,所以它的任何属性都可以被监听,但你应该避免监听整个 route
对象
9.2组件内导航守卫
onBeforeRouteUpdate:
添加一个导航守卫,在当前位置即将更新时触发。类似于beforeRouteUpdate,但它可以在任何组件中使用。当组件被卸载时,导航守卫将被移除。
参数:要添加的导航守卫信息
onbeforeRouteUpdate((updateGuard: NavigationGuard)=>{
})
//如下
{
fullPath,
hash,
href,
matched,
meta,
name,
params,
path,
query,
redirectedFrom
}
onBeforeRouteLeave
添加一个导航守卫,在当前位置的组件将要离开时触发。类似于beforeRouteLeave ,但它可以在任何组件中使用。当组件被卸载时,导航守卫将被移除。
onbeforeRouteUpdate((leaveGuard: NavigationGuard)=>{
})
10.keep-alive/rouer-view/component/trasition相关
1.router-view+component—>4.x版本使用
<template>
<div>
<div>当前用户:{{ userInfo.username }}</div>
<button @click="loginout">退出登录</button>
<div>导航栏</div>
<siderBar></siderBar>
//使用router-view插槽
<router-view v-slot="{ Component }">
//INCLUDE 定义需要缓存的组件
<keep-alive :include="['roleList']">
<component :is="Component"></component>
</keep-alive>
</router-view>
</div>
</template>
2.router-view ----->4.x版本不再使用
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />
3.router-view+transition+keep-alive
HTML
<router-view v-slot="{ Component }">
<transition name="fade">
<keep-alive :include="['roleList']" mode="out-in">
<component :is="Component"></component>
</keep-alive>
</transition>
</router-view>
STYLE
<style>
.fade-enter-from,
.fade-leave-to {
transform: translateX(20px);
opacity: 0;
}
.fade-enter-to,
.fade-leave-from {
opacity: 1;
}
.fade-enter-active {
transition: all 0.7s ease;
}
.fade-leave-active {
transition: all 0.3s cubic-bezier(1, 0.6, 0.6, 1);
}
</style>
补充:
需要注意,页面过渡效果需要在页面当中使用单个的div进行包裹,不然会警告
<template>
//单独的div进行包裹
<div>
<div>userList</div>
<div>{{ num }}</div>
<button @click="changeNum">changeNum</button>
</div>
</template>
11.addRoute
addRoute(route: RouteRecordRaw): () => void
例子:
模拟数据如下:
//一般用户
{"username":"小张","role":"一般用户","userId":"123","showMenu":[{"menucname":"会员管理","menuType":"1","menuCode":"1","childrenMenu":[{"menucname":"新增会员","path":"user","menuType":"2","menuCode":"1-1"},{"menucname":"会员列表","path":"userList","menuType":"2","menuCode":"1-2"}]},{"menucname":"权限管理","menuType":"1","menuCode":"3","childrenMenu":[{"menucname":"新增管理员","path":"role","menuType":"2","menuCode":"3-1"},{"menucname":"管理员列表","path":"roleList","menuType":"2","menuCode":"3-2"}]}]}
//管理员
{"username":"小王","role":"管理员","userId":"123","showMenu":[{"menucname":"会员管理","menuType":"1","menuCode":"1","childrenMenu":[{"menucname":"新增会员","path":"user","menuType":"2","menuCode":"1-1"},{"menucname":"会员列表","path":"userList","menuType":"2","menuCode":"1-2"}]},{"menucname":"权限管理","menuType":"1","menuCode":"2","childrenMenu":[{"menucname":"新增管理员","path":"role","menuType":"2","menuCode":"2-1"},{"menucname":"管理员列表","path":"roleList","menuType":"2","menuCode":"2-2"}]},{"menucname":"账务管理","menuType":"1","menuCode":"3","childrenMenu":[{"menucname":"查看账单","path":"money","menuType":"2","menuCode":"3-1"},{"menucname":"修改账单","path":"moneyList","menuType":"2","menuCode":"3-2"}]}]}
router文件夹=>index.ts
//index.ts
import NotFound from "@/view/notFound.vue";
import Home from "@/view/home.vue";
import Login from "@/view/login.vue";
import Welcome from "@/view/welcome.vue";
import * as VueRouter from "vue-router";
//引入仓库
import main from "@/store/main";
import { storeToRefs } from "pinia";
import {getManager,getOther} from "@/api/request"
import { userInfoType } from "@/utils/const";
import SiderBar from '@/components/siderBar.vue'
// 1. 定义一些路由
const user = { path: "user", component: () => import("@/view/user.vue") };
const userList = {
path: "userList",
component: () => import("@/view/userList.vue"),
};
const role = { path: "role", component: () => import("@/view/role.vue") };
const roleList = {
path: "roleList",
component: () => import("@/view/roleList.vue"),
};
const money = { path: "money", component: () => import("@/view/money.vue") };
const moneyList = {
path: "moneyList",
component: () => import("@/view/moneyList.vue"),
};
interface ruleMappingType {
[key: string]: any;
}
const ruleMapping: ruleMappingType = {
user,
userList,
role,
roleList,
money,
moneyList,
};
const routes = [
{
path: "/",
component: Login,
},
{
path: "/home",
name: "Home",
component: Home,
redirect: "/home/welcome",
children: [
{
path: "welcome",
component: Welcome,
meta:{
keepAlive:true
}
},
],
},
//404通配符
{ path: "/:pathMatch(.*)*", name: "NotFound", component: NotFound },
];
// 2.创建路由实例并传递 `routes` 配置
const router = VueRouter.createRouter({
history: VueRouter.createWebHistory(),
routes,
});
//初始化路由数据
export const initRoutes = async () => {
let res;
//模拟权限登录
//'2' 管理员登录
//'1' 普通用户登录
if(sessionStorage.token==='2'){
res=await getManager();
}else if(sessionStorage.token==='1'){
res=await getOther();
}
const {userInfo} = storeToRefs(main());
userInfo.value=(res?.data) as userInfoType;
((userInfo.value.showMenu) as Record<string,any>).forEach(
(item: Record<string, any>) => {
console.log(item.childrenMenu)
item.childrenMenu.forEach((i: Record<string, any>) => {
const temp = ruleMapping[i.path as string];
console.log(temp)
temp.name=i.path;
// temp.meta.keepAlive=true
//需要用使用路由名Home 将路由信息 加入到对应的父级路由当中
router.addRoute("Home", temp);
});
}
);
};
router.beforeEach(async (to, form, next) => {
const mainStore = main();
//如果含有token 表示已经登录 根据仓储数据isRefresh判断是否刷新页面 调用请求获取路由信息 然后next({path:to.path,query:to.query})
if(sessionStorage.getItem('token')){
if(!mainStore.isRefresh){
mainStore.isRefresh=true
await initRoutes()
next({path:to.path,query:to.query})
}else{
next()
}
}else{
next();
}
});
// 3.暴露
export default router;
12.history打包踩坑
坑:
1.最开始的写法
1.1 vite.config.ts
base 使用了./
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import {join} from "path";
// const { join } = require("path");
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
base: "./",//这里是重点 ./
resolve: {
alias: {
"@": join(__dirname, "src"), //需要配合tsconfig.json文件配置baseUrl和paths设置src别名@
},
},
server: {
port: 3001, // 服务端口号
open: true, // 服务启动时是否自动打开浏览器
cors: true, // 允许跨域
//配置代理
proxy: {
"/api": {
target: "http://localhost:3000",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
define: {
"process.env": {},
},
build: {
target: "modules",
outDir: "dist", //指定输出路径
assetsDir: "assets", // 指定生成静态资源的存放路径
minify: "terser", // 混淆器,terser构建后文件体积更小
},
publicDir: "public",
});
1.2 router配置
createWebHistory未写入参数
// 创建路由实例并传递 `routes` 配置
const router = VueRouter.createRouter({
history: VueRouter.createWebHistory(),
routes,
});
nginx部署之后,导致刷新页面 404
原因分析:base ./用于开发环境 /用于生产环境
调整:
base:“/”,//这里是使用nginx html根目录作为资源入口 history模式下搭配 createWebHistory()
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import {join} from "path";
// const { join } = require("path");
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
//base: "./",//这里是重点 ./
base:"/",//这里是使用nginx html根目录作为资源入口 history模式下搭配 createWebHistory()
resolve: {
alias: {
"@": join(__dirname, "src"), //需要配合tsconfig.json文件配置baseUrl和paths设置src别名@
},
},
server: {
port: 3001, // 服务端口号
open: true, // 服务启动时是否自动打开浏览器
cors: true, // 允许跨域
//配置代理
proxy: {
"/api": {
target: "http://localhost:3000",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
define: {
"process.env": {},
},
build: {
target: "modules",
outDir: "dist", //指定输出路径
assetsDir: "assets", // 指定生成静态资源的存放路径
minify: "terser", // 混淆器,terser构建后文件体积更小
},
publicDir: "public",
});
nginx
location / {
root html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
补充:
多文件部署:
**base: “/ntf/”,//这里是重点 ,根据html根目录下子文件位置填写 **
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import {join} from "path";
// const { join } = require("path");
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
base: "/ntf/",//这里是重点
resolve: {
alias: {
"@": join(__dirname, "src"), //需要配合tsconfig.json文件配置baseUrl和paths设置src别名@
},
},
server: {
port: 3001, // 服务端口号
open: true, // 服务启动时是否自动打开浏览器
cors: true, // 允许跨域
//配置代理
proxy: {
"/api": {
target: "http://localhost:3000",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
define: {
"process.env": {},
},
build: {
target: "modules",
outDir: "dist", //指定输出路径
assetsDir: "assets", // 指定生成静态资源的存放路径
minify: "terser", // 混淆器,terser构建后文件体积更小
},
publicDir: "public",
});
路由调整
// 创建路由实例并传递 `routes` 配置
const router = VueRouter.createRouter({
history: VueRouter.createWebHistory("/ntf"),
routes,
});
nginx调整
location /ntf {
root html;
index index.html index.htm;
try_files $uri $uri/ /nft/index.html;
}
版权声明:本文标题:vue-router@4 使用 -动态路由刷新页面请求(含history模式打包踩坑) 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/xitong/1729177218a1188869.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论