@ktjs/router 0.32.4 → 0.32.5
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 +24 -277
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,292 +1,39 @@
|
|
|
1
|
-
#
|
|
1
|
+
# KT.js
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/kt.js)
|
|
4
|
+
[](https://www.npmjs.com/package/kt.js)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://baendlorel.github.io/kt.js/">
|
|
9
|
+
<img src="https://raw.githubusercontent.com/baendlorel/kt.js/refs/heads/main/assets/ktjs-0.0.1.svg" width="240px" alt="KT.js logo" />
|
|
10
|
+
</a>
|
|
11
|
+
</p>
|
|
6
12
|
|
|
7
|
-
>
|
|
13
|
+
<p align="center"><strong>Visit KT.js: <a href="https://baendlorel.github.io/kt.js/">https://baendlorel.github.io/kt.js/</a></strong></p>
|
|
8
14
|
|
|
9
|
-
|
|
15
|
+
## Recent Updates
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
1. Special refs for `Array`, `Set`, `Map`, `WeakMap`, `WeakSet`, `Date` to better track mutations.
|
|
18
|
+
- e.g. `const a = ref.array([1, 2])`, then we can call `a.push(3)` to make a reactive change instead of `a.value.push(3)`.
|
|
19
|
+
2. Fixed issues of `svg` and `mathml` elements.
|
|
12
20
|
|
|
13
|
-
##
|
|
21
|
+
## Community
|
|
14
22
|
|
|
15
|
-
|
|
23
|
+
- QQ Group: `1070434849`
|
|
24
|
+
- Telegram: https://t.me/kt_js
|
|
16
25
|
|
|
17
|
-
##
|
|
26
|
+
## Introduction
|
|
18
27
|
|
|
19
|
-
|
|
20
|
-
- **Path Matching**: Static and dynamic route matching with parameter extraction
|
|
21
|
-
- Dynamic segments: `/user/:id`
|
|
22
|
-
- Wildcard matching support
|
|
23
|
-
- Optimized matching algorithm with pre-flattened routes
|
|
24
|
-
- **Async Navigation Guards**: Control navigation flow with Promise-based guard system
|
|
25
|
-
- `beforeEach`: Global guard before every navigation
|
|
26
|
-
- `beforeEnter`: Per-route guard for specific routes
|
|
27
|
-
- `afterEach`: Global hook after successful navigation
|
|
28
|
-
- Guard-level control with bitwise operations for fine-grained execution
|
|
29
|
-
- All guards are async - return `Promise` or immediate values
|
|
30
|
-
- **Named Routes**: Navigate using route names instead of paths
|
|
31
|
-
- **Query Parameters**: Built-in query string parsing and handling
|
|
32
|
-
- **Route Context**: Access route information in handlers
|
|
33
|
-
- Current route path and name
|
|
34
|
-
- Dynamic parameters (`params`)
|
|
35
|
-
- Query string parameters (`query`)
|
|
36
|
-
- **Error Handling**: Comprehensive error handling with `onError` callback
|
|
37
|
-
- **Type-Safe**: Full TypeScript support with intelligent type inference
|
|
38
|
-
- **Zero Dependencies**: Fully self-contained implementation (does **not** require `@ktjs/core` for runtime, only for TypeScript types)
|
|
39
|
-
- **ES5 Compatible**: Works in IE9+ and all modern browsers
|
|
28
|
+
kt.js is a simple framework with a tiny runtime that renders real DOM directly (no virtual DOM), uses explicit reactivity variables and gives you manual control over refs, bindings, and redraw timing.
|
|
40
29
|
|
|
41
|
-
|
|
30
|
+
KT.js focuses on one principle: keep direct control of the DOM and avoid unnecessary repainting.
|
|
42
31
|
|
|
43
|
-
|
|
32
|
+
## Quick Start
|
|
44
33
|
|
|
45
34
|
```bash
|
|
46
35
|
pnpm create kt.js my-app
|
|
36
|
+
cd my-app
|
|
37
|
+
pnpm install
|
|
38
|
+
pnpm dev
|
|
47
39
|
```
|
|
48
|
-
|
|
49
|
-
### Add router to an existing project
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
pnpm add @ktjs/router @ktjs/core
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Basic Usage
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
import { createRouter } from '@ktjs/router';
|
|
59
|
-
import { div, h1 } from '@ktjs/core';
|
|
60
|
-
|
|
61
|
-
const router = createRouter({
|
|
62
|
-
routes: [
|
|
63
|
-
{
|
|
64
|
-
path: '/',
|
|
65
|
-
name: 'home',
|
|
66
|
-
beforeEnter: (to) => {
|
|
67
|
-
// Render your home page
|
|
68
|
-
const app = document.getElementById('app')!;
|
|
69
|
-
app.innerHTML = '';
|
|
70
|
-
app.appendChild(div({}, [h1({}, 'Home Page')]));
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
path: '/about',
|
|
75
|
-
name: 'about',
|
|
76
|
-
beforeEnter: (to) => {
|
|
77
|
-
// Render your about page
|
|
78
|
-
const app = document.getElementById('app')!;
|
|
79
|
-
app.innerHTML = '';
|
|
80
|
-
app.appendChild(div({}, [h1({}, 'About Page')]));
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
path: '/user/:id',
|
|
85
|
-
name: 'user',
|
|
86
|
-
beforeEnter: (to) => {
|
|
87
|
-
// Render user profile with params
|
|
88
|
-
const app = document.getElementById('app')!;
|
|
89
|
-
app.innerHTML = '';
|
|
90
|
-
app.appendChild(
|
|
91
|
-
div({}, [
|
|
92
|
-
h1({}, `User Profile`),
|
|
93
|
-
div({}, `User ID: ${to.params.id}`),
|
|
94
|
-
div({}, `Query: ${JSON.stringify(to.query)}`),
|
|
95
|
-
]),
|
|
96
|
-
);
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
],
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
// Navigate programmatically
|
|
103
|
-
router.push('/user/123?tab=profile');
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
## Advanced Usage
|
|
107
|
-
|
|
108
|
-
### Navigation Guards
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
111
|
-
import { createRouter, GuardLevel } from '@ktjs/router';
|
|
112
|
-
|
|
113
|
-
const router = createRouter({
|
|
114
|
-
routes: [
|
|
115
|
-
{
|
|
116
|
-
path: '/admin',
|
|
117
|
-
name: 'admin',
|
|
118
|
-
beforeEnter: (to) => {
|
|
119
|
-
// Route-specific guard and rendering
|
|
120
|
-
if (!isAuthenticated()) {
|
|
121
|
-
console.log('Access denied');
|
|
122
|
-
return false; // Block navigation
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Render admin panel
|
|
126
|
-
const app = document.getElementById('app')!;
|
|
127
|
-
app.innerHTML = '';
|
|
128
|
-
app.appendChild(div({}, 'Admin Panel'));
|
|
129
|
-
return true;
|
|
130
|
-
},
|
|
131
|
-
after: (to) => {
|
|
132
|
-
// Called after this route is successfully entered
|
|
133
|
-
console.log('Admin page rendered');
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
],
|
|
137
|
-
beforeEach: async (to, from) => {
|
|
138
|
-
// Global guard - can be async
|
|
139
|
-
console.log(`Navigating from ${from?.path} to ${to.path}`);
|
|
140
|
-
|
|
141
|
-
// You can return false to block navigation
|
|
142
|
-
if (to.path.includes('forbidden')) {
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Or return true to allow
|
|
147
|
-
return true;
|
|
148
|
-
},
|
|
149
|
-
afterEach: (to, from) => {
|
|
150
|
-
// Called after successful navigation
|
|
151
|
-
document.title = to.name || to.path;
|
|
152
|
-
|
|
153
|
-
// Track page views
|
|
154
|
-
analytics.track('pageview', { path: to.path });
|
|
155
|
-
},
|
|
156
|
-
onError: (error) => {
|
|
157
|
-
console.error('Navigation error:', error);
|
|
158
|
-
},
|
|
159
|
-
onNotFound: (path) => {
|
|
160
|
-
console.log('404:', path);
|
|
161
|
-
// You can render a 404 page here
|
|
162
|
-
},
|
|
163
|
-
});
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### Named Route Navigation
|
|
167
|
-
|
|
168
|
-
```typescript
|
|
169
|
-
// Navigate by route name with parameters
|
|
170
|
-
router.push({
|
|
171
|
-
name: 'user',
|
|
172
|
-
params: { id: '456' },
|
|
173
|
-
query: { tab: 'settings' },
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// Equivalent to: router.push('/user/456?tab=settings');
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
### Accessing Current Route
|
|
180
|
-
|
|
181
|
-
```typescript
|
|
182
|
-
const current = router.current;
|
|
183
|
-
|
|
184
|
-
if (current) {
|
|
185
|
-
console.log('Path:', current.path); // e.g., '/user/123'
|
|
186
|
-
console.log('Name:', current.name); // e.g., 'user'
|
|
187
|
-
console.log('Params:', current.params); // e.g., { id: '123' }
|
|
188
|
-
console.log('Query:', current.query); // e.g., { tab: 'profile' }
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
## API Reference
|
|
193
|
-
|
|
194
|
-
### `createRouter(config)`
|
|
195
|
-
|
|
196
|
-
Creates and returns a router instance.
|
|
197
|
-
|
|
198
|
-
**Config Options:**
|
|
199
|
-
|
|
200
|
-
- `routes` (Array): Array of route configurations
|
|
201
|
-
- `path` (string): Route path with optional dynamic segments (`:param`)
|
|
202
|
-
- `name` (string, optional): Route name for named navigation
|
|
203
|
-
- `meta` (object, optional): Metadata attached to the route
|
|
204
|
-
- `component` (function): Function that returns HTMLElement or Promise<HTMLElement>
|
|
205
|
-
- `beforeEnter` (function, optional): Route-specific guard, receives `(to: RouteContext) => boolean | string | NavOptions | void | Promise<...>`
|
|
206
|
-
- `after` (function, optional): Route-specific hook after navigation
|
|
207
|
-
- `children` (array, optional): Nested child routes
|
|
208
|
-
- `beforeEach` (function, optional): Global guard before every navigation, receives `(to: RouteContext, from: RouteContext | null) => boolean | string | NavOptions | void | Promise<...>`
|
|
209
|
-
- `afterEach` (function, optional): Global hook after successful navigation, receives `(to: RouteContext, from: RouteContext | null) => void | Promise<void>`
|
|
210
|
-
- `onNotFound` (function, optional): Handler for 404 errors, receives `(path: string) => void`
|
|
211
|
-
- `onError` (function, optional): Error handler for navigation failures, receives `(error: Error) => void`
|
|
212
|
-
- `prefix` (string, optional): URL prefix for all routes (default: `''`)
|
|
213
|
-
|
|
214
|
-
**Router Instance Properties:**
|
|
215
|
-
|
|
216
|
-
- `current` (property): Current active route context (or `null`)
|
|
217
|
-
- `history` (property): Array of navigation history
|
|
218
|
-
- `routes` (property): Normalized route configurations
|
|
219
|
-
|
|
220
|
-
**Router Instance Methods:**
|
|
221
|
-
|
|
222
|
-
- `push(location)`: Navigate to a new location (string path or route object with `name`/`params`)
|
|
223
|
-
- `replace(location)`: Replace current history entry
|
|
224
|
-
- `back()`: Navigate back in history
|
|
225
|
-
- `listen()`: Start listening to hash changes (auto-called on router creation since v0.14.7)
|
|
226
|
-
- `init(routes)`: Initialize router with route configurations
|
|
227
|
-
|
|
228
|
-
### Navigation Guards (v0.14.7+)
|
|
229
|
-
|
|
230
|
-
All guards are **async** and can return:
|
|
231
|
-
|
|
232
|
-
- `false`: Cancel navigation
|
|
233
|
-
- `string`: Redirect to the given path
|
|
234
|
-
- `NavOptions` object: Redirect with options `{ name, params, query }`
|
|
235
|
-
- `void` or `undefined`: Continue navigation
|
|
236
|
-
|
|
237
|
-
```typescript
|
|
238
|
-
// Global guard
|
|
239
|
-
beforeEach: (async (to, from) => {
|
|
240
|
-
// Check authentication
|
|
241
|
-
const isAuthenticated = await checkAuth();
|
|
242
|
-
if (!isAuthenticated && to.path !== '/login') {
|
|
243
|
-
return '/login'; // Redirect to login
|
|
244
|
-
}
|
|
245
|
-
// Return nothing to continue
|
|
246
|
-
},
|
|
247
|
-
// Route guard
|
|
248
|
-
{
|
|
249
|
-
path: '/admin',
|
|
250
|
-
component: () => AdminPage(),
|
|
251
|
-
beforeEnter: async (to) => {
|
|
252
|
-
const hasPermission = await checkAdminPermission();
|
|
253
|
-
if (!hasPermission) {
|
|
254
|
-
return false; // Cancel navigation
|
|
255
|
-
}
|
|
256
|
-
},
|
|
257
|
-
});
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### Route Context
|
|
261
|
-
|
|
262
|
-
Guards and hooks receive a `RouteContext` object with:
|
|
263
|
-
|
|
264
|
-
- `params`: Object containing dynamic route parameters
|
|
265
|
-
- `query`: Object containing query string parameters
|
|
266
|
-
- `path`: Current route path
|
|
267
|
-
- `name`: Current route name (if defined)
|
|
268
|
-
|
|
269
|
-
## Performance Optimizations
|
|
270
|
-
|
|
271
|
-
The router includes several performance optimizations:
|
|
272
|
-
|
|
273
|
-
- **Hash-Mode Only**: Simplified implementation focusing on hash-based routing (v0.14.7+)
|
|
274
|
-
- **Pre-flattened Routes**: Nested routes are flattened during initialization for faster lookups
|
|
275
|
-
- **Efficient Matching**: Optimized regex-based path matching with caching
|
|
276
|
-
- **Async Guards**: All guards use Promise-based architecture for consistent async handling
|
|
277
|
-
- **Guard Level Control**: Fine-grained control over guard execution using bitwise operations
|
|
278
|
-
- **Automatic Initialization**: Router auto-initializes on creation, no manual setup needed
|
|
279
|
-
|
|
280
|
-
## Browser Compatibility
|
|
281
|
-
|
|
282
|
-
Works in all modern browsers that support:
|
|
283
|
-
|
|
284
|
-
- Hash-based navigation
|
|
285
|
-
- ES5 (with transpilation)
|
|
286
|
-
- Promise API (required for async guards)
|
|
287
|
-
|
|
288
|
-
For older browsers without Promise support, include a Promise polyfill.
|
|
289
|
-
|
|
290
|
-
## License
|
|
291
|
-
|
|
292
|
-
MIT
|