vue3+ts 根据路由来写生成侧边栏菜单, 总感觉 ts 用起来太别扭, 教教我正确用法

2022-12-16 11:49:38 +08:00
 xpyusrs
<template>
  <div class="sidebar">
    <div>{{ t('menu.dashboard') }}</div>
    <n-scrollbar>
      <n-menu :options="menuOptions" />
    </n-scrollbar>
  </div>
</template>

<script lang="ts" setup>
import { h, Component, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { RouteRecordRaw, RouterLink } from 'vue-router'
import { useRouter } from 'vue-router'
import { computed } from 'vue'
import { useAppStore } from '@/store'
import type { MenuOption } from 'naive-ui'
import { NIcon } from 'naive-ui'
import { DashboardOutlined as DashboardIcon } from '@vicons/antd'

const router = useRouter()

const { t, locale } = useI18n()

console.log(router.getRoutes())
const appStore = useAppStore()
const routers = computed(() => {
  return appStore.routers.filter(value => !value.meta?.hidden)
})
const menuOptions = ref<MenuOption[]>([])

const iconMap: { [index: string]: any } = {
  Dashboard: DashboardIcon
}

function renderIcon(icon: Component) {
  return () => h(NIcon, null, { default: () => h(icon) })
}

const genMenu = () => {
  menuOptions.value = []
  for (const item of routers.value) {
    const children = []
    for (const item2 of item?.children || []) {
      children.push({
        label: () =>
          h(
            RouterLink,
            {
              to: {
                name: item2.name
              }
            },
            { default: () => t(item2.meta?.locale as string) }
          ),
        key: item2.name as string
      })
    }
    const menu: MenuOption = {
      key: item.name as string,
      label: t(item.meta?.locale as string),
      icon: renderIcon(iconMap[item.name as string]),
      children
    }
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    menuOptions.value.push(menu)
  }
}

genMenu()

watch(
  () => locale.value,
  () => {
    genMenu()
  }
)
</script>

vue-router 的类型 RouteRecordRaw, 一个属性有好几种类型, 然后用起来 ts 总是报类型不匹配, 用了大量 as string 来解决报错, 好难受, 我之前都是写 js, 可能对 ts 理解有误, 怎么才能优雅的使用, 有个报错说我嵌套太深, 还不得不用 // @ts-ignore 来解决

2189 次点击
所在节点    Vue.js
13 条回复
gogogo2000
2022-12-16 12:17:47 +08:00
vue 对 ts 的兼容性一向来都不太好,这也是 react 的重大优势之一,有时候没办法只能 as any……
wunonglin
2022-12-16 12:39:39 +08:00
你 routers 的类型推断出来了吗?还是什么问题单单代码看不出,你去 stackblitz.com 写个例子。
xpyusrs
2022-12-16 12:39:44 +08:00
@gogogo2000 用 react 就没有这个问题了嘛? 如果这样的话我可以转 react
xpyusrs
2022-12-16 12:49:25 +08:00
anguiao
2022-12-16 13:38:17 +08:00
路由的元信息是可以预先声明的呀,文档里面有写。
https://router.vuejs.org/zh/guide/advanced/meta.html#typescript
chengxy
2022-12-16 13:42:21 +08:00
你用法不对,你的 routers 类型应该和 MenuOption 类型对上的,RouteRecordRaw 里面的参数你可以使用重写功能去重写。
chengxy
2022-12-16 13:47:28 +08:00
@gogogo2000 #1 都 24 年了,现在完全不是问题了
xpyusrs
2022-12-16 14:05:25 +08:00
@chengxy 重写能给个示例么, 我好模仿一下
chengxy
2022-12-16 14:07:02 +08:00
@xpyusrs #8 5 楼给了
ZoeeoZ
2022-12-16 14:47:19 +08:00
@xpyusrs ts 官方为 react 做了兼容..你说呢,所以 react+ts 才好用
ccyu220
2022-12-16 15:32:25 +08:00
我建议 menu 这种,你直接换成 tsx 来写。
wunonglin
2022-12-16 17:40:40 +08:00
@xpyusrs #4

噢噢。router meta 这个问题在 vue2 时代也是这样,只能类型覆盖。

虽然但是,不建议直接把路由当做 navmenu 以及把路由表引到 ui 层使用,路由归路由,menu 归 menu 。这是个很标准的错误用法,虽然可以这么用,但是建议不。
Bingchunmoli
2022-12-16 20:25:54 +08:00
没有 vue3 的一些代码示例,,pinia 什么的 ts 都不太会用

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/902903

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX