vue-router@4 使用 -动态路由刷新页面请求(含history模式打包踩坑)

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 守卫 只在进入路由时触发,不会在 paramsqueryhash 改变时触发。例如,从 /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 传递回调的唯一守卫。对于 beforeRouteUpdatebeforeRouteLeave 来说,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;
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值