> 웹 프론트엔드 > View.js > Vue3에서 동적 메뉴를 로드하는 방법

Vue3에서 동적 메뉴를 로드하는 방법

PHPz
풀어 주다: 2023-05-10 13:58:06
앞으로
2133명이 탐색했습니다.

1. 전체적인 아이디어

우선 전체적인 구현 아이디어를 정리해보자. 전체적인 아이디어는 vhr과 똑같다.

일부 친구들이 vhr의 프런트 엔드 동적 메뉴 구현 아이디어를 잊어버렸을 수도 있다는 점을 고려하여 이 기사에서는 이를 여러분과 함께 분석해 보겠습니다.

모든 .vue 파일에서 메뉴 데이터에 액세스할 수 있도록 하기 위해 vuex는 vue에 데이터를 저장하는 일반적인 장소입니다. >.vue 파일은 vuex에서 데이터를 읽을 수 있습니다. vuex에 저장된 데이터는 기본적으로 메모리에 저장되기 때문에 브라우저가 F5를 눌러 새로 고침을 하면 해당 데이터가 사라지는 특징이 있습니다. 따라서 페이지 점프가 발생하면 사용자가 페이지의 메뉴 버튼을 클릭한 후에 페이지 점프가 발생하는지, 아니면 사용자가 브라우저 새로 고침 버튼을 클릭(또는 F5 키를 누른 후)되는지 페이지 점프가 발생하는지 구별해야 합니다. .vue 文件中都能访问到到菜单数据,所以选择将菜单数据存入 vuex 中,vuex 是 vue 中一个存储数据的公共地方,所有的 .vue 文件都可以从 vuex 中读取到数据。存储在 vuex 中的数据本质上是存在内存中,所以它有一个特点,就是浏览器按 F5 刷新之后,数据就没了。所以在发生页面的跳转的时候,我们应该去区分一下,是用户点击了页面上的菜单按钮之后发生了页面跳转还是用户点击了浏览器刷新按钮(或者按了 F5)发生了跳转。

为了实现这一点,我们需要用到 vue 中的路由导航守卫功能,对于我们 Java 工程师而言,这些可能听起来有点陌生,但是你把它当作 Java 中的 Filter 来看待就好理解了,实际上我们视频中和小伙伴们讲解的时候就是这么类比的,将一个新事物跟我们脑海中一个已有的熟悉的事物进行类比,就很容易理解了。

vue 中的导航守卫就类似一个监控,它可以监控到所有的页面跳转,在页面跳转中,我们可以去判断一下 vuex 中的菜单数据是否还在,如果还在,就说明用户是点击了页面上的菜单按钮完成了跳转的,如果不在,就说明用户是点击了浏览器的刷新按钮或者是按了 F5 进行页面刷新的,此时我们就要赶紧去服务端重新加载一下菜单数据。

---xxxxxxxxxxxxxxxxxx---

整体上的实现思路就是这样,接下来我们来看看一些具体的实现细节。

2. 实现细节

2.1 加载细节

首先我们来看看加载的细节。

小伙伴们知道,单页面项目的入口是 main.js,路由加载的内容在 src/permission.js 文件中,该文件在 main.js 中被引入,src/permission.js 中的前置导航守卫内容如下:

router.beforeEach((to, from, next) => {
  NProgress.start()
  if (getToken()) {
    to.meta.title && useSettingsStore().setTitle(to.meta.title)
    /* has token*/
    if (to.path === '/login') {
      next({ path: '/' })
      NProgress.done()
    } else {
      if (useUserStore().roles.length === 0) {
        isRelogin.show = true
        // 判断当前用户是否已拉取完user_info信息
        useUserStore().getInfo().then(() => {
          isRelogin.show = false
          usePermissionStore().generateRoutes().then(accessRoutes => {
            // 根据roles权限生成可访问的路由表
            accessRoutes.forEach(route => {
              if (!isHttp(route.path)) {
                router.addRoute(route) // 动态添加可访问路由表
              }
            })
            next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
          })
        }).catch(err => {
          useUserStore().logOut().then(() => {
            ElMessage.error(err)
            next({ path: '/' })
          })
        })
      } else {
        next()
      }
    }
  } else {
    // 没有token
    if (whiteList.indexOf(to.path) !== -1) {
      // 在免登录白名单,直接进入
      next()
    } else {
      next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
      NProgress.done()
    }
  }
})
로그인 후 복사

我跟大家捋一下这个前置导航守卫中的思路:

  • 首先调用 getToken 方法,这个方法实际上是去 Cookie 中拿认证 Token,也就是登录成功后后端返回给前端的那个 JWT 字符串。

  • 如果 getToken 方法有返回值,说明用户已经登录了,那么进入到 if 分支中,如果 getToken 没拿到值,说明用户未登录,未登录的话,又分为两种情况:i:访问的目标地址处于免登录白名单中,那么此时直接访问即可;ii:访问的目标地址不在白名单中,那么此时就跳转到登录页面去,跳转的时候同时携带一个 redirect 参数,这样方便在登录成功之后,再跳转回访问的目标页面。这个免登录访问的白名单,是一个在 src/permission.js 文件中定义的变量,默认有四个路径,分别是 ['/login', '/auth-redirect', '/bind', '/register']

  • 如果 getToken 拿到了值,说明用户已经登录了,此时又分情况:如果用户访问的路径是登录页面,那么就给他重定向到项目首页(也就是在已经登录的情况下,不允许用户再次访问登录页面);如果用户访问的路径不是登录页面,那么首先判断 vuex 中的 roles 是否还有值?如果有值,说明当前就是用户点击了一个菜单按钮进行跳转的,那么直接跳转就行了;如果没有值,说明用户是按了浏览器的刷新按钮或者是 F5 按钮刷新进行的页面跳转,那么此时首先调用 getInfo 方法(位于 src/store/modules/user.js 文件中)去服务端重新加载当前用户的基本信息、角色信息以及权限信息,然后再调用 generateRoutes 方法(位于 src/store/modules/permission.js 文件中)去服务端加载路由信息,并将加载到的路由信息放入到 router 对象中(前提是这个路由对象不是一个 http 链接,就是普通的路由地址)。

这就是动态路由的加载整体思路。

在第三步骤中,涉及到两个方法,一个是 getInfo 还有一个 generateRoutes,这两个方法也都比较关键,我们再来稍微看下。

2.2 getInfo

首先这个加载用户信息的方法位于 src/store/modules/user.js

이를 달성하려면 vue에서 경로 탐색 가드 기능을 사용해야 합니다. 우리 Java 엔지니어에게는 다소 생소하게 들릴 수도 있지만 Java의 필터라고 생각하면 이해하기 쉽습니다. 실제로 우리는 영상에서 친구들에게 설명할 때 이 비유를 사용합니다. 새로운 것을 우리 마음 속에 이미 익숙한 것과 비교하면 이해하기 쉽습니다.

vue의 네비게이션 가드는 모든 페이지 점프를 모니터링할 수 있으며, vuex의 메뉴 데이터가 여전히 존재하는지 여부를 판단할 수 있습니다. 페이지의 메뉴 버튼이 점프를 완료했다면 이는 사용자가 브라우저의 새로 고침 버튼을 클릭했거나 페이지를 새로 고치기 위해 F5를 눌렀다는 의미입니다. 이때 메뉴를 다시 로드하려면 빠르게 서버로 이동해야 합니다. . 🎜🎜---xxxxxxxxxxxxxxxxxxxxx---🎜🎜전체 구현 아이디어는 다음과 같습니다. 다음으로 구체적인 구현 세부 사항을 살펴보겠습니다. 🎜🎜2. 구현 세부사항🎜

2.1 로드 세부사항

🎜먼저 로드 세부사항을 살펴보겠습니다. 🎜🎜친구들은 단일 페이지 프로젝트의 입구가 main.js이고 해당 경로로 로드된 콘텐츠가 src/permission.js 파일에 있다는 것을 알고 있습니다. 이 파일은 main.js에 소개되어 있습니다. , src/permission .js의 전면 네비게이션 가드 내용은 다음과 같습니다: 🎜
getInfo() {
  return new Promise((resolve, reject) => {
    getInfo().then(res => {
      const user = res.user
      const avatar = (user.avatar == "" || user.avatar == null) ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;
      if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
        this.roles = res.roles
        this.permissions = res.permissions
      } else {
        this.roles = ['ROLE_DEFAULT']
      }
      this.name = user.userName
      this.avatar = avatar;
      resolve(res)
    }).catch(error => {
      reject(error)
    })
  })
},
로그인 후 복사
🎜 이 전면 네비게이션 가드의 아이디어를 안내해 드리겠습니다: 🎜
  • 🎜먼저 getToken 메소드를 호출하세요. 이 메소드는 실제로 로그인 성공 후 백엔드가 프런트엔드에 반환하는 JWT 문자열인 쿠키에서 인증 토큰을 가져옵니다. 🎜
  • 🎜 getToken 메소드에 반환 값이 있으면 사용자가 로그인했다는 뜻이고, getToken이 값을 얻지 못하면 사용자가 로그인하지 않았다는 의미입니다. 그렇지 않은 경우 상황은 두 가지로 나뉩니다. i: 접속한 대상 주소가 로그인이 필요 없는 화이트리스트에 있으므로 이때 직접 접속할 수 있습니다. ii: 접속한 대상 주소가 화이트리스트에 없습니다. 이때 로그인 페이지로 이동하게 됩니다. 점프할 때 리디렉션 매개변수를 전달하면 로그인 성공 후 대상 페이지로 더 쉽게 이동할 수 있습니다. 로그인 없는 액세스를 위한 이 화이트리스트는 src/permission.js 파일에 정의된 변수입니다. 기본 경로는 ['/login', '/auth-redirect', '/bind' , '/ 입니다. 등록']. 🎜
  • 🎜 getToken이 값을 받으면 사용자가 로그인했다는 의미입니다. 이때는 상황이 다릅니다. 사용자가 액세스하는 경로가 로그인 페이지인 경우 프로젝트로 리디렉션합니다. 홈페이지(즉, 로그인한 후) 로그인의 경우 사용자가 로그인 페이지에 다시 액세스할 수 없습니다. 사용자가 액세스하는 경로가 로그인 페이지가 아닌 경우 먼저 해당 역할이 있는지 확인합니다. vuex에는 여전히 가치가 있습니까? 값이 있으면 사용자가 메뉴 버튼을 눌러 점프했다는 의미이므로 바로 점프한다는 의미이고, 값이 없으면 사용자가 브라우저의 새로 고침 버튼이나 F5 버튼을 눌러 페이지를 새로 고침했다는 의미입니다. getInfo 메소드(src/store/modules/user.js 파일에 위치)를 호출하여 현재 사용자의 기본 정보, 역할 정보, 권한 정보를 서버에 다시 로드한 후 generateRoutes 메소드(src/store/modules에 위치)를 호출합니다. /permission.js 파일)을 서버에 전송하여 라우팅 정보를 로드하고, 로드된 라우팅 정보를 라우터 객체에 넣습니다(이 라우팅 객체가 http 링크가 아니라 일반 라우팅 주소인 경우). 🎜
🎜동적 라우팅을 로드하는 전체적인 아이디어입니다. 🎜🎜세 번째 단계에는 두 가지 방법이 포함됩니다. 하나는 getInfo이고 다른 하나는 generateRoutes입니다. 🎜

2.2 getInfo

🎜우선 이 사용자 정보를 불러오는 방법은 src/store/modules/user.js 파일, 즉 기본 뒤에 위치합니다. 이러한 사용자의 정보가 로드되면 브라우저를 새로 고치면 vuex에 저장된 데이터가 손실됩니다. 🎜
{
    "permissions":[
        "*:*:*"
    ],
    "roles":[
        "admin"
    ],
    "user":
        "userName":"admin",
        "nickName":"TienChin健身",
        "avatar":"",
    }
}
로그인 후 복사
로그인 후 복사
🎜 메소드의 논리는 실제로 서버에서 반환된 JSON 형식과 결합되어야 합니다. 이해하기 쉬워야 합니다(JSON의 일부): 🎜
{
    "permissions":[
        "*:*:*"
    ],
    "roles":[
        "admin"
    ],
    "user":
        "userName":"admin",
        "nickName":"TienChin健身",
        "avatar":"",
    }
}
로그인 후 복사
로그인 후 복사

另外再强调下,之前在 vhr 中,我们是将请求封装成了一个 api.js 文件,里边有常用的 get、post、put 以及 delete 请求等,然后在需要使用的地方,直接去调用这些方法发送请求即可,但是在 TienChin 中,脚手架的封装是将所有的请求都提前统一封装好,在需要的时候直接调用封装好的方法,连请求地址都不用传递了(封装的时候就已经写死了),所以小伙伴们看上面的 getInfo 方法只有方法调用,没有传递路径参数等。

2.3 generateRoutes

generateRoutes 方法则位于 src/store/modules/permission.js 文件中,这里值得说道的地方就比较多了:

generateRoutes(roles) {
  return new Promise(resolve => {
    // 向后端请求路由数据
    getRouters().then(res => {
      const sdata = JSON.parse(JSON.stringify(res.data))
      const rdata = JSON.parse(JSON.stringify(res.data))
      const defaultData = JSON.parse(JSON.stringify(res.data))
      const sidebarRoutes = filterAsyncRouter(sdata)
      const rewriteRoutes = filterAsyncRouter(rdata, false, true)
      const defaultRoutes = filterAsyncRouter(defaultData)
      const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
      asyncRoutes.forEach(route => { router.addRoute(route) })
      this.setRoutes(rewriteRoutes)
      this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
      this.setDefaultRoutes(sidebarRoutes)
      this.setTopbarRoutes(defaultRoutes)
      resolve(rewriteRoutes)
    })
  })
}
로그인 후 복사

首先大家看到,服务端返回的动态菜单数据解析了三次,分别拿到了三个对象,这三个对象都是将来要用的,只不过使用的场景不同,下面结合页面的显示跟大家细说。

  • 首先是调用 filterAsyncRouter 方法,这个方法的核心作用就是将服务端返回的 component 组件动态加载为一个 component 对象。不过这个方法在调用的过程中,后面还有两个参数,第二个是 lastRouter 在该方法中并无实质性作用;第三个参数则主要是说是否需要对 children 的 path 进行重写。小伙伴们知道,服务端返回的动态菜单的 path 属性都是只有一层的,例如一级菜单系统管理的 path 是 system,二级菜单用户管理的 path 则是 user,那么用户管理最终访问的 path 就是 system/path,如果第三个参数为 true,则会进行 path 的重写,将 path 最终设置正确。

  • 所以这里的 sidebarRoutes 和 defaultRoutes 只是能用于菜单渲染(因为这两个里边的菜单 path 不对),而最终的页面跳转要通过 rewriteRoutes 才可以实现。

  • 除了服务端返回的动态菜单,前端本身也定义了一些基础菜单,前端的基础菜单分为两大类,分别是 constantRoutes 和 dynamicRoutes,其中 constantRoutes 是固定菜单,也就是一些跟用户权限无关的菜单,例如 404 页面、首页等;dynamicRoutes 是动态菜单,也就是也根据用户权限来决定是否展示的菜单,例如分配用户、字典数据、调度日志等等。

  • filterDynamicRoutes 方法则是将前端提前定义好的 dynamicRoutes 菜单进行过滤,找出那些符合当前用户权限的菜单将之添加到路由中(这些菜单都不需要在菜单栏渲染出来)。

  • 接下来涉及到四个不同的保存路由数据的变量,分别是 routes、addRoutes(经松哥分析,这个变量并无实际作用,可以删除之)、defaultRoutes、topbarRouters 以及 sidebarRouters,四个路由变量的作用各有不同:

routes:

routes 中保存的是 constantRoutes 以及服务端返回的动态路由数据,并且这个动态路由数据中的 path 已经完成了重写,所以这个 routes 主要用在两个地方:

首页的搜索上:首页的搜索也可以按照路径去搜索,所以需要用到这个 routes,如下图:

Vue3에서 동적 메뉴를 로드하는 방법

用在 TagsView,这个地方也需要根据页面渲染不同的菜单,也是用的 routes:

Vue3에서 동적 메뉴를 로드하는 방법

sidebarRouters:

这个就是大家所熟知的侧边栏菜单了,具体展示是 constantRoutes+服务端返回的菜单,不过这些 constantRoutes 基本上 hidden 属性都是 false,渲染的时候是不会被渲染出来的。

Vue3에서 동적 메뉴를 로드하는 방법

topbarRouters:

这个是用在 TopNav 组件中,这个是将系统的一级菜单在头部显示出来的,如下图:

Vue3에서 동적 메뉴를 로드하는 방법

一级菜单在顶部显示,左边显示的都是二级三级菜单,那么顶部菜单的渲染,用的就是这个 topbarRouters。

defaultRoutes:

想要开启顶部菜单,需要在 src/layout/components/Settings/index.vue 组件中设置,如下图:

Vue3에서 동적 메뉴를 로드하는 방법

开启顶部菜单之后,点击顶部菜单,左边菜单栏会跟着切换,此时就是从 defaultRoutes 中遍历出相关的菜单设置给 sidebarRouters。

네, 이것이 바로 이 네 가지 경로 변수의 역할입니다. 솔직히 말해서 이 스캐폴딩 부분의 코드 디자인은 약간 혼란스럽습니다. 송 형제가 최적화하는 데 시간을 투자할 필요는 없습니다. 모두를위한.

generateRoutes 메서드는 결국 앞서 언급한 전면 네비게이션 가드에 rewriteRoutes 변수를 반환하고, 마지막으로 전면 네비게이션 가드가 라우터에 데이터를 추가합니다.

메뉴 렌더링은 src/layout/comComponents/Sidebar/index.vue에서 하고 있으니 보시면 정상적인 동작이라고 할 수 없습니다.

위 내용은 Vue3에서 동적 메뉴를 로드하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:yisu.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿