Agregue enrutamiento dinámico y salte después del inicio de sesión de Vue (super detallado)

prefacio

Marco del proyecto: Vue3+TypeScript

Existe el requisito de que el sistema solo tenga las rutas más básicas de forma predeterminada, como inicio de sesión, 404, etc. Otras rutas deben agregarse dinámicamente después del inicio de sesión. El sistema no tiene una página de inicio fija. Después de iniciar sesión, saltará a la primera página del menú dinámico.

analizar

Esta lógica es simple a primera vista, pero de hecho hay muchos pequeños hoyos en ella. Uno de los baches más fáciles de pisar es que la dinámica 路由尚未渲染完成ya disparó un salto de enrutamiento, en este momento debe ser 404, porque la ruta no existe, otro escollo que es fácil de pisar es que la página estará en 路由重复加载blanco en este momento, y debe actualizarse manualmente para que se muestre normalmente. .

Lo primero que se me ocurre es usar Promisela función para solucionarlo, pero no funciona. addRoutees una tarea macro y resolvees una tarea micro, por lo que Promiseel final de no significa que se haya agregado el enrutamiento dinámico.

En segundo lugar, pensé en usar asyncla función para garantizar que cuando se obtenga el resultado de inicio de sesión exitoso, se haya agregado la ruta, pero después de algunos intentos, todavía no funciona. Debido a que la operación de agregar una ruta no es asíncrona y no devuelve Promiseun objeto, aquí awaitno tendrá ningún efecto. (PD: use después Promise.allpara resolver este problema, el método específico se mencionará más adelante).

Finalmente, se me ocurrió una solución muy estúpida, sondear. Después de experimentar, se determina que se puede lograr, pero como se dijo al principio, esto parecerá muy incómodo low, aunque finalmente solucionó el problema.

práctica

La operación de inicio de sesión es la misma, así que sáquelo y escríbalo solo una vez. No presentaré el formulario, comencemos haciendo clic en el formulario de inicio de sesión después de pasar la verificación.

Poner todos los códigos de inicio de sesión en una página se verá inflado, por lo que extraje la lógica de operación de inicio de sesión específica. src/utilsCree un archivo en el directorio auth.ts.

auth.ts

import {
    
     useRouteListStore } from '@/store/router'
const routeListStore = useRouteListStore()

// 登录
export async function Login(data: {
    
     username: string; password: string; portal: string; corpCode: string }) {
    
    
  const {
    
     username, password, portal, corpCode } = data
  try {
    
    
    // 登录接口
    const res = await getLogin({
    
     username, password, portal, corpCode })
    // ...
    // 这里写保存用户信息及 token 的逻辑
    // ...
    
    // 添加路由操作,写在 pinia 中,后面会说
    await routeListStore.updateRouteList()
    return res
  } catch (err) {
    
    
    return err
  }
}

A continuación, escriba la lógica específica para agregar rutas. src/storeCree un archivo en el directorio y router.tsagregue el contenido de la siguiente manera: (PD: la ruta del archivo específico debe combinarse con el contenido del proyecto específico. La ruta y el formato de menú siguientes son solo ejemplos).

De acuerdo con los diferentes métodos de procesamiento, hay dos opciones.

Solución 1: use la función asíncrona

src/tienda/router.ts

export const useRouteListStore = defineStore('routeList', {
    
    
  state: () => ({
    
    
    routeList: [],
    breadcrumb: [],
    getRouter: true // 是否需要重新加载路由
  }),
  actions: {
    
    
    // 更新菜单并追加路由
    async updateRouteList() {
    
    
      const modules = import.meta.glob('../views/**/*.vue')
      // 此为接口请求获取的菜单
      const list = await getMenus()
      list.forEach((e) => {
    
    
        e.route = e.path
        e.component = () => import('@/layout/index.vue')
        e.redirect = `${
      
      e.path}/${
      
      e.children[0].path}`
        e.children.forEach((item) => {
    
    
          item.route = `${
      
      e.path}/${
      
      item.path}`
          item.component = modules[`../views${
      
      item.component}.vue`]
        })
      })
      await addRouteList(list)
      this.getRouter = false
      this.routeList = list
      return true
    },
  }
})

Luego, escriba la lógica de agregar rutas dinámicamente y use Promise.all para asegurarse de que cuando los resultados se devuelvan en Pinia, las rutas dinámicas se hayan cargado. En src/routerel index.tsarchivo de creación, agregue lo siguiente:

src/router/index.ts

export function addRouteList(data: any[] = []) {
    
    
  return new Promise((resolve) => {
    
    
    const promises = []
    data.forEach((e) => promises.push(router.addRoute(e)))
    Promise.all(promises).then(() => resolve(true))
  })
}

Después de usar la función asíncrona, la operación de la página de inicio de sesión será muy simple.

iniciar sesión.vue

import {
    
     Login } from '@/utils/auth'

const onSubmit = () => {
    
    
  validate().then(() => {
    
    
    Login(formState).then(() => {
    
    
      router.push(routerStore.routeList[0].path)
    }).catch(err => {
    
    
      message.error(err.message)
    })
  })
}

Solución 2: utilice el sondeo

asyncEl esquema de sondeo es mucho más simple que usar la función, porque no necesita asegurarse de que la ruta esté cargada en el momento en que se obtiene el resultado después de iniciar sesión. El código de implementación específico es el siguiente:

src/tienda/router.ts

export const useRouteListStore = defineStore('routeList', {
    
    
  state: () => ({
    
    
    routeList: [],
    breadcrumb: [],
    getRouter: true
  }),
  actions: {
    
    
    // 更新菜单并追加路由
    updateRouteList() {
    
    
      listMenus().then((res) => {
    
    
        const list = res.data
        if (list === null) {
    
    
          this.getRouter = false
          router.push('/404')
          return
        }
        list.forEach((e) => {
    
    
          e.route = e.path
          e.component = () => import('@/layout/index.vue')
          e.children.forEach((item) => {
    
    
            item.route = `${
      
      e.path}/${
      
      item.path}`
            item.component = modules[`../views${
      
      item.component}.vue`]
          })
        })
        addRouteList(list)
        this.getRouter = false
        this.routeList = list
      })
  }
})

src/router/index.ts

export function addRouteList(data: any[] = []) {
    
    
  data.forEach((e) => {
    
    
    router.addRoute(e)
  })
}

La ventaja del sondeo es que la lógica es simple. El único punto problemático es agregar un temporizador después de iniciar sesión para obtener periódicamente si la ruta está cargada. La razón por la que se agrega el temporizador es que la obtención del menú es una solicitud asíncrona y el programa se ejecuta muy rápido, por lo que es necesario asegurarse de que el menú se carga cuando se ejecuta el comando de salto de enrutamiento.

iniciar sesión.vue

import {
    
     ref, onBeforeUnmount } from 'vue'
import {
    
     useRouter } from 'vue-router'
import {
    
     useRouteListStore } from '@/store/router'
const routerStore = useRouteListStore()
import {
    
     Login } from '@/utils/auth'
const router = useRouter()

// 每0.5s判断一次菜单是否加载完成,最多判断30次,超过则说明网络环境极差
const timer = ref(null)
const onSubmit = () => {
    
    
  validate().then(() => {
    
    
    Login(formState).then(() => {
    
    
    let i = 0
      timer.value = setInterval(() => {
    
    
        if (routerStore.routeList[0].path) {
    
    
          router.push(routerStore.routeList[0].path)
        }
        i++
        if (i > 30) {
    
    
          clearInterval(timer.value)
          timer.value = null
          i = null
          message.error('当前网络环境较差!')
          spinning.value = false
        }
      }, 500)
    })
  })
}

// 不要忘记清除定时器
onBeforeUnmount(() => {
    
    
  clearInterval(timer.value)
  timer.value = null
})

Reponer

El código anterior solo puede garantizar que el sistema pueda saltar a la página normalmente después del primer inicio de sesión. Si cierra sesión en la cuenta actual, inicia sesión nuevamente o cambia la cuenta para iniciar sesión, habrá un problema de carga de enrutamiento repetido, que es otro foso fácil de pisar al principio del artículo. No es difícil resolver este pozo, siempre que lo note, se puede resolver fácilmente.

La solución es agregar una guardia previa a la ruta y agregar un campo a Pinia para determinar si la ruta actual debe recargarse. El código específico es el siguiente:

import Cookies from 'js-cookie'
import {
    
     useRouteListStore } from '@/store/router'

// 前置守卫
router.beforeEach(async (to, from, next) => {
    
    
  const token = Cookies.get('token')
  if (!token) {
    
    
    next({
    
     path: '/login' })
  } else {
    
    
    const routerStore = useRouteListStore()
    routerStore.addBreadcrumb(to)
    
    // 判断菜单是否存在且是否需要重新加载
    if (routerStore.routeList.length === 0 && routerStore.getRouter) {
    
    
      await routerStore.updateRouteList()
      next({
    
     path: to.path, query: to.query })
    } else {
    
    
      next()
    }
  }
})

Si tiene alguna pregunta o puntos de vista diferentes sobre este artículo, indíquelo en el área de comentarios.

FIN

Supongo que te gusta

Origin blog.csdn.net/m0_53808238/article/details/131792975
Recomendado
Clasificación