From cac24b970603fdacfb6bd332f45dc18fc4252039 Mon Sep 17 00:00:00 2001 From: InsHomePgup <7328234@gmail.com> Date: Tue, 9 Jan 2024 15:57:51 +0800 Subject: [PATCH] feat: Add support for file-based routing (#53) --- package.json | 1 + pnpm-lock.yaml | 91 +++++++++++++++++++++++++- src/auto-imports.d.ts | 12 ++-- src/router/README.md | 4 ++ src/router/index.ts | 36 +---------- src/typed-router.d.ts | 144 ++++++++++++++++++++++++++++++++++++++++++ vite.config.ts | 12 +++- 7 files changed, 258 insertions(+), 42 deletions(-) create mode 100644 src/router/README.md create mode 100644 src/typed-router.d.ts diff --git a/package.json b/package.json index 3f55301f..97b785a3 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "unocss": "^0.58.0", "unplugin-auto-import": "^0.17.2", "unplugin-vue-components": "^0.26.0", + "unplugin-vue-router": "^0.7.0", "vite": "^5.0.10", "vite-plugin-mock-dev-server": "^1.4.3", "vite-plugin-vconsole": "^2.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cf4826c2..6cbb6775 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -133,6 +133,9 @@ devDependencies: unplugin-vue-components: specifier: ^0.26.0 version: 0.26.0(rollup@4.9.1)(vue@3.3.13) + unplugin-vue-router: + specifier: ^0.7.0 + version: 0.7.0(rollup@4.9.1)(vue-router@4.2.5)(vue@3.3.13) vite: specifier: ^5.0.10 version: 5.0.10(@types/node@20.10.5)(less@4.2.0)(terser@5.26.0) @@ -2660,6 +2663,26 @@ packages: path-browserify: 1.0.1 dev: true + /@vue-macros/common@1.10.0(rollup@4.9.1)(vue@3.3.13): + resolution: {integrity: sha512-4DZsPeQA/nBQDw2RkYAmH7KrFjJVrMdAhJhO1JCl1bbbFXCGeoGjXfkg9wHPppj47s2HpAB3GrqNwqVGbi12NQ==} + engines: {node: '>=16.14.0'} + peerDependencies: + vue: ^2.7.0 || ^3.2.25 + peerDependenciesMeta: + vue: + optional: true + dependencies: + '@babel/types': 7.23.6 + '@rollup/pluginutils': 5.1.0(rollup@4.9.1) + '@vue/compiler-sfc': 3.3.13 + ast-kit: 0.11.3(rollup@4.9.1) + local-pkg: 0.5.0 + magic-string-ast: 0.3.0 + vue: 3.3.13(typescript@5.3.3) + transitivePeerDependencies: + - rollup + dev: true + /@vue/babel-helper-vue-transform-on@1.1.5: resolution: {integrity: sha512-SgUymFpMoAyWeYWLAY+MkCK3QEROsiUnfaw5zxOVD/M64KQs8D/4oK6Q5omVA2hnvEOE0SCkH2TZxs/jnnUj7w==} dev: true @@ -2719,7 +2742,6 @@ packages: /@vue/devtools-api@6.5.1: resolution: {integrity: sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==} - dev: false /@vue/language-core@1.8.25(typescript@5.3.3): resolution: {integrity: sha512-NJk/5DnAZlpvXX8BdWmHI45bWGLViUaS3R/RMrmFSvFMSbJKuEODpM4kR0F0Ofv5SFzCWuNiMhxameWpVdQsnA==} @@ -2895,6 +2917,38 @@ packages: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} dev: true + /ast-kit@0.11.3(rollup@4.9.1): + resolution: {integrity: sha512-qdwwKEhckRk0XE22/xDdmU3v/60E8Edu4qFhgTLIhGGDs/PAJwLw9pQn8Rj99PitlbBZbYpx0k/lbir4kg0SuA==} + engines: {node: '>=16.14.0'} + dependencies: + '@babel/parser': 7.23.6 + '@rollup/pluginutils': 5.1.0(rollup@4.9.1) + pathe: 1.1.1 + transitivePeerDependencies: + - rollup + dev: true + + /ast-kit@0.9.5(rollup@4.9.1): + resolution: {integrity: sha512-kbL7ERlqjXubdDd+szuwdlQ1xUxEz9mCz1+m07ftNVStgwRb2RWw+U6oKo08PAvOishMxiqz1mlJyLl8yQx2Qg==} + engines: {node: '>=16.14.0'} + dependencies: + '@babel/parser': 7.23.6 + '@rollup/pluginutils': 5.1.0(rollup@4.9.1) + pathe: 1.1.1 + transitivePeerDependencies: + - rollup + dev: true + + /ast-walker-scope@0.5.0(rollup@4.9.1): + resolution: {integrity: sha512-NsyHMxBh4dmdEHjBo1/TBZvCKxffmZxRYhmclfu0PP6Aftre47jOHYaYaNqJcV0bxihxFXhDkzLHUwHc0ocd0Q==} + engines: {node: '>=16.14.0'} + dependencies: + '@babel/parser': 7.23.6 + ast-kit: 0.9.5(rollup@4.9.1) + transitivePeerDependencies: + - rollup + dev: true + /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: false @@ -5173,6 +5227,13 @@ packages: yallist: 4.0.0 dev: true + /magic-string-ast@0.3.0: + resolution: {integrity: sha512-0shqecEPgdFpnI3AP90epXyxZy9g6CRZ+SZ7BcqFwYmtFEnZ1jpevcV5HoyVnlDS9gCnc1UIg3Rsvp3Ci7r8OA==} + engines: {node: '>=16.14.0'} + dependencies: + magic-string: 0.30.5 + dev: true + /magic-string@0.30.5: resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} engines: {node: '>=12'} @@ -6717,6 +6778,33 @@ packages: - supports-color dev: true + /unplugin-vue-router@0.7.0(rollup@4.9.1)(vue-router@4.2.5)(vue@3.3.13): + resolution: {integrity: sha512-ddRreGq0t5vlSB7OMy4e4cfU1w2AwBQCwmvW3oP/0IHQiokzbx4hd3TpwBu3eIAFVuhX2cwNQwp1U32UybTVCw==} + peerDependencies: + vue-router: ^4.1.0 + peerDependenciesMeta: + vue-router: + optional: true + dependencies: + '@babel/types': 7.23.6 + '@rollup/pluginutils': 5.1.0(rollup@4.9.1) + '@vue-macros/common': 1.10.0(rollup@4.9.1)(vue@3.3.13) + ast-walker-scope: 0.5.0(rollup@4.9.1) + chokidar: 3.5.3 + fast-glob: 3.3.2 + json5: 2.2.3 + local-pkg: 0.4.3 + mlly: 1.4.2 + pathe: 1.1.1 + scule: 1.1.1 + unplugin: 1.5.1 + vue-router: 4.2.5(vue@3.3.13) + yaml: 2.3.4 + transitivePeerDependencies: + - rollup + - vue + dev: true + /unplugin@1.5.1: resolution: {integrity: sha512-0QkvG13z6RD+1L1FoibQqnvTwVBXvS4XSPwAyinVgoOCl2jAgwzdUKmEj05o4Lt8xwQI85Hb6mSyYkcAGwZPew==} dependencies: @@ -6985,7 +7073,6 @@ packages: dependencies: '@vue/devtools-api': 6.5.1 vue: 3.3.13(typescript@5.3.3) - dev: false /vue-template-compiler@2.7.15: resolution: {integrity: sha512-yQxjxMptBL7UAog00O8sANud99C6wJF+7kgbcwqkvA38vCGF7HWE66w0ZFnS/kX5gSoJr/PQ4/oS3Ne2pW37Og==} diff --git a/src/auto-imports.d.ts b/src/auto-imports.d.ts index 64fe7239..949508db 100644 --- a/src/auto-imports.d.ts +++ b/src/auto-imports.d.ts @@ -17,6 +17,8 @@ declare global { const customRef: typeof import('vue')['customRef'] const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] const defineComponent: typeof import('vue')['defineComponent'] + const defineLoader: typeof import('vue-router/auto')['defineLoader'] + const definePage: typeof import('unplugin-vue-router/runtime')['_definePage'] const describe: typeof import('vitest')['describe'] const effectScope: typeof import('vue')['effectScope'] const expect: typeof import('vitest')['expect'] @@ -33,8 +35,8 @@ declare global { const nextTick: typeof import('vue')['nextTick'] const onActivated: typeof import('vue')['onActivated'] const onBeforeMount: typeof import('vue')['onBeforeMount'] - const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] - const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] + const onBeforeRouteLeave: typeof import('vue-router/auto')['onBeforeRouteLeave'] + const onBeforeRouteUpdate: typeof import('vue-router/auto')['onBeforeRouteUpdate'] const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] const onDeactivated: typeof import('vue')['onDeactivated'] @@ -65,9 +67,9 @@ declare global { const useAttrs: typeof import('vue')['useAttrs'] const useCssModule: typeof import('vue')['useCssModule'] const useCssVars: typeof import('vue')['useCssVars'] - const useLink: typeof import('vue-router')['useLink'] - const useRoute: typeof import('vue-router')['useRoute'] - const useRouter: typeof import('vue-router')['useRouter'] + const useLink: typeof import('vue-router/auto')['useLink'] + const useRoute: typeof import('vue-router/auto')['useRoute'] + const useRouter: typeof import('vue-router/auto')['useRouter'] const useSlots: typeof import('vue')['useSlots'] const vi: typeof import('vitest')['vi'] const vitest: typeof import('vitest')['vitest'] diff --git a/src/router/README.md b/src/router/README.md new file mode 100644 index 00000000..9f54f64c --- /dev/null +++ b/src/router/README.md @@ -0,0 +1,4 @@ +## File-based Routing + +Routes will be auto-generated for Vue files in the **src/views** dir with the same file structure. +Check out [`unplugin-vue-router`](https://github.com/posva/unplugin-vue-router) for more details. diff --git a/src/router/index.ts b/src/router/index.ts index cc81010e..75c92797 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,44 +1,12 @@ // https://router.vuejs.org/zh/ -import { createRouter, createWebHistory } from 'vue-router' + +import { createRouter, createWebHistory } from 'vue-router/auto' import NProgress from 'nprogress' import 'nprogress/nprogress.css' -// 导入路由组件 -import main from '@/views/index.vue' -import mock from '@/views/mock/index.vue' -import charts from '@/views/charts/index.vue' -import unocss from '@/views/unocss/index.vue' - NProgress.configure({ showSpinner: true, parent: '#app' }) - -// 定义路由,每个路由都需要映射到一个组件 -const routes = [ - { - path: '/', - name: 'main', - component: main, - }, - { - path: '/mock', - name: 'mock', - component: mock, - }, - { - path: '/charts', - name: 'charts', - component: charts, - }, - { - path: '/unocss', - name: 'unocss', - component: unocss, - }, -] - -// 创建路由实例并传递 `routes` 配置 const router = createRouter({ history: createWebHistory(import.meta.env.VITE_APP_PUBLIC_PATH), - routes, }) router.beforeEach((_to, _from, next) => { diff --git a/src/typed-router.d.ts b/src/typed-router.d.ts new file mode 100644 index 00000000..32844fb4 --- /dev/null +++ b/src/typed-router.d.ts @@ -0,0 +1,144 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// Generated by unplugin-vue-router. ‼️ DO NOT MODIFY THIS FILE ‼️ +// It's recommended to commit this file. +// Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry. + +/// + +import type { + // type safe route locations + RouteLocationTypedList, + RouteLocationResolvedTypedList, + RouteLocationNormalizedTypedList, + RouteLocationNormalizedLoadedTypedList, + RouteLocationAsString, + RouteLocationAsRelativeTypedList, + RouteLocationAsPathTypedList, + + // helper types + // route definitions + RouteRecordInfo, + ParamValue, + ParamValueOneOrMore, + ParamValueZeroOrMore, + ParamValueZeroOrOne, + + // vue-router extensions + _RouterTyped, + RouterLinkTyped, + RouterLinkPropsTyped, + NavigationGuard, + UseLinkFnTyped, + + // data fetching + _DataLoader, + _DefineLoaderOptions, +} from 'unplugin-vue-router/types' + +declare module 'vue-router/auto/routes' { + export interface RouteNamedMap { + '/': RouteRecordInfo<'/', '/', Record, Record>, + '/charts/': RouteRecordInfo<'/charts/', '/charts', Record, Record>, + '/mock/': RouteRecordInfo<'/mock/', '/mock', Record, Record>, + '/unocss/': RouteRecordInfo<'/unocss/', '/unocss', Record, Record>, + } +} + +declare module 'vue-router/auto' { + import type { RouteNamedMap } from 'vue-router/auto/routes' + + export type RouterTyped = _RouterTyped + + /** + * Type safe version of `RouteLocationNormalized` (the type of `to` and `from` in navigation guards). + * Allows passing the name of the route to be passed as a generic. + */ + export type RouteLocationNormalized = RouteLocationNormalizedTypedList[Name] + + /** + * Type safe version of `RouteLocationNormalizedLoaded` (the return type of `useRoute()`). + * Allows passing the name of the route to be passed as a generic. + */ + export type RouteLocationNormalizedLoaded = RouteLocationNormalizedLoadedTypedList[Name] + + /** + * Type safe version of `RouteLocationResolved` (the returned route of `router.resolve()`). + * Allows passing the name of the route to be passed as a generic. + */ + export type RouteLocationResolved = RouteLocationResolvedTypedList[Name] + + /** + * Type safe version of `RouteLocation` . Allows passing the name of the route to be passed as a generic. + */ + export type RouteLocation = RouteLocationTypedList[Name] + + /** + * Type safe version of `RouteLocationRaw` . Allows passing the name of the route to be passed as a generic. + */ + export type RouteLocationRaw = + | RouteLocationAsString + | RouteLocationAsRelativeTypedList[Name] + | RouteLocationAsPathTypedList[Name] + + /** + * Generate a type safe params for a route location. Requires the name of the route to be passed as a generic. + */ + export type RouteParams = RouteNamedMap[Name]['params'] + /** + * Generate a type safe raw params for a route location. Requires the name of the route to be passed as a generic. + */ + export type RouteParamsRaw = RouteNamedMap[Name]['paramsRaw'] + + export function useRouter(): RouterTyped + export function useRoute(name?: Name): RouteLocationNormalizedLoadedTypedList[Name] + + export const useLink: UseLinkFnTyped + + export function onBeforeRouteLeave(guard: NavigationGuard): void + export function onBeforeRouteUpdate(guard: NavigationGuard): void + + export const RouterLink: RouterLinkTyped + export const RouterLinkProps: RouterLinkPropsTyped + + // Experimental Data Fetching + + export function defineLoader< + P extends Promise, + Name extends keyof RouteNamedMap = keyof RouteNamedMap, + isLazy extends boolean = false, + >( + name: Name, + loader: (route: RouteLocationNormalizedLoaded) => P, + options?: _DefineLoaderOptions, + ): _DataLoader, isLazy> + export function defineLoader< + P extends Promise, + isLazy extends boolean = false, + >( + loader: (route: RouteLocationNormalizedLoaded) => P, + options?: _DefineLoaderOptions, + ): _DataLoader, isLazy> + + export { + _definePage as definePage, + _HasDataLoaderMeta as HasDataLoaderMeta, + _setupDataFetchingGuard as setupDataFetchingGuard, + _stopDataFetchingScope as stopDataFetchingScope, + } from 'unplugin-vue-router/runtime' +} + +declare module 'vue-router' { + import type { RouteNamedMap } from 'vue-router/auto/routes' + + export interface TypesConfig { + beforeRouteUpdate: NavigationGuard + beforeRouteLeave: NavigationGuard + + $route: RouteLocationNormalizedLoadedTypedList[keyof RouteNamedMap] + $router: _RouterTyped + + RouterLink: RouterLinkTyped + } +} diff --git a/vite.config.ts b/vite.config.ts index 23d2d088..2807fc0c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -6,6 +6,8 @@ import type { ConfigEnv, UserConfig } from 'vite' import { visualizer } from 'rollup-plugin-visualizer' import Components from 'unplugin-vue-components/vite' import AutoImport from 'unplugin-auto-import/vite' +import VueRouter from 'unplugin-vue-router/vite' +import { VueRouterAutoImports } from 'unplugin-vue-router' import { VantResolver } from 'unplugin-vue-components/resolvers' import vue from '@vitejs/plugin-vue' @@ -28,6 +30,11 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { base: env.VITE_APP_PUBLIC_PATH, plugins: [ + VueRouter({ + routesFolder: 'src/views', + dts: 'src/typed-router.d.ts', + }), + vue(), vueJsx(), visualizer(), @@ -52,8 +59,11 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { /\.vue\?vue/, ], imports: [ + VueRouterAutoImports, + { + 'vue-router/auto': ['useLink'], + }, 'vue', - 'vue-router', 'vitest', ], dts: 'src/auto-imports.d.ts',