@virou/core 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +335 -0
- package/dist/index.d.mts +87 -79
- package/dist/index.mjs +136 -124
- package/package.json +9 -20
- package/dist/index.cjs +0 -175
- package/dist/index.d.cts +0 -99
- package/dist/index.d.ts +0 -99
package/README.md
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
# 🧭 Virou
|
|
2
|
+
|
|
3
|
+
<p>
|
|
4
|
+
<a href="https://www.npmjs.com/package/@virou/core"><img src="https://img.shields.io/npm/v/@virou/core.svg?style=flat&colorA=18181B&colorB=39737d" alt="Version"></a>
|
|
5
|
+
<a href="https://www.npmjs.com/package/@virou/core"><img src="https://img.shields.io/npm/dm/@virou/core.svg?style=flat&colorA=18181B&colorB=39737d" alt="Downloads"></a>
|
|
6
|
+
<a href="https://bundlephobia.com/package/@virou/core"><img src="https://img.shields.io/bundlephobia/min/@virou/core?style=flat&colorA=18181B&colorB=39737d" alt="Bundle Size"></a>
|
|
7
|
+
<a href="https://github.com/tankosinn/virou/tree/main/LICENSE"><img src="https://img.shields.io/github/license/tankosinn/virou.svg?style=flat&colorA=18181B&colorB=39737d" alt="License"></a>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
Virou is a high-performance, lightweight virtual router for Vue with dynamic routing capabilities.
|
|
11
|
+
|
|
12
|
+
> Perfect for modals, wizards, embeddable widgets, or any scenario requiring routing without altering the browser's URL or history.
|
|
13
|
+
|
|
14
|
+
## ✨ Features
|
|
15
|
+
|
|
16
|
+
- 🪄 **Dynamic Virtual Routing**: Navigate without altering the browser's URL or history
|
|
17
|
+
- 🍂 **Multiple Router Instances**: Manage independent routing contexts within the same app
|
|
18
|
+
- 🪆 **Nested Routing**: Seamlessly handle complex, nested routes
|
|
19
|
+
- 🦾 **Type-Safe**: Written in [TypeScript](https://www.typescriptlang.org/)
|
|
20
|
+
- ⚡ **SSR-Friendly**: Compatible with server-side rendering
|
|
21
|
+
|
|
22
|
+
## 🐣 Usage
|
|
23
|
+
```vue
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
import { useVRouter } from '@virou/core'
|
|
26
|
+
|
|
27
|
+
// Define your routes
|
|
28
|
+
const routes = [
|
|
29
|
+
{
|
|
30
|
+
path: '/',
|
|
31
|
+
component: () => import('./views/Home.vue'),
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
path: '/about',
|
|
35
|
+
component: () => import('./views/About.vue'),
|
|
36
|
+
},
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
// Create a virtual router instance
|
|
40
|
+
const { router, route } = useVRouter(routes)
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<template>
|
|
44
|
+
<!-- Renders the current route's component -->
|
|
45
|
+
<VRouterView />
|
|
46
|
+
</template>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 📦 Installation
|
|
50
|
+
|
|
51
|
+
Install Virou with your package manager:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pnpm add @virou/core
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Register the `virou` plugin in your Vue app:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { virou } from '@virou/core'
|
|
61
|
+
import { createApp } from 'vue'
|
|
62
|
+
import App from './App.vue'
|
|
63
|
+
|
|
64
|
+
createApp(App)
|
|
65
|
+
.use(virou)
|
|
66
|
+
.mount('#app')
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 🧩 Using with Nuxt
|
|
70
|
+
|
|
71
|
+
Install Virou Nuxt module with your package manager:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pnpm add @virou/nuxt
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Add the module to your Nuxt configuration:
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// nuxt.config.ts
|
|
81
|
+
export default defineNuxtConfig({
|
|
82
|
+
modules: [
|
|
83
|
+
'@virou/nuxt',
|
|
84
|
+
],
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 🧱 Essentials
|
|
89
|
+
|
|
90
|
+
### 🌿 Routes
|
|
91
|
+
|
|
92
|
+
Declare your routes as an array of objects with required `path` and `component`, and optional `meta` and `children` properties.
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
const routes: VRouterRaw[] = [
|
|
96
|
+
{
|
|
97
|
+
path: '/', // static path
|
|
98
|
+
component: Home,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
path: '/user/:id', // dynamic path with parameter
|
|
102
|
+
component: () => import('./views/User.vue'),
|
|
103
|
+
meta: {
|
|
104
|
+
foo: 'bar',
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
path: '/**:notFound', // Named wildcard path
|
|
109
|
+
component: defineAsyncComponent(() => import('./views/NotFound.vue')),
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Props**:
|
|
115
|
+
- `path`: the URL pattern to match. Supports:
|
|
116
|
+
- Static ("/about") for exact matches
|
|
117
|
+
- Dynamic ("/user/:id") for named parameters
|
|
118
|
+
- Wildcard ("/**") for catch-all segments
|
|
119
|
+
- Named wildcard ("/**:notFound") for catch-all with a name
|
|
120
|
+
- `component`: the Vue component to render when this route matches. Can be synchronous or an async loader.
|
|
121
|
+
- `meta`: metadata for the route.
|
|
122
|
+
- `children`: an array of child routes.
|
|
123
|
+
|
|
124
|
+
> Virou uses [rou3](https://github.com/h3js/rou3) under the hood for create router context and route matching.
|
|
125
|
+
|
|
126
|
+
### 🪆 Nested Routes
|
|
127
|
+
|
|
128
|
+
To define nested routes, add a children array to a route record. Child path values are relative to their parent (leading `/` is ignored).
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
const routes: VRouterRaw[] = [
|
|
132
|
+
// ...
|
|
133
|
+
{
|
|
134
|
+
path: '/user/:id',
|
|
135
|
+
component: User,
|
|
136
|
+
children: [
|
|
137
|
+
{
|
|
138
|
+
path: '', // /user/:id -> default child route
|
|
139
|
+
component: UserProfile,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
path: '/settings', // /user/:id/settings
|
|
143
|
+
component: UserSettings,
|
|
144
|
+
children: [
|
|
145
|
+
{
|
|
146
|
+
path: '/', // /user/:id/settings -> deep default child route
|
|
147
|
+
component: UserSettingsGeneral,
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
path: '/notifications', // /user/:id/settings/notifications
|
|
151
|
+
component: UserSettingsNotifications,
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
]
|
|
156
|
+
},
|
|
157
|
+
// ...
|
|
158
|
+
]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 🌳 Router
|
|
162
|
+
|
|
163
|
+
Create (or access) a virtual router instance with `useVRouter` composable.
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
const { router, route } = useVRouter('my-wizard', routes)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
> `useVRouter` must be called inside `setup()`.
|
|
170
|
+
|
|
171
|
+
**Params:**
|
|
172
|
+
- `key`: a unique key for the router instance. If you do not provide a key, Virou will generate one via `useId()`.
|
|
173
|
+
- `routes`: an array of route objects.
|
|
174
|
+
- `options`:
|
|
175
|
+
- `initialPath`: the path to render on initialization (defaults to `/`).
|
|
176
|
+
|
|
177
|
+
**Returns:**
|
|
178
|
+
- `route`: a Vue shallow ref that contains the current route object.
|
|
179
|
+
```typescript
|
|
180
|
+
export interface VRoute {
|
|
181
|
+
fullPath: string
|
|
182
|
+
path: string
|
|
183
|
+
search: string
|
|
184
|
+
hash: string
|
|
185
|
+
meta?: Record<PropertyKey, unknown>
|
|
186
|
+
params?: Record<string, string>
|
|
187
|
+
_renderList: Component[] | null
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
- `router`:
|
|
191
|
+
- `replace(path: string): void`: navigate to a new path.
|
|
192
|
+
- `addRoute(route: VRouteRaw): void`: add a route at runtime.
|
|
193
|
+
|
|
194
|
+
#### 🍃 Multiple Router Instances
|
|
195
|
+
|
|
196
|
+
Virou allows you to create multiple independent router instances within the same app.
|
|
197
|
+
|
|
198
|
+
```vue
|
|
199
|
+
<script setup lang="ts">
|
|
200
|
+
// Settings modal router
|
|
201
|
+
useVRouter('settings-modal', [
|
|
202
|
+
{
|
|
203
|
+
path: '/profile',
|
|
204
|
+
component: UserProfile,
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
path: '/preferences',
|
|
208
|
+
component: UserPreferences,
|
|
209
|
+
},
|
|
210
|
+
// ...
|
|
211
|
+
], { initialPath: '/profile' })
|
|
212
|
+
|
|
213
|
+
// Onboarding wizard router
|
|
214
|
+
useVRouter('onboarding-wizard', [
|
|
215
|
+
{
|
|
216
|
+
path: '/profile',
|
|
217
|
+
component: OnboardingProfile,
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
path: '/teamspace',
|
|
221
|
+
component: OnboardingTeamspace,
|
|
222
|
+
},
|
|
223
|
+
// ...
|
|
224
|
+
], { initialPath: '/profile' })
|
|
225
|
+
</script>
|
|
226
|
+
|
|
227
|
+
<template>
|
|
228
|
+
<SettingsModal>
|
|
229
|
+
<VRouterView router-key="settings-modal" />
|
|
230
|
+
</SettingsModal>
|
|
231
|
+
|
|
232
|
+
<Wizard>
|
|
233
|
+
<VRouterView router-key="onboarding-wizard" />
|
|
234
|
+
</Wizard>
|
|
235
|
+
</template>
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## 📺 Rendering
|
|
239
|
+
|
|
240
|
+
`<VRouterView>` mounts the matched component at its current nesting depth.
|
|
241
|
+
|
|
242
|
+
```vue
|
|
243
|
+
<template>
|
|
244
|
+
<VRouterView router-key="my-router">
|
|
245
|
+
:keep-alive="true" <!-- preserve component state -->
|
|
246
|
+
:view-key="(route, key) => `${key}|${route.fullPath}`" <!-- custom vnode key -->
|
|
247
|
+
>
|
|
248
|
+
<!-- default slot receives { Component, route } -->
|
|
249
|
+
<template #default="{ Component, route }">
|
|
250
|
+
<Transition name="fade" mode="out-in">
|
|
251
|
+
<component :is="Component" v-bind="route.params" />
|
|
252
|
+
</Transition>
|
|
253
|
+
</template>
|
|
254
|
+
|
|
255
|
+
<!-- fallback slot shown while async loader resolves -->
|
|
256
|
+
<template #fallback>
|
|
257
|
+
<div class="spinner">
|
|
258
|
+
Loading...
|
|
259
|
+
</div>
|
|
260
|
+
</template>
|
|
261
|
+
</VRouterView>
|
|
262
|
+
</template>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Props:**
|
|
266
|
+
- `routerKey`: key of the router instance to render. If not provided, uses the nearest router instance.
|
|
267
|
+
- `keepAlive`: wraps the rendered component in `<KeepAlive>` when set to true, preserving its state across navigations.
|
|
268
|
+
- `viewKey`: accepts either a string or a function `(route, key) => string` to compute the vnode key for the rendered component.
|
|
269
|
+
|
|
270
|
+
**Slots:**
|
|
271
|
+
- `default`: slot receives `{ Component, route }` so you can wrap or decorate the active component.
|
|
272
|
+
- `fallback`: receives `{ route }` and is displayed inside `<Suspense>` while an async component is resolving.
|
|
273
|
+
|
|
274
|
+
> Virou wraps components in `<Suspense>` by default. To combine `<Suspense>` with other components, see the [Vue docs](https://vuejs.org/guide/built-ins/suspense#combining-with-other-components).
|
|
275
|
+
|
|
276
|
+
## 🛠️ Advanced
|
|
277
|
+
|
|
278
|
+
### 🌐 Global Routers
|
|
279
|
+
|
|
280
|
+
By default, routers created with `useVRouter(key, routes)` are disposable—they unregister themselves automatically once no components reference them.
|
|
281
|
+
|
|
282
|
+
To keep a router alive for your app’s entire lifecycle, register it as a global router.
|
|
283
|
+
|
|
284
|
+
#### Plugin-Registered Globals
|
|
285
|
+
|
|
286
|
+
Defined routers in the `virou` plugin options are registered as global routers:
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
createApp(App)
|
|
290
|
+
.use(virou, {
|
|
291
|
+
routers: {
|
|
292
|
+
'embedded-widget-app': {
|
|
293
|
+
routes: [
|
|
294
|
+
{ path: '/chat', component: () => import('./views/Chat.vue') },
|
|
295
|
+
{ path: '/settings', component: () => import('./views/Settings.vue') },
|
|
296
|
+
],
|
|
297
|
+
options: { initialPath: '/chat' }
|
|
298
|
+
},
|
|
299
|
+
// add more global routers here...
|
|
300
|
+
}
|
|
301
|
+
})
|
|
302
|
+
.mount('#app')
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Later:
|
|
306
|
+
|
|
307
|
+
```ts
|
|
308
|
+
const { router, route } = useVRouter('embedded-widget-app')
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
#### Runtime-Registered Globals
|
|
312
|
+
|
|
313
|
+
You may also mark a router as global at runtime by passing the `_isGlobal` option:
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
useVRouter(routes, { _isGlobal: true })
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
That router will stay registered even after components that use it unmount.
|
|
320
|
+
|
|
321
|
+
### 🧪 Create Standalone Virtual Router
|
|
322
|
+
|
|
323
|
+
You can create a standalone virtual router with `createVRouter`:
|
|
324
|
+
|
|
325
|
+
```ts
|
|
326
|
+
export function useCustomRouter() {
|
|
327
|
+
const { router, route } = createVRouter(routes)
|
|
328
|
+
|
|
329
|
+
// Custom logic here...
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## 📝 License
|
|
334
|
+
|
|
335
|
+
[MIT License](https://github.com/tankosinn/virou/blob/main/LICENSE)
|
package/dist/index.d.mts
CHANGED
|
@@ -1,99 +1,107 @@
|
|
|
1
|
-
import * as vue from 'vue';
|
|
2
|
-
import { Component, AsyncComponentLoader, ComputedRef } from 'vue';
|
|
3
1
|
import { RouterContext } from 'rou3';
|
|
2
|
+
import * as vue from 'vue';
|
|
3
|
+
import { Component, DefineComponent, Ref, ShallowRef, PropType, SlotsType, VNodeChild, Plugin } from 'vue';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
type: StringConstructor;
|
|
12
|
-
default: undefined;
|
|
13
|
-
};
|
|
14
|
-
keepalive: {
|
|
15
|
-
type: BooleanConstructor;
|
|
16
|
-
default: boolean;
|
|
17
|
-
};
|
|
18
|
-
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
|
|
19
|
-
[key: string]: any;
|
|
20
|
-
}> | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
21
|
-
routerKey: {
|
|
22
|
-
type: StringConstructor;
|
|
23
|
-
default: undefined;
|
|
24
|
-
};
|
|
25
|
-
viewKey: {
|
|
26
|
-
type: StringConstructor;
|
|
27
|
-
default: undefined;
|
|
28
|
-
};
|
|
29
|
-
keepalive: {
|
|
30
|
-
type: BooleanConstructor;
|
|
31
|
-
default: boolean;
|
|
32
|
-
};
|
|
33
|
-
}>> & Readonly<{}>, {
|
|
34
|
-
routerKey: string;
|
|
35
|
-
viewKey: string;
|
|
36
|
-
keepalive: boolean;
|
|
37
|
-
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
38
|
-
|
|
39
|
-
type VRouteTreeNode = [key: string, children?: VRouteTreeNode[]];
|
|
40
|
-
type VRouteViews = Record<string, Component[]>;
|
|
41
|
-
type VRouteKeys = Record<string, Array<string | null>>;
|
|
42
|
-
type VRouteRenderView = [key: string, component: Component];
|
|
43
|
-
interface VRouteRecordRaw {
|
|
44
|
-
key?: string;
|
|
5
|
+
type Lazy<T> = () => Promise<T>;
|
|
6
|
+
type VRouteComponent = Component | DefineComponent;
|
|
7
|
+
type VRouteLazyComponent = Lazy<VRouteComponent>;
|
|
8
|
+
type VRouteRenderComponent = VRouteComponent | VRouteLazyComponent;
|
|
9
|
+
type VRouteMeta = Record<PropertyKey, unknown>;
|
|
10
|
+
interface VRouteRaw {
|
|
45
11
|
path: string;
|
|
46
|
-
component:
|
|
47
|
-
meta?:
|
|
48
|
-
children?:
|
|
12
|
+
component: VRouteRenderComponent;
|
|
13
|
+
meta?: VRouteMeta;
|
|
14
|
+
children?: VRouteRaw[];
|
|
49
15
|
}
|
|
16
|
+
type VRouteId = Readonly<[path: string, depth: number]>;
|
|
50
17
|
interface VRouteMatchedData {
|
|
51
|
-
|
|
18
|
+
id: VRouteId;
|
|
19
|
+
meta?: VRouteMeta;
|
|
20
|
+
}
|
|
21
|
+
interface VRouteNormalized extends Omit<VRouteRaw, 'path' | 'children'> {
|
|
22
|
+
parentId?: VRouteId;
|
|
23
|
+
}
|
|
24
|
+
type VRoutesMap = Map<VRouteId, VRouteNormalized>;
|
|
25
|
+
interface VRoute {
|
|
52
26
|
fullPath: string;
|
|
53
|
-
meta?:
|
|
27
|
+
meta?: VRouteMeta;
|
|
28
|
+
params?: Record<string, string>;
|
|
29
|
+
path: string;
|
|
30
|
+
search: string;
|
|
31
|
+
hash: string;
|
|
32
|
+
_renderList: Component[] | null;
|
|
54
33
|
}
|
|
55
|
-
interface
|
|
34
|
+
interface VRouterData {
|
|
56
35
|
context: RouterContext<VRouteMatchedData>;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
36
|
+
routes: VRoutesMap;
|
|
37
|
+
activePath: Ref<string>;
|
|
38
|
+
route: ShallowRef<VRoute>;
|
|
39
|
+
_isGlobal: boolean;
|
|
40
|
+
_deps: number;
|
|
41
|
+
_dispose: () => void;
|
|
60
42
|
}
|
|
61
43
|
interface VRouterOptions {
|
|
62
|
-
initialPath
|
|
44
|
+
initialPath?: string;
|
|
45
|
+
_isGlobal?: boolean;
|
|
63
46
|
}
|
|
64
47
|
interface VRouter {
|
|
65
|
-
route:
|
|
66
|
-
fullPath: string;
|
|
67
|
-
data?: VRouteMatchedData;
|
|
68
|
-
params?: Record<string, string>;
|
|
69
|
-
path: string;
|
|
70
|
-
search: string;
|
|
71
|
-
hash: string;
|
|
72
|
-
activeRouteTree?: string[];
|
|
73
|
-
renderList?: VRouteRenderView[];
|
|
74
|
-
}>;
|
|
48
|
+
route: VRouterData['route'];
|
|
75
49
|
router: {
|
|
76
|
-
addRoute: (route:
|
|
50
|
+
addRoute: (route: VRouteRaw) => void;
|
|
77
51
|
replace: (path: string) => void;
|
|
78
|
-
_key: string;
|
|
79
52
|
_depthKey: symbol;
|
|
80
53
|
};
|
|
81
54
|
}
|
|
55
|
+
interface VirouPluginOptions {
|
|
56
|
+
routers?: Record<string, {
|
|
57
|
+
routes: VRouteRaw[];
|
|
58
|
+
options?: Omit<VRouterOptions, '_isGlobal'>;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
declare module 'vue' {
|
|
63
|
+
interface ComponentCustomProperties {
|
|
64
|
+
$virou: Map<string, VRouterData>
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
declare const VRouterView: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
69
|
+
routerKey: StringConstructor;
|
|
70
|
+
keepAlive: {
|
|
71
|
+
type: BooleanConstructor;
|
|
72
|
+
default: boolean;
|
|
73
|
+
};
|
|
74
|
+
viewKey: {
|
|
75
|
+
type: PropType<string | ((route: VRoute, key: string) => string)>;
|
|
76
|
+
};
|
|
77
|
+
}>, () => string | number | boolean | vue.VNode<vue.RendererNode, vue.RendererElement, {
|
|
78
|
+
[key: string]: any;
|
|
79
|
+
}> | vue.VNodeArrayChildren | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
80
|
+
routerKey: StringConstructor;
|
|
81
|
+
keepAlive: {
|
|
82
|
+
type: BooleanConstructor;
|
|
83
|
+
default: boolean;
|
|
84
|
+
};
|
|
85
|
+
viewKey: {
|
|
86
|
+
type: PropType<string | ((route: VRoute, key: string) => string)>;
|
|
87
|
+
};
|
|
88
|
+
}>> & Readonly<{}>, {
|
|
89
|
+
keepAlive: boolean;
|
|
90
|
+
}, SlotsType<{
|
|
91
|
+
default: (payload: {
|
|
92
|
+
Component: VNodeChild;
|
|
93
|
+
route: VRoute;
|
|
94
|
+
}) => VNodeChild;
|
|
95
|
+
fallback: (payload: {
|
|
96
|
+
route: VRoute;
|
|
97
|
+
}) => VNodeChild;
|
|
98
|
+
}>, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
99
|
+
|
|
100
|
+
declare const virou: Plugin<[VirouPluginOptions?]>;
|
|
82
101
|
|
|
83
102
|
declare const virouSymbol: unique symbol;
|
|
84
|
-
declare function createVRouter(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
*
|
|
88
|
-
* @param key - a unique identifier for the router
|
|
89
|
-
* @param routes - a list of routes to add to the router
|
|
90
|
-
* @param options - configuration options for the router
|
|
91
|
-
*
|
|
92
|
-
* @returns an object containing:
|
|
93
|
-
* - `route`: reactive data for the current route (e.g., path, fullPath, query, etc.)
|
|
94
|
-
* - `router`: utilities to manage the router (e.g., `addRoute`, `replace`)
|
|
95
|
-
*/
|
|
96
|
-
declare function useVRouter(key?: string, routes?: VRouteRecordRaw[], options?: VRouterOptions): VRouter;
|
|
97
|
-
declare function useVRouter(routes?: VRouteRecordRaw[], options?: VRouterOptions): VRouter;
|
|
103
|
+
declare function createVRouter(routes: VRouteRaw[], options?: VRouterOptions): VRouterData;
|
|
104
|
+
declare function useVRouter(routes?: VRouteRaw[], options?: VRouterOptions): VRouter;
|
|
105
|
+
declare function useVRouter(key?: string, routes?: VRouteRaw[], options?: VRouterOptions): VRouter;
|
|
98
106
|
|
|
99
|
-
export { type
|
|
107
|
+
export { type VRoute, type VRouteComponent, type VRouteId, type VRouteLazyComponent, type VRouteMatchedData, type VRouteMeta, type VRouteNormalized, type VRouteRaw, type VRouteRenderComponent, type VRouter, type VRouterData, type VRouterOptions, VRouterView, type VRoutesMap, type VirouPluginOptions, createVRouter, useVRouter, virou, virouSymbol };
|
package/dist/index.mjs
CHANGED
|
@@ -1,133 +1,130 @@
|
|
|
1
|
-
import { defineAsyncComponent, shallowRef,
|
|
2
|
-
import { createGlobalState } from '@vueuse/core';
|
|
1
|
+
import { defineAsyncComponent, ref, shallowRef, watchEffect, inject, useId, provide, getCurrentInstance, onScopeDispose, defineComponent, h, Suspense, KeepAlive } from 'vue';
|
|
3
2
|
import { addRoute, createRouter, findRoute } from 'rou3';
|
|
4
3
|
import { joinURL, parseURL } from 'ufo';
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
keys[fullPath] ||= [];
|
|
13
|
-
keys[fullPath].push(key ?? null);
|
|
14
|
-
addRoute(context, "GET", fullPath, { key, fullPath, meta });
|
|
15
|
-
const node = [fullPath];
|
|
16
|
-
if (children) {
|
|
17
|
-
const childRouteTree = [];
|
|
18
|
-
addRoutes({ ...router, routeTree: childRouteTree }, children, fullPath);
|
|
19
|
-
node[1] = childRouteTree;
|
|
20
|
-
}
|
|
21
|
-
routeTree.push(node);
|
|
22
|
-
});
|
|
5
|
+
const renderListCache = /* @__PURE__ */ new WeakMap();
|
|
6
|
+
function normalizeComponent(component) {
|
|
7
|
+
if (typeof component === "function" && component.length === 0) {
|
|
8
|
+
return defineAsyncComponent(component);
|
|
9
|
+
}
|
|
10
|
+
return component;
|
|
23
11
|
}
|
|
24
|
-
function
|
|
25
|
-
for (const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
12
|
+
function registerRoutes(ctx, routes, registry, parentId) {
|
|
13
|
+
for (const { path, meta, component, children } of routes) {
|
|
14
|
+
const fullPath = joinURL(parentId?.[0] ?? "/", path);
|
|
15
|
+
const depth = (parentId?.[1] ?? -1) + 1;
|
|
16
|
+
const id = Object.freeze([fullPath, depth]);
|
|
17
|
+
registry.set(id, {
|
|
18
|
+
meta,
|
|
19
|
+
component: normalizeComponent(component),
|
|
20
|
+
parentId
|
|
21
|
+
});
|
|
22
|
+
if (children && children.length) {
|
|
23
|
+
registerRoutes(ctx, children, registry, id);
|
|
35
24
|
}
|
|
25
|
+
addRoute(ctx, "GET", fullPath, { id, meta });
|
|
36
26
|
}
|
|
37
|
-
return [];
|
|
38
27
|
}
|
|
39
|
-
function createRenderList(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
28
|
+
function createRenderList(data, routes) {
|
|
29
|
+
let cacheForRoutes = renderListCache.get(routes);
|
|
30
|
+
if (!cacheForRoutes) {
|
|
31
|
+
cacheForRoutes = /* @__PURE__ */ new Map();
|
|
32
|
+
renderListCache.set(routes, cacheForRoutes);
|
|
33
|
+
}
|
|
34
|
+
const cached = cacheForRoutes.get(data.id);
|
|
35
|
+
if (cached) {
|
|
36
|
+
return cached;
|
|
37
|
+
}
|
|
38
|
+
const depth = data.id[1];
|
|
39
|
+
const list = Array.from({ length: depth });
|
|
40
|
+
let idx = depth;
|
|
41
|
+
let cursor = routes.get(data.id);
|
|
42
|
+
while (cursor) {
|
|
43
|
+
list[idx--] = cursor.component;
|
|
44
|
+
cursor = cursor.parentId !== void 0 ? routes.get(cursor.parentId) : void 0;
|
|
45
|
+
}
|
|
46
|
+
cacheForRoutes.set(data.id, list);
|
|
47
|
+
return list;
|
|
58
48
|
}
|
|
59
49
|
|
|
60
50
|
const virouSymbol = Symbol("virou");
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
51
|
+
function createVRouter(routes, options) {
|
|
52
|
+
const context = createRouter();
|
|
53
|
+
const routeRegistry = /* @__PURE__ */ new Map();
|
|
54
|
+
registerRoutes(context, routes, routeRegistry);
|
|
55
|
+
const activePath = ref(options?.initialPath ?? "/");
|
|
56
|
+
const snapshot = () => {
|
|
57
|
+
const matchedRoute = findRoute(context, "GET", activePath.value);
|
|
58
|
+
const _renderList = matchedRoute ? createRenderList(matchedRoute.data, routeRegistry) : null;
|
|
59
|
+
const { pathname, hash, search } = parseURL(activePath.value);
|
|
60
|
+
return {
|
|
61
|
+
fullPath: activePath.value,
|
|
62
|
+
path: pathname,
|
|
63
|
+
search,
|
|
64
|
+
hash,
|
|
65
|
+
meta: matchedRoute?.data.meta,
|
|
66
|
+
params: matchedRoute?.params,
|
|
67
|
+
_renderList
|
|
68
|
+
};
|
|
76
69
|
};
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
70
|
+
const route = shallowRef(snapshot());
|
|
71
|
+
const unwatch = watchEffect(() => {
|
|
72
|
+
route.value = snapshot();
|
|
73
|
+
});
|
|
74
|
+
return {
|
|
75
|
+
context,
|
|
76
|
+
routes: routeRegistry,
|
|
77
|
+
activePath,
|
|
78
|
+
route,
|
|
79
|
+
_isGlobal: options?._isGlobal ?? false,
|
|
80
|
+
_deps: 0,
|
|
81
|
+
_dispose: unwatch
|
|
80
82
|
};
|
|
81
|
-
activeRoutePaths.value[key] = initialPath;
|
|
82
83
|
}
|
|
83
84
|
function useVRouter(...args) {
|
|
84
85
|
if (typeof args[0] !== "string") {
|
|
85
86
|
args.unshift(inject(virouSymbol, useId()));
|
|
86
87
|
}
|
|
87
|
-
const [key, routes] = args;
|
|
88
|
+
const [key, routes = [], options = {}] = args;
|
|
88
89
|
if (!key || typeof key !== "string") {
|
|
89
90
|
throw new TypeError(`[virou] [useVRouter] key must be a string: ${key}`);
|
|
90
91
|
}
|
|
91
92
|
provide(virouSymbol, key);
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
const vm = getCurrentInstance();
|
|
94
|
+
if (!vm) {
|
|
95
|
+
throw new TypeError("[virou] [useVRouter] useVRouter must be called in setup()");
|
|
96
|
+
}
|
|
97
|
+
const virou = vm.proxy?.$virou;
|
|
98
|
+
if (!virou) {
|
|
99
|
+
throw new Error("[virou] [useVRouter] virou plugin not installed");
|
|
100
|
+
}
|
|
101
|
+
if (routes.length) {
|
|
102
|
+
if (virou.get(key)) {
|
|
103
|
+
throw new Error(`[virou] [useVRouter] router with key "${key}" already exists`);
|
|
97
104
|
}
|
|
98
|
-
|
|
105
|
+
virou.set(key, createVRouter(routes, options));
|
|
99
106
|
}
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return {
|
|
112
|
-
fullPath: activeRoutePath,
|
|
113
|
-
...matchedRoute,
|
|
114
|
-
search,
|
|
115
|
-
path: pathname,
|
|
116
|
-
hash,
|
|
117
|
-
activeRouteTree,
|
|
118
|
-
renderList
|
|
119
|
-
};
|
|
107
|
+
const router = virou.get(key);
|
|
108
|
+
if (!router) {
|
|
109
|
+
throw new Error(`[virou] [useVRouter] router with key "${key}" not found`);
|
|
110
|
+
}
|
|
111
|
+
router._deps++;
|
|
112
|
+
onScopeDispose(() => {
|
|
113
|
+
router._deps--;
|
|
114
|
+
if (router._deps === 0 && !router._isGlobal) {
|
|
115
|
+
router._dispose();
|
|
116
|
+
virou.delete(key);
|
|
117
|
+
}
|
|
120
118
|
});
|
|
121
119
|
return {
|
|
122
|
-
route,
|
|
120
|
+
route: router.route,
|
|
123
121
|
router: {
|
|
124
|
-
addRoute: (
|
|
125
|
-
|
|
122
|
+
addRoute: (route) => {
|
|
123
|
+
registerRoutes(router.context, [route], router.routes);
|
|
126
124
|
},
|
|
127
125
|
replace: (path) => {
|
|
128
|
-
|
|
126
|
+
router.activePath.value = path;
|
|
129
127
|
},
|
|
130
|
-
_key: key,
|
|
131
128
|
_depthKey: Symbol.for(key)
|
|
132
129
|
}
|
|
133
130
|
};
|
|
@@ -135,36 +132,51 @@ function useVRouter(...args) {
|
|
|
135
132
|
|
|
136
133
|
const VRouterView = defineComponent({
|
|
137
134
|
name: "VRouterView",
|
|
135
|
+
inheritAttrs: false,
|
|
138
136
|
props: {
|
|
139
|
-
routerKey:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
},
|
|
143
|
-
viewKey: {
|
|
144
|
-
type: String,
|
|
145
|
-
default: undefined
|
|
146
|
-
},
|
|
147
|
-
keepalive: {
|
|
148
|
-
type: Boolean,
|
|
149
|
-
default: false
|
|
150
|
-
}
|
|
137
|
+
routerKey: String,
|
|
138
|
+
keepAlive: { type: Boolean, default: false },
|
|
139
|
+
viewKey: { type: [String, Function] }
|
|
151
140
|
},
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
141
|
+
slots: Object,
|
|
142
|
+
setup(props, { slots, attrs }) {
|
|
143
|
+
const key = props.routerKey ?? inject(virouSymbol);
|
|
144
|
+
if (key === void 0) {
|
|
145
|
+
throw new Error("[virou] [VRouterView] routerKey is required");
|
|
156
146
|
}
|
|
157
|
-
const {
|
|
147
|
+
const { route, router } = useVRouter(key);
|
|
158
148
|
const depth = inject(router._depthKey, 0);
|
|
159
|
-
const render = computed(() => route.value.renderList?.[depth]);
|
|
160
149
|
provide(router._depthKey, depth + 1);
|
|
161
150
|
return () => {
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
151
|
+
const component = route.value._renderList?.[depth];
|
|
152
|
+
if (!component) {
|
|
153
|
+
return slots.default?.({ Component: null, route: route.value }) ?? null;
|
|
154
|
+
}
|
|
155
|
+
const vnodeKey = typeof props.viewKey === "function" ? props.viewKey(route.value, key) : props.viewKey ?? `${key}-${depth}-${route.value.path}`;
|
|
156
|
+
const suspenseVNode = h(
|
|
157
|
+
Suspense,
|
|
158
|
+
null,
|
|
159
|
+
{
|
|
160
|
+
default: () => h(component, { key: vnodeKey, ...attrs }),
|
|
161
|
+
fallback: () => slots.fallback?.({ route: route.value }) ?? null
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
const vnode = props.keepAlive ? h(KeepAlive, null, { default: () => suspenseVNode }) : suspenseVNode;
|
|
165
|
+
return slots.default?.({ Component: vnode, route: route.value }) ?? vnode;
|
|
166
166
|
};
|
|
167
167
|
}
|
|
168
168
|
});
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
const virou = (app, options = {}) => {
|
|
171
|
+
const { routers } = options;
|
|
172
|
+
const map = /* @__PURE__ */ new Map();
|
|
173
|
+
app.config.globalProperties.$virou = map;
|
|
174
|
+
app.component("VRouterView", VRouterView);
|
|
175
|
+
if (routers) {
|
|
176
|
+
for (const [key, router] of Object.entries(routers)) {
|
|
177
|
+
map.set(key, createVRouter(router.routes, { ...router.options, _isGlobal: true }));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export { VRouterView, createVRouter, useVRouter, virou, virouSymbol };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@virou/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"description": "Virtual router with multiple instance support for Vue",
|
|
6
6
|
"author": "Tankosin<https://github.com/tankosinn>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -22,32 +22,21 @@
|
|
|
22
22
|
],
|
|
23
23
|
"sideEffects": false,
|
|
24
24
|
"exports": {
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
"import": {
|
|
28
|
-
"types": "./dist/index.d.mts",
|
|
29
|
-
"default": "./dist/index.mjs"
|
|
30
|
-
},
|
|
31
|
-
"require": {
|
|
32
|
-
"types": "./dist/index.d.cts",
|
|
33
|
-
"default": "./dist/index.cjs"
|
|
34
|
-
}
|
|
35
|
-
}
|
|
25
|
+
"types": "./dist/index.d.mts",
|
|
26
|
+
"default": "./dist/index.mjs"
|
|
36
27
|
},
|
|
37
|
-
"main": "./dist/index.cjs",
|
|
38
|
-
"module": "./dist/index.mjs",
|
|
39
|
-
"types": "./dist/index.d.cts",
|
|
40
28
|
"files": [
|
|
41
29
|
"dist"
|
|
42
30
|
],
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"vue": "^3.5.0"
|
|
33
|
+
},
|
|
43
34
|
"dependencies": {
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"ufo": "^1.5.4",
|
|
47
|
-
"vue": "^3.5.13"
|
|
35
|
+
"rou3": "^0.6.1",
|
|
36
|
+
"ufo": "^1.6.1"
|
|
48
37
|
},
|
|
49
38
|
"devDependencies": {
|
|
50
|
-
"unbuild": "3.
|
|
39
|
+
"unbuild": "3.5.0"
|
|
51
40
|
},
|
|
52
41
|
"scripts": {
|
|
53
42
|
"build": "unbuild"
|
package/dist/index.cjs
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const vue = require('vue');
|
|
4
|
-
const core = require('@vueuse/core');
|
|
5
|
-
const rou3 = require('rou3');
|
|
6
|
-
const ufo = require('ufo');
|
|
7
|
-
|
|
8
|
-
function addRoutes(router, routes, parentPath = "") {
|
|
9
|
-
const { context, routeTree, views, keys } = router;
|
|
10
|
-
routes.forEach(({ key, path, component, meta, children }) => {
|
|
11
|
-
const fullPath = ufo.joinURL(parentPath, path);
|
|
12
|
-
views[fullPath] ||= [];
|
|
13
|
-
views[fullPath].push(vue.defineAsyncComponent(component));
|
|
14
|
-
keys[fullPath] ||= [];
|
|
15
|
-
keys[fullPath].push(key ?? null);
|
|
16
|
-
rou3.addRoute(context, "GET", fullPath, { key, fullPath, meta });
|
|
17
|
-
const node = [fullPath];
|
|
18
|
-
if (children) {
|
|
19
|
-
const childRouteTree = [];
|
|
20
|
-
addRoutes({ ...router, routeTree: childRouteTree }, children, fullPath);
|
|
21
|
-
node[1] = childRouteTree;
|
|
22
|
-
}
|
|
23
|
-
routeTree.push(node);
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
function resolveRouteTree(routeTree, targetPath, accumulatedPaths = []) {
|
|
27
|
-
for (const [path, children] of routeTree) {
|
|
28
|
-
const paths = [...accumulatedPaths, path];
|
|
29
|
-
if (path === targetPath) {
|
|
30
|
-
return paths;
|
|
31
|
-
}
|
|
32
|
-
if (children) {
|
|
33
|
-
const result = resolveRouteTree(children, targetPath, paths);
|
|
34
|
-
if (result.length) {
|
|
35
|
-
return result;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return [];
|
|
40
|
-
}
|
|
41
|
-
function createRenderList(routes, targetPath, views, keys) {
|
|
42
|
-
const renderList = [];
|
|
43
|
-
const currentSegments = targetPath.split("/").filter(Boolean);
|
|
44
|
-
routes.forEach((path) => {
|
|
45
|
-
const treeViews = views[path];
|
|
46
|
-
const treeKeys = keys[path];
|
|
47
|
-
if (treeViews === undefined || treeKeys === undefined || treeViews.length !== treeKeys.length) {
|
|
48
|
-
throw new Error(`[virou] Mismatch or missing data for path: ${path}`);
|
|
49
|
-
}
|
|
50
|
-
const resolvedPath = ufo.joinURL("/", ...currentSegments.slice(0, path.split("/").filter(Boolean).length));
|
|
51
|
-
for (let i = 0; i < treeViews.length; i++) {
|
|
52
|
-
const view = treeViews[i];
|
|
53
|
-
const key = treeKeys[i];
|
|
54
|
-
if (view !== undefined && key !== undefined && (targetPath === resolvedPath || i === 0)) {
|
|
55
|
-
renderList.push([key ?? resolvedPath, view]);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
return renderList;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const virouSymbol = Symbol("virou");
|
|
63
|
-
const useVirouState = core.createGlobalState(() => {
|
|
64
|
-
const routers = vue.shallowRef({});
|
|
65
|
-
const activeRoutePaths = vue.ref({});
|
|
66
|
-
return { routers, activeRoutePaths };
|
|
67
|
-
});
|
|
68
|
-
function createVRouter(key, routes, options) {
|
|
69
|
-
const { routers, activeRoutePaths } = useVirouState();
|
|
70
|
-
if (key in routers.value) {
|
|
71
|
-
throw new Error(`[virou] [createVRouter] key already exists: ${key}`);
|
|
72
|
-
}
|
|
73
|
-
routers.value[key] = {
|
|
74
|
-
context: rou3.createRouter(),
|
|
75
|
-
routeTree: [],
|
|
76
|
-
views: {},
|
|
77
|
-
keys: {}
|
|
78
|
-
};
|
|
79
|
-
addRoutes(routers.value[key], routes);
|
|
80
|
-
const { initialPath } = options ||= {
|
|
81
|
-
initialPath: "/"
|
|
82
|
-
};
|
|
83
|
-
activeRoutePaths.value[key] = initialPath;
|
|
84
|
-
}
|
|
85
|
-
function useVRouter(...args) {
|
|
86
|
-
if (typeof args[0] !== "string") {
|
|
87
|
-
args.unshift(vue.inject(virouSymbol, vue.useId()));
|
|
88
|
-
}
|
|
89
|
-
const [key, routes] = args;
|
|
90
|
-
if (!key || typeof key !== "string") {
|
|
91
|
-
throw new TypeError(`[virou] [useVRouter] key must be a string: ${key}`);
|
|
92
|
-
}
|
|
93
|
-
vue.provide(virouSymbol, key);
|
|
94
|
-
const options = args[2];
|
|
95
|
-
const { routers, activeRoutePaths } = useVirouState();
|
|
96
|
-
if (routers.value[key] === undefined) {
|
|
97
|
-
if (routes === undefined) {
|
|
98
|
-
throw new Error(`[virou] [useVRouter] routes is required for key: ${key}`);
|
|
99
|
-
}
|
|
100
|
-
createVRouter(key, routes, options);
|
|
101
|
-
}
|
|
102
|
-
const route = vue.computed(() => {
|
|
103
|
-
const { context, routeTree, views, keys } = routers.value[key];
|
|
104
|
-
const activeRoutePath = activeRoutePaths.value[key];
|
|
105
|
-
const matchedRoute = rou3.findRoute(
|
|
106
|
-
context,
|
|
107
|
-
"GET",
|
|
108
|
-
activeRoutePath
|
|
109
|
-
);
|
|
110
|
-
const activeRouteTree = matchedRoute && resolveRouteTree(routeTree, matchedRoute.data.fullPath);
|
|
111
|
-
const { search, pathname, hash } = ufo.parseURL(activeRoutePath);
|
|
112
|
-
const renderList = activeRouteTree && createRenderList(activeRouteTree, pathname, views, keys);
|
|
113
|
-
return {
|
|
114
|
-
fullPath: activeRoutePath,
|
|
115
|
-
...matchedRoute,
|
|
116
|
-
search,
|
|
117
|
-
path: pathname,
|
|
118
|
-
hash,
|
|
119
|
-
activeRouteTree,
|
|
120
|
-
renderList
|
|
121
|
-
};
|
|
122
|
-
});
|
|
123
|
-
return {
|
|
124
|
-
route,
|
|
125
|
-
router: {
|
|
126
|
-
addRoute: (route2) => {
|
|
127
|
-
addRoutes(routers.value[key], [route2]);
|
|
128
|
-
},
|
|
129
|
-
replace: (path) => {
|
|
130
|
-
activeRoutePaths.value[key] = path;
|
|
131
|
-
},
|
|
132
|
-
_key: key,
|
|
133
|
-
_depthKey: Symbol.for(key)
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const VRouterView = vue.defineComponent({
|
|
139
|
-
name: "VRouterView",
|
|
140
|
-
props: {
|
|
141
|
-
routerKey: {
|
|
142
|
-
type: String,
|
|
143
|
-
default: undefined
|
|
144
|
-
},
|
|
145
|
-
viewKey: {
|
|
146
|
-
type: String,
|
|
147
|
-
default: undefined
|
|
148
|
-
},
|
|
149
|
-
keepalive: {
|
|
150
|
-
type: Boolean,
|
|
151
|
-
default: false
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
setup(props) {
|
|
155
|
-
const routerKey = props.routerKey ?? vue.inject(virouSymbol);
|
|
156
|
-
if (routerKey === undefined) {
|
|
157
|
-
throw new Error(`[virou] [VRouterView] routerKey is required`);
|
|
158
|
-
}
|
|
159
|
-
const { router, route } = useVRouter(routerKey);
|
|
160
|
-
const depth = vue.inject(router._depthKey, 0);
|
|
161
|
-
const render = vue.computed(() => route.value.renderList?.[depth]);
|
|
162
|
-
vue.provide(router._depthKey, depth + 1);
|
|
163
|
-
return () => {
|
|
164
|
-
const [key, component] = render.value ?? [];
|
|
165
|
-
const _key = key ?? props.viewKey ?? route.value.path;
|
|
166
|
-
const ViewComponent = component !== null && component !== undefined ? vue.h(component, { _key }) : null;
|
|
167
|
-
return props.keepalive ? vue.h(vue.KeepAlive, null, [ViewComponent]) : ViewComponent;
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
exports.VRouterView = VRouterView;
|
|
173
|
-
exports.createVRouter = createVRouter;
|
|
174
|
-
exports.useVRouter = useVRouter;
|
|
175
|
-
exports.virouSymbol = virouSymbol;
|
package/dist/index.d.cts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import * as vue from 'vue';
|
|
2
|
-
import { Component, AsyncComponentLoader, ComputedRef } from 'vue';
|
|
3
|
-
import { RouterContext } from 'rou3';
|
|
4
|
-
|
|
5
|
-
declare const VRouterView: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
6
|
-
routerKey: {
|
|
7
|
-
type: StringConstructor;
|
|
8
|
-
default: undefined;
|
|
9
|
-
};
|
|
10
|
-
viewKey: {
|
|
11
|
-
type: StringConstructor;
|
|
12
|
-
default: undefined;
|
|
13
|
-
};
|
|
14
|
-
keepalive: {
|
|
15
|
-
type: BooleanConstructor;
|
|
16
|
-
default: boolean;
|
|
17
|
-
};
|
|
18
|
-
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
|
|
19
|
-
[key: string]: any;
|
|
20
|
-
}> | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
21
|
-
routerKey: {
|
|
22
|
-
type: StringConstructor;
|
|
23
|
-
default: undefined;
|
|
24
|
-
};
|
|
25
|
-
viewKey: {
|
|
26
|
-
type: StringConstructor;
|
|
27
|
-
default: undefined;
|
|
28
|
-
};
|
|
29
|
-
keepalive: {
|
|
30
|
-
type: BooleanConstructor;
|
|
31
|
-
default: boolean;
|
|
32
|
-
};
|
|
33
|
-
}>> & Readonly<{}>, {
|
|
34
|
-
routerKey: string;
|
|
35
|
-
viewKey: string;
|
|
36
|
-
keepalive: boolean;
|
|
37
|
-
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
38
|
-
|
|
39
|
-
type VRouteTreeNode = [key: string, children?: VRouteTreeNode[]];
|
|
40
|
-
type VRouteViews = Record<string, Component[]>;
|
|
41
|
-
type VRouteKeys = Record<string, Array<string | null>>;
|
|
42
|
-
type VRouteRenderView = [key: string, component: Component];
|
|
43
|
-
interface VRouteRecordRaw {
|
|
44
|
-
key?: string;
|
|
45
|
-
path: string;
|
|
46
|
-
component: AsyncComponentLoader<Component>;
|
|
47
|
-
meta?: Record<string, any>;
|
|
48
|
-
children?: VRouteRecordRaw[];
|
|
49
|
-
}
|
|
50
|
-
interface VRouteMatchedData {
|
|
51
|
-
key?: string;
|
|
52
|
-
fullPath: string;
|
|
53
|
-
meta?: Record<string, any>;
|
|
54
|
-
}
|
|
55
|
-
interface VRouterInstance {
|
|
56
|
-
context: RouterContext<VRouteMatchedData>;
|
|
57
|
-
routeTree: VRouteTreeNode[];
|
|
58
|
-
views: VRouteViews;
|
|
59
|
-
keys: VRouteKeys;
|
|
60
|
-
}
|
|
61
|
-
interface VRouterOptions {
|
|
62
|
-
initialPath: string;
|
|
63
|
-
}
|
|
64
|
-
interface VRouter {
|
|
65
|
-
route: ComputedRef<{
|
|
66
|
-
fullPath: string;
|
|
67
|
-
data?: VRouteMatchedData;
|
|
68
|
-
params?: Record<string, string>;
|
|
69
|
-
path: string;
|
|
70
|
-
search: string;
|
|
71
|
-
hash: string;
|
|
72
|
-
activeRouteTree?: string[];
|
|
73
|
-
renderList?: VRouteRenderView[];
|
|
74
|
-
}>;
|
|
75
|
-
router: {
|
|
76
|
-
addRoute: (route: VRouteRecordRaw) => void;
|
|
77
|
-
replace: (path: string) => void;
|
|
78
|
-
_key: string;
|
|
79
|
-
_depthKey: symbol;
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
declare const virouSymbol: unique symbol;
|
|
84
|
-
declare function createVRouter(key: string, routes: VRouteRecordRaw[], options?: VRouterOptions): void;
|
|
85
|
-
/**
|
|
86
|
-
* Creates or retrieves a virtual router
|
|
87
|
-
*
|
|
88
|
-
* @param key - a unique identifier for the router
|
|
89
|
-
* @param routes - a list of routes to add to the router
|
|
90
|
-
* @param options - configuration options for the router
|
|
91
|
-
*
|
|
92
|
-
* @returns an object containing:
|
|
93
|
-
* - `route`: reactive data for the current route (e.g., path, fullPath, query, etc.)
|
|
94
|
-
* - `router`: utilities to manage the router (e.g., `addRoute`, `replace`)
|
|
95
|
-
*/
|
|
96
|
-
declare function useVRouter(key?: string, routes?: VRouteRecordRaw[], options?: VRouterOptions): VRouter;
|
|
97
|
-
declare function useVRouter(routes?: VRouteRecordRaw[], options?: VRouterOptions): VRouter;
|
|
98
|
-
|
|
99
|
-
export { type VRouteKeys, type VRouteMatchedData, type VRouteRecordRaw, type VRouteRenderView, type VRouteTreeNode, type VRouteViews, type VRouter, type VRouterInstance, type VRouterOptions, VRouterView, createVRouter, useVRouter, virouSymbol };
|
package/dist/index.d.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import * as vue from 'vue';
|
|
2
|
-
import { Component, AsyncComponentLoader, ComputedRef } from 'vue';
|
|
3
|
-
import { RouterContext } from 'rou3';
|
|
4
|
-
|
|
5
|
-
declare const VRouterView: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
6
|
-
routerKey: {
|
|
7
|
-
type: StringConstructor;
|
|
8
|
-
default: undefined;
|
|
9
|
-
};
|
|
10
|
-
viewKey: {
|
|
11
|
-
type: StringConstructor;
|
|
12
|
-
default: undefined;
|
|
13
|
-
};
|
|
14
|
-
keepalive: {
|
|
15
|
-
type: BooleanConstructor;
|
|
16
|
-
default: boolean;
|
|
17
|
-
};
|
|
18
|
-
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
|
|
19
|
-
[key: string]: any;
|
|
20
|
-
}> | null, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {}, string, vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
21
|
-
routerKey: {
|
|
22
|
-
type: StringConstructor;
|
|
23
|
-
default: undefined;
|
|
24
|
-
};
|
|
25
|
-
viewKey: {
|
|
26
|
-
type: StringConstructor;
|
|
27
|
-
default: undefined;
|
|
28
|
-
};
|
|
29
|
-
keepalive: {
|
|
30
|
-
type: BooleanConstructor;
|
|
31
|
-
default: boolean;
|
|
32
|
-
};
|
|
33
|
-
}>> & Readonly<{}>, {
|
|
34
|
-
routerKey: string;
|
|
35
|
-
viewKey: string;
|
|
36
|
-
keepalive: boolean;
|
|
37
|
-
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
38
|
-
|
|
39
|
-
type VRouteTreeNode = [key: string, children?: VRouteTreeNode[]];
|
|
40
|
-
type VRouteViews = Record<string, Component[]>;
|
|
41
|
-
type VRouteKeys = Record<string, Array<string | null>>;
|
|
42
|
-
type VRouteRenderView = [key: string, component: Component];
|
|
43
|
-
interface VRouteRecordRaw {
|
|
44
|
-
key?: string;
|
|
45
|
-
path: string;
|
|
46
|
-
component: AsyncComponentLoader<Component>;
|
|
47
|
-
meta?: Record<string, any>;
|
|
48
|
-
children?: VRouteRecordRaw[];
|
|
49
|
-
}
|
|
50
|
-
interface VRouteMatchedData {
|
|
51
|
-
key?: string;
|
|
52
|
-
fullPath: string;
|
|
53
|
-
meta?: Record<string, any>;
|
|
54
|
-
}
|
|
55
|
-
interface VRouterInstance {
|
|
56
|
-
context: RouterContext<VRouteMatchedData>;
|
|
57
|
-
routeTree: VRouteTreeNode[];
|
|
58
|
-
views: VRouteViews;
|
|
59
|
-
keys: VRouteKeys;
|
|
60
|
-
}
|
|
61
|
-
interface VRouterOptions {
|
|
62
|
-
initialPath: string;
|
|
63
|
-
}
|
|
64
|
-
interface VRouter {
|
|
65
|
-
route: ComputedRef<{
|
|
66
|
-
fullPath: string;
|
|
67
|
-
data?: VRouteMatchedData;
|
|
68
|
-
params?: Record<string, string>;
|
|
69
|
-
path: string;
|
|
70
|
-
search: string;
|
|
71
|
-
hash: string;
|
|
72
|
-
activeRouteTree?: string[];
|
|
73
|
-
renderList?: VRouteRenderView[];
|
|
74
|
-
}>;
|
|
75
|
-
router: {
|
|
76
|
-
addRoute: (route: VRouteRecordRaw) => void;
|
|
77
|
-
replace: (path: string) => void;
|
|
78
|
-
_key: string;
|
|
79
|
-
_depthKey: symbol;
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
declare const virouSymbol: unique symbol;
|
|
84
|
-
declare function createVRouter(key: string, routes: VRouteRecordRaw[], options?: VRouterOptions): void;
|
|
85
|
-
/**
|
|
86
|
-
* Creates or retrieves a virtual router
|
|
87
|
-
*
|
|
88
|
-
* @param key - a unique identifier for the router
|
|
89
|
-
* @param routes - a list of routes to add to the router
|
|
90
|
-
* @param options - configuration options for the router
|
|
91
|
-
*
|
|
92
|
-
* @returns an object containing:
|
|
93
|
-
* - `route`: reactive data for the current route (e.g., path, fullPath, query, etc.)
|
|
94
|
-
* - `router`: utilities to manage the router (e.g., `addRoute`, `replace`)
|
|
95
|
-
*/
|
|
96
|
-
declare function useVRouter(key?: string, routes?: VRouteRecordRaw[], options?: VRouterOptions): VRouter;
|
|
97
|
-
declare function useVRouter(routes?: VRouteRecordRaw[], options?: VRouterOptions): VRouter;
|
|
98
|
-
|
|
99
|
-
export { type VRouteKeys, type VRouteMatchedData, type VRouteRecordRaw, type VRouteRenderView, type VRouteTreeNode, type VRouteViews, type VRouter, type VRouterInstance, type VRouterOptions, VRouterView, createVRouter, useVRouter, virouSymbol };
|