@fukict/router 0.1.0 → 0.1.2
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 +598 -0
- package/dist/RouterView.d.ts.map +1 -1
- package/dist/RouterView.js +13 -15
- package/dist/RouterView.js.map +1 -1
- package/dist/metadata.d.ts +1 -1
- package/dist/metadata.js +1 -1
- package/package.json +5 -2
package/README.md
ADDED
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
# @fukict/router
|
|
2
|
+
|
|
3
|
+
SPA router for Fukict framework with nested routing, lazy loading, and navigation guards.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Nested Routes**: Multi-level route hierarchies with automatic depth management
|
|
8
|
+
- **Lazy Loading**: Dynamic component loading for code splitting
|
|
9
|
+
- **Navigation Guards**: `beforeEach`, `afterEach`, and route-level `beforeEnter` hooks
|
|
10
|
+
- **Route Parameters**: Dynamic segments with `:param` syntax
|
|
11
|
+
- **Query Parameters**: URL query string support
|
|
12
|
+
- **Hash & History Mode**: Choose between hash-based or HTML5 history routing
|
|
13
|
+
- **Type-Safe**: Full TypeScript support with route type inference
|
|
14
|
+
- **Link Component**: Declarative navigation with automatic active states
|
|
15
|
+
- **Programmatic Navigation**: `push()`, `replace()`, `back()`, `forward()` methods
|
|
16
|
+
- **Route Meta**: Attach custom metadata to routes
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add @fukict/router @fukict/basic
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### Basic Setup
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { Fukict, attach } from '@fukict/basic';
|
|
30
|
+
import { Link, RouteComponent, RouterProvider } from '@fukict/router';
|
|
31
|
+
|
|
32
|
+
// Define page components
|
|
33
|
+
class HomePage extends RouteComponent {
|
|
34
|
+
render() {
|
|
35
|
+
return (
|
|
36
|
+
<div>
|
|
37
|
+
<h1>Home Page</h1>
|
|
38
|
+
<Link to="/about">Go to About</Link>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class AboutPage extends RouteComponent {
|
|
45
|
+
render() {
|
|
46
|
+
return <h1>About Page</h1>;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Configure routes
|
|
51
|
+
const routes = [
|
|
52
|
+
{ path: '/', component: HomePage },
|
|
53
|
+
{ path: '/about', component: AboutPage },
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
// Create app with RouterProvider
|
|
57
|
+
class App extends Fukict {
|
|
58
|
+
render() {
|
|
59
|
+
return (
|
|
60
|
+
<div>
|
|
61
|
+
<nav>
|
|
62
|
+
<Link to="/">Home</Link>
|
|
63
|
+
<Link to="/about">About</Link>
|
|
64
|
+
</nav>
|
|
65
|
+
<RouterProvider routes={routes} />
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
attach(<App />, document.getElementById('app')!);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Core Concepts
|
|
75
|
+
|
|
76
|
+
### Route Configuration
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import type { RouteConfig } from '@fukict/router';
|
|
80
|
+
|
|
81
|
+
const routes: RouteConfig[] = [
|
|
82
|
+
{
|
|
83
|
+
path: '/',
|
|
84
|
+
component: HomePage,
|
|
85
|
+
meta: { title: 'Home' },
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
path: '/users/:id',
|
|
89
|
+
component: UserPage,
|
|
90
|
+
meta: { title: 'User Profile', requiresAuth: true },
|
|
91
|
+
beforeEnter: (to, from, next) => {
|
|
92
|
+
// Route-level guard
|
|
93
|
+
if (isAuthenticated()) {
|
|
94
|
+
next();
|
|
95
|
+
} else {
|
|
96
|
+
next('/login');
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
path: '/dashboard',
|
|
102
|
+
component: DashboardLayout,
|
|
103
|
+
children: [
|
|
104
|
+
{
|
|
105
|
+
path: '/dashboard/overview',
|
|
106
|
+
component: OverviewPage,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
path: '/dashboard/settings',
|
|
110
|
+
component: SettingsPage,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
path: '*',
|
|
116
|
+
component: NotFoundPage,
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### RouterProvider Options
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
import { RouterProvider } from '@fukict/router';
|
|
125
|
+
|
|
126
|
+
<RouterProvider
|
|
127
|
+
routes={routes}
|
|
128
|
+
mode="hash" // or "history"
|
|
129
|
+
beforeEach={(to, from, next) => {
|
|
130
|
+
// Global before guard
|
|
131
|
+
console.log('Navigating:', from.path, '->', to.path);
|
|
132
|
+
document.title = to.meta?.title || 'App';
|
|
133
|
+
next();
|
|
134
|
+
}}
|
|
135
|
+
afterEach={(to, from) => {
|
|
136
|
+
// Global after hook
|
|
137
|
+
console.log('Navigation complete');
|
|
138
|
+
}}
|
|
139
|
+
/>;
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### RouteComponent Base Class
|
|
143
|
+
|
|
144
|
+
Extend `RouteComponent` for convenient access to routing APIs:
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import { RouteComponent } from '@fukict/router';
|
|
148
|
+
|
|
149
|
+
class UserPage extends RouteComponent {
|
|
150
|
+
mounted() {
|
|
151
|
+
// Access route information
|
|
152
|
+
console.log('Route path:', this.route.path);
|
|
153
|
+
console.log('Params:', this.params);
|
|
154
|
+
console.log('Query:', this.query);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Listen to parameter changes
|
|
158
|
+
routeParamsChanged(newParams: any, oldParams: any) {
|
|
159
|
+
console.log('Params changed:', oldParams, '->', newParams);
|
|
160
|
+
// Re-fetch data based on new params
|
|
161
|
+
this.loadUser(newParams.id);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Listen to query changes
|
|
165
|
+
routeQueryChanged(newQuery: any, oldQuery: any) {
|
|
166
|
+
console.log('Query changed:', oldQuery, '->', newQuery);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
loadUser(id: string) {
|
|
170
|
+
// Load user data
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
goHome = () => {
|
|
174
|
+
this.push('/');
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
render() {
|
|
178
|
+
const { id } = this.params;
|
|
179
|
+
const { page = '1' } = this.query;
|
|
180
|
+
|
|
181
|
+
return (
|
|
182
|
+
<div>
|
|
183
|
+
<h1>User {id}</h1>
|
|
184
|
+
<p>Page: {page}</p>
|
|
185
|
+
<button on:click={this.goHome}>Go Home</button>
|
|
186
|
+
</div>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Link Component
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
import { Link } from '@fukict/router';
|
|
196
|
+
|
|
197
|
+
// Basic link
|
|
198
|
+
<Link to="/about">About</Link>
|
|
199
|
+
|
|
200
|
+
// With query parameters
|
|
201
|
+
<Link to={{ path: '/search', query: { q: 'fukict', page: '1' } }}>
|
|
202
|
+
Search
|
|
203
|
+
</Link>
|
|
204
|
+
|
|
205
|
+
// Replace mode (doesn't add history entry)
|
|
206
|
+
<Link to="/login" replace>Login</Link>
|
|
207
|
+
|
|
208
|
+
// Custom active class
|
|
209
|
+
<Link to="/" activeClass="nav-active" exactActiveClass="nav-exact">
|
|
210
|
+
Home
|
|
211
|
+
</Link>
|
|
212
|
+
|
|
213
|
+
// Styled link
|
|
214
|
+
<Link to="/profile" className="nav-link">
|
|
215
|
+
<span>Profile</span>
|
|
216
|
+
</Link>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Programmatic Navigation
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
class MyComponent extends RouteComponent {
|
|
223
|
+
handleSubmit = () => {
|
|
224
|
+
// Push new route
|
|
225
|
+
this.push('/success');
|
|
226
|
+
|
|
227
|
+
// Push with query
|
|
228
|
+
this.push({ path: '/search', query: { q: 'test' } });
|
|
229
|
+
|
|
230
|
+
// Replace current route
|
|
231
|
+
this.replace('/login');
|
|
232
|
+
|
|
233
|
+
// Update query only
|
|
234
|
+
this.updateQuery({ page: '2', sort: 'date' });
|
|
235
|
+
|
|
236
|
+
// Go back
|
|
237
|
+
this.back();
|
|
238
|
+
|
|
239
|
+
// Go forward
|
|
240
|
+
this.forward();
|
|
241
|
+
|
|
242
|
+
// Access router directly
|
|
243
|
+
this.router.push('/custom');
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
render() {
|
|
247
|
+
return <button on:click={this.handleSubmit}>Submit</button>;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Nested Routes
|
|
253
|
+
|
|
254
|
+
### Parent-Child Structure
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
// Parent layout component
|
|
258
|
+
class DashboardLayout extends RouteComponent {
|
|
259
|
+
render() {
|
|
260
|
+
return (
|
|
261
|
+
<div class="dashboard">
|
|
262
|
+
<aside>
|
|
263
|
+
<Link to="/dashboard/overview">Overview</Link>
|
|
264
|
+
<Link to="/dashboard/analytics">Analytics</Link>
|
|
265
|
+
<Link to="/dashboard/settings">Settings</Link>
|
|
266
|
+
</aside>
|
|
267
|
+
<main>
|
|
268
|
+
{/* Render child routes here */}
|
|
269
|
+
<RouterView router={this.router} />
|
|
270
|
+
</main>
|
|
271
|
+
</div>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Child components
|
|
277
|
+
class OverviewPage extends RouteComponent {
|
|
278
|
+
render() {
|
|
279
|
+
return <h2>Dashboard Overview</h2>;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
class AnalyticsPage extends RouteComponent {
|
|
284
|
+
render() {
|
|
285
|
+
return <h2>Analytics</h2>;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Route configuration
|
|
290
|
+
const routes = [
|
|
291
|
+
{
|
|
292
|
+
path: '/dashboard',
|
|
293
|
+
component: DashboardLayout,
|
|
294
|
+
children: [
|
|
295
|
+
{ path: '/dashboard/overview', component: OverviewPage },
|
|
296
|
+
{ path: '/dashboard/analytics', component: AnalyticsPage },
|
|
297
|
+
{ path: '/dashboard/settings', component: SettingsPage },
|
|
298
|
+
],
|
|
299
|
+
},
|
|
300
|
+
];
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Navigation Guards
|
|
304
|
+
|
|
305
|
+
### Global Guards
|
|
306
|
+
|
|
307
|
+
```tsx
|
|
308
|
+
<RouterProvider
|
|
309
|
+
routes={routes}
|
|
310
|
+
beforeEach={(to, from, next) => {
|
|
311
|
+
// Authentication check
|
|
312
|
+
if (to.meta?.requiresAuth && !isLoggedIn()) {
|
|
313
|
+
next('/login');
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Update page title
|
|
318
|
+
document.title = to.meta?.title || 'App';
|
|
319
|
+
|
|
320
|
+
// Proceed
|
|
321
|
+
next();
|
|
322
|
+
}}
|
|
323
|
+
afterEach={(to, from) => {
|
|
324
|
+
// Analytics tracking
|
|
325
|
+
trackPageView(to.path);
|
|
326
|
+
|
|
327
|
+
// Scroll to top
|
|
328
|
+
window.scrollTo(0, 0);
|
|
329
|
+
}}
|
|
330
|
+
/>
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Route-Level Guards
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
const routes = [
|
|
337
|
+
{
|
|
338
|
+
path: '/admin',
|
|
339
|
+
component: AdminPage,
|
|
340
|
+
beforeEnter: (to, from, next) => {
|
|
341
|
+
if (isAdmin()) {
|
|
342
|
+
next();
|
|
343
|
+
} else {
|
|
344
|
+
next('/forbidden');
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
];
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Guard Execution Order
|
|
352
|
+
|
|
353
|
+
```
|
|
354
|
+
Global beforeEach
|
|
355
|
+
↓
|
|
356
|
+
Route beforeEnter
|
|
357
|
+
↓
|
|
358
|
+
Component mount/update
|
|
359
|
+
↓
|
|
360
|
+
Global afterEach
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Route Parameters
|
|
364
|
+
|
|
365
|
+
### Dynamic Segments
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
// Route config
|
|
369
|
+
{ path: '/users/:id', component: UserPage }
|
|
370
|
+
{ path: '/posts/:category/:id', component: PostPage }
|
|
371
|
+
|
|
372
|
+
// Access in component
|
|
373
|
+
class UserPage extends RouteComponent {
|
|
374
|
+
render() {
|
|
375
|
+
const { id } = this.params;
|
|
376
|
+
return <h1>User {id}</h1>;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// React to param changes
|
|
380
|
+
routeParamsChanged(newParams: any, oldParams: any) {
|
|
381
|
+
console.log('User changed from', oldParams.id, 'to', newParams.id);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Query Parameters
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
// Navigate with query
|
|
390
|
+
this.push({ path: '/search', query: { q: 'fukict', page: '1' } });
|
|
391
|
+
|
|
392
|
+
// Access in component
|
|
393
|
+
class SearchPage extends RouteComponent {
|
|
394
|
+
render() {
|
|
395
|
+
const { q, page = '1' } = this.query;
|
|
396
|
+
return (
|
|
397
|
+
<div>
|
|
398
|
+
<h1>Search: {q}</h1>
|
|
399
|
+
<p>Page: {page}</p>
|
|
400
|
+
</div>
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Update query
|
|
405
|
+
nextPage = () => {
|
|
406
|
+
const currentPage = parseInt(this.query.page || '1');
|
|
407
|
+
this.updateQuery({ page: String(currentPage + 1) });
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// React to query changes
|
|
411
|
+
routeQueryChanged(newQuery: any, oldQuery: any) {
|
|
412
|
+
this.performSearch(newQuery.q, newQuery.page);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
## Lazy Loading
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
// Define lazy component loader
|
|
421
|
+
const LazyPage = () => import('./pages/LazyPage');
|
|
422
|
+
|
|
423
|
+
// Route configuration
|
|
424
|
+
const routes = [
|
|
425
|
+
{
|
|
426
|
+
path: '/lazy',
|
|
427
|
+
component: LazyPage, // Will be loaded on demand
|
|
428
|
+
},
|
|
429
|
+
];
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
## Route Metadata
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
// Define custom route meta
|
|
436
|
+
interface RouteMeta {
|
|
437
|
+
title?: string;
|
|
438
|
+
requiresAuth?: boolean;
|
|
439
|
+
roles?: string[];
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Use in routes
|
|
443
|
+
const routes: RouteConfig[] = [
|
|
444
|
+
{
|
|
445
|
+
path: '/admin',
|
|
446
|
+
component: AdminPage,
|
|
447
|
+
meta: {
|
|
448
|
+
title: 'Admin Panel',
|
|
449
|
+
requiresAuth: true,
|
|
450
|
+
roles: ['admin'],
|
|
451
|
+
} as RouteMeta,
|
|
452
|
+
},
|
|
453
|
+
];
|
|
454
|
+
|
|
455
|
+
// Access in guards
|
|
456
|
+
beforeEach: (to, from, next) => {
|
|
457
|
+
const meta = to.meta as RouteMeta;
|
|
458
|
+
if (meta?.requiresAuth && !isAuthenticated()) {
|
|
459
|
+
next('/login');
|
|
460
|
+
} else {
|
|
461
|
+
next();
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## Advanced Usage
|
|
467
|
+
|
|
468
|
+
### Router Instance
|
|
469
|
+
|
|
470
|
+
```tsx
|
|
471
|
+
import { Router } from '@fukict/router';
|
|
472
|
+
|
|
473
|
+
// Create router manually
|
|
474
|
+
const router = new Router({
|
|
475
|
+
mode: 'history',
|
|
476
|
+
routes,
|
|
477
|
+
beforeEach: (to, from, next) => {
|
|
478
|
+
// Guard logic
|
|
479
|
+
next();
|
|
480
|
+
},
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
// Use in app
|
|
484
|
+
class App extends Fukict {
|
|
485
|
+
render() {
|
|
486
|
+
return <RouterView router={router} />;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### Custom Link Styling
|
|
492
|
+
|
|
493
|
+
```tsx
|
|
494
|
+
<Link
|
|
495
|
+
to="/profile"
|
|
496
|
+
activeClass="text-blue-500"
|
|
497
|
+
exactActiveClass="font-bold text-blue-700"
|
|
498
|
+
>
|
|
499
|
+
<span>Profile</span>
|
|
500
|
+
</Link>
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Redirect Routes
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
const routes = [
|
|
507
|
+
{ path: '/', component: HomePage },
|
|
508
|
+
{
|
|
509
|
+
path: '/old-path',
|
|
510
|
+
beforeEnter: (to, from, next) => {
|
|
511
|
+
next('/new-path');
|
|
512
|
+
},
|
|
513
|
+
},
|
|
514
|
+
{ path: '/new-path', component: NewPage },
|
|
515
|
+
];
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
## Best Practices
|
|
519
|
+
|
|
520
|
+
### 1. Use RouteComponent Base Class
|
|
521
|
+
|
|
522
|
+
```tsx
|
|
523
|
+
// ✅ Good: Extend RouteComponent
|
|
524
|
+
class UserPage extends RouteComponent {
|
|
525
|
+
render() {
|
|
526
|
+
return <div>User {this.params.id}</div>;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// ❌ Bad: Manual router access
|
|
531
|
+
class UserPage extends Fukict<{ router: Router }> {
|
|
532
|
+
render() {
|
|
533
|
+
const params = this.props.router.currentRoute.params;
|
|
534
|
+
return <div>User {params.id}</div>;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### 2. Handle Parameter Changes
|
|
540
|
+
|
|
541
|
+
```tsx
|
|
542
|
+
// ✅ Good: Implement routeParamsChanged
|
|
543
|
+
class ProductPage extends RouteComponent {
|
|
544
|
+
routeParamsChanged(newParams: any, oldParams: any) {
|
|
545
|
+
this.loadProduct(newParams.id);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// ❌ Bad: No reaction to param changes
|
|
550
|
+
class ProductPage extends RouteComponent {
|
|
551
|
+
mounted() {
|
|
552
|
+
this.loadProduct(this.params.id);
|
|
553
|
+
// Won't reload when navigating to different product
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### 3. Organize Routes
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
// ✅ Good: Modular route structure
|
|
562
|
+
const userRoutes = [
|
|
563
|
+
{ path: '/users', component: UserList },
|
|
564
|
+
{ path: '/users/:id', component: UserDetail },
|
|
565
|
+
];
|
|
566
|
+
|
|
567
|
+
const adminRoutes = [
|
|
568
|
+
{ path: '/admin', component: AdminDashboard },
|
|
569
|
+
{ path: '/admin/users', component: AdminUsers },
|
|
570
|
+
];
|
|
571
|
+
|
|
572
|
+
const routes = [...userRoutes, ...adminRoutes, notFoundRoute];
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
## Examples
|
|
576
|
+
|
|
577
|
+
See the [examples/infra-router](../../examples/infra-router) for complete examples:
|
|
578
|
+
|
|
579
|
+
- Basic routing
|
|
580
|
+
- Nested routes
|
|
581
|
+
- Route parameters and query strings
|
|
582
|
+
- Navigation guards
|
|
583
|
+
- Lazy loading
|
|
584
|
+
- 404 handling
|
|
585
|
+
|
|
586
|
+
## API Reference
|
|
587
|
+
|
|
588
|
+
See [docs/README.md](./docs/README.md) for detailed API documentation.
|
|
589
|
+
|
|
590
|
+
## Related Packages
|
|
591
|
+
|
|
592
|
+
- [@fukict/basic](../basic) - Core rendering engine (required)
|
|
593
|
+
- [@fukict/babel-preset](../babel-preset) - JSX transformation
|
|
594
|
+
- [@fukict/vite-plugin](../vite-plugin) - Vite integration
|
|
595
|
+
|
|
596
|
+
## License
|
|
597
|
+
|
|
598
|
+
MIT
|
package/dist/RouterView.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RouterView.d.ts","sourceRoot":"","sources":["../src/RouterView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,KAAK,
|
|
1
|
+
{"version":3,"file":"RouterView.d.ts","sourceRoot":"","sources":["../src/RouterView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,KAAK,EAAa,MAAM,eAAe,CAAC;AAG9D,OAAO,KAAK,EAAe,eAAe,EAAE,MAAM,SAAS,CAAC;AAE5D;;;;;GAKG;AACH,qBAAa,UAAW,SAAQ,MAAM,CAAC,eAAe,CAAC;IACrD;;OAEG;IACH,OAAO,CAAC,WAAW,CAAuB;IAE1C;;OAEG;IACH,aAAa,IAAI,IAAI;IAKrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAezB;;OAEG;IACH,MAAM,IAAI,KAAK;CA6BhB"}
|
package/dist/RouterView.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Fukict, VNodeType
|
|
1
|
+
import { Fukict, VNodeType } from '@fukict/basic';
|
|
2
2
|
/**
|
|
3
3
|
* RouterView 组件
|
|
4
4
|
*
|
|
@@ -40,26 +40,24 @@ export class RouterView extends Fukict {
|
|
|
40
40
|
const matched = this.props.router.matched;
|
|
41
41
|
// 没有匹配的路由,渲染 fallback
|
|
42
42
|
if (!matched || !matched.component) {
|
|
43
|
-
return this.props.fallback ||
|
|
43
|
+
return (this.props.fallback || {
|
|
44
|
+
type: 'div',
|
|
45
|
+
__type__: VNodeType.Element,
|
|
46
|
+
props: {
|
|
47
|
+
class: 'router-view',
|
|
48
|
+
},
|
|
49
|
+
children: [],
|
|
50
|
+
});
|
|
44
51
|
}
|
|
45
52
|
const RouteComp = matched.component;
|
|
46
53
|
const childRouter = this.getRouterForChild(matched);
|
|
47
54
|
return {
|
|
48
|
-
type:
|
|
49
|
-
__type__: VNodeType.
|
|
55
|
+
type: RouteComp,
|
|
56
|
+
__type__: VNodeType.ClassComponent,
|
|
50
57
|
props: {
|
|
51
|
-
|
|
58
|
+
router: childRouter,
|
|
52
59
|
},
|
|
53
|
-
children: [
|
|
54
|
-
{
|
|
55
|
-
type: RouteComp,
|
|
56
|
-
__type__: VNodeType.ClassComponent,
|
|
57
|
-
props: {
|
|
58
|
-
router: childRouter,
|
|
59
|
-
},
|
|
60
|
-
children: [],
|
|
61
|
-
},
|
|
62
|
-
],
|
|
60
|
+
children: [],
|
|
63
61
|
};
|
|
64
62
|
}
|
|
65
63
|
}
|
package/dist/RouterView.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RouterView.js","sourceRoot":"","sources":["../src/RouterView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAc,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"RouterView.js","sourceRoot":"","sources":["../src/RouterView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAc,SAAS,EAAE,MAAM,eAAe,CAAC;AAK9D;;;;;GAKG;AACH,MAAM,OAAO,UAAW,SAAQ,MAAuB;IACrD;;OAEG;IACK,WAAW,GAAkB,IAAI,CAAC;IAE1C;;OAEG;IACH,aAAa;QACX,QAAQ;QACR,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED;;OAEG;IACK,iBAAiB,CAAC,OAAoB;QAC5C,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpD,cAAc;YACd,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBACtB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACrD,CAAC;YACD,kDAAkD;YAClD,IAAI,CAAC,WAAW,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC;YAC/D,OAAO,IAAI,CAAC,WAAW,CAAC;QAC1B,CAAC;QAED,oBAAoB;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,MAAM;QACJ,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC;QAE1C,sBAAsB;QACtB,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YACnC,OAAO,CACL,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI;gBACrB,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,SAAS,CAAC,OAAO;gBAC3B,KAAK,EAAE;oBACL,KAAK,EAAE,aAAa;iBACrB;gBACD,QAAQ,EAAE,EAAE;aACb,CACF,CAAC;QACJ,CAAC;QAED,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACpC,MAAM,WAAW,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAEpD,OAAO;YACL,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE,SAAS,CAAC,cAAc;YAClC,KAAK,EAAE;gBACL,MAAM,EAAE,WAAW;aACpB;YACD,QAAQ,EAAE,EAAE;SACb,CAAC;IACJ,CAAC;CACF"}
|
package/dist/metadata.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export declare const METADATA: {
|
|
6
6
|
readonly name: "@fukict/router";
|
|
7
|
-
readonly version: "0.1.
|
|
7
|
+
readonly version: "0.1.2";
|
|
8
8
|
readonly description: "Router for Fukict framework with nested routing and lazy loading";
|
|
9
9
|
readonly author: "Fukict Team";
|
|
10
10
|
readonly license: "MIT";
|
package/dist/metadata.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fukict/router",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Router for Fukict framework with nested routing and lazy loading",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fukict",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"README.md"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@fukict/basic": "0.1.
|
|
40
|
+
"@fukict/basic": "0.1.2"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"typescript": "^5.6.3"
|
|
@@ -45,6 +45,9 @@
|
|
|
45
45
|
"engines": {
|
|
46
46
|
"node": ">=16.0.0"
|
|
47
47
|
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"registry": "https://registry.npmjs.org/"
|
|
50
|
+
},
|
|
48
51
|
"scripts": {
|
|
49
52
|
"build": "tsx ../../scripts/build-package.ts --pkg-name router --no-watch",
|
|
50
53
|
"dev": "tsx ../../scripts/build-package.ts --pkg-name router --watch",
|