weifuwu 0.74.0 → 0.75.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 +14 -14
- package/dist/ai/types.d.ts +1 -1
- package/dist/components/Button/Button.d.ts +2 -0
- package/dist/components/index.js +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +2 -123
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui-dom/ai.d.ts +1 -1
- package/dist/ui-dom/browser.d.ts +1 -1
- package/dist/ui-dom/diff.d.ts +1 -1
- package/dist/ui-dom/focus-trap.d.ts +1 -1
- package/dist/ui-dom/hydration.d.ts +1 -1
- package/dist/ui-dom/index.d.ts +1 -1
- package/dist/ui-dom/index.js +10 -8
- package/dist/ui-dom/middleware/api.d.ts +3 -3
- package/dist/ui-dom/middleware/auth.d.ts +1 -1
- package/dist/ui-dom/motion.d.ts +1 -1
- package/dist/ui-dom/popup.d.ts +1 -1
- package/dist/ui-dom/registry.d.ts +1 -13
- package/dist/ui-dom/render.d.ts +1 -1
- package/dist/ui-dom/scroll-lock.d.ts +1 -1
- package/dist/ui-dom/ssr.d.ts +1 -0
- package/dist/ui-dom/types.d.ts +6 -6
- package/dist/ui-dom/ui.d.ts +1 -1
- package/dist/ui-dom/vnode.d.ts +4 -20
- package/dist/user/token.d.ts +1 -1
- package/docs/compat-three-library.md +1 -1
- package/docs/components-map.md +1 -1
- package/docs/custom-components.md +5 -6
- package/docs/data.md +16 -33
- package/docs/environment.md +1 -1
- package/docs/frontend-middleware.md +65 -79
- package/docs/frontend-ui-dom.md +4 -2
- package/docs/frontend.md +35 -38
- package/docs/realtime.md +23 -28
- package/docs/saas.md +8 -6
- package/package.json +1 -1
- package/dist/ui/ssr-page.d.ts +0 -41
- package/dist/ui-dom/route-match.d.ts +0 -22
package/docs/frontend.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 前端 API 核心(weifuwu/ui-dom)
|
|
2
2
|
|
|
3
|
-
> ⚠️ **`weifuwu/client`
|
|
3
|
+
> ⚠️ **`weifuwu/client` 已并入 `weifuwu/ui-dom`**(`src/client/` 已删除)——本页 import 均用 `weifuwu/ui-dom`。前端运行时唯一入口是 **`weifuwu/ui-dom`**(UIRouter 纯路由 + uiServe 渲染运行时 + SSR/hydration),权威参考见 **[docs/frontend-ui-dom.md](frontend-ui-dom.md)**。
|
|
4
4
|
|
|
5
5
|
> 以下为完整 API 参考,按需查阅。新手建议先阅读 README 的「核心概念」和「快速开始」。
|
|
6
6
|
|
|
@@ -13,40 +13,37 @@
|
|
|
13
13
|
```js
|
|
14
14
|
esbuild.build({
|
|
15
15
|
jsx: 'automatic',
|
|
16
|
-
jsxImportSource: 'weifuwu/
|
|
16
|
+
jsxImportSource: 'weifuwu/ui-dom',
|
|
17
17
|
bundle: true,
|
|
18
18
|
})
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## 应用引导(UIRouter + uiServe)
|
|
24
24
|
|
|
25
25
|
```tsx
|
|
26
|
-
import {
|
|
26
|
+
import { UIRouter, uiServe } from 'weifuwu/ui-dom'
|
|
27
27
|
|
|
28
|
-
const app =
|
|
28
|
+
const app = new UIRouter()
|
|
29
29
|
|
|
30
|
-
//
|
|
30
|
+
// 注册中间件(ctx 注入)
|
|
31
31
|
app.use(middleware1)
|
|
32
32
|
app.use(middleware2)
|
|
33
33
|
|
|
34
|
-
//
|
|
35
|
-
app.
|
|
34
|
+
// 页面路由(handler = 异步组件)
|
|
35
|
+
app.get('/', () => h(Home, {}))
|
|
36
36
|
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// 销毁
|
|
41
|
-
app.destroy()
|
|
37
|
+
// 落地:客户端渲染(hydrate: true 收养 SSR HTML)
|
|
38
|
+
const handle = uiServe(app, { root: '#root' })
|
|
42
39
|
```
|
|
43
40
|
|
|
44
|
-
|
|
|
41
|
+
| API | 说明 |
|
|
45
42
|
|------|------|
|
|
46
|
-
| `
|
|
47
|
-
| `app.use(mw)` |
|
|
48
|
-
| `app.
|
|
49
|
-
| `app.
|
|
43
|
+
| `new UIRouter()` | 创建路由实例 |
|
|
44
|
+
| `app.use(mw)` | 注册中间件(AppMiddleware / UIMiddleware) |
|
|
45
|
+
| `app.get(path, handler)` / `use(prefix, sub)` / `notFound(handler)` | 页面路由 / 子路由树 / 404 |
|
|
46
|
+
| `uiServe(app, { root, hydrate })` | 客户端落地(返回 handle,`handle.close()` 卸载) |
|
|
50
47
|
| `app.ctx` | 当前 WfuiContext |
|
|
51
48
|
|
|
52
49
|
---
|
|
@@ -54,7 +51,7 @@ app.destroy()
|
|
|
54
51
|
## 组件模型
|
|
55
52
|
|
|
56
53
|
```tsx
|
|
57
|
-
import type { Component, WfuiContext } from 'weifuwu/
|
|
54
|
+
import type { Component, WfuiContext } from 'weifuwu/ui-dom'
|
|
58
55
|
|
|
59
56
|
// 两阶段组件:mount(只一次)→ render(每次 dirty/props 变化)
|
|
60
57
|
const Counter: Component = (_init, ctx) => {
|
|
@@ -74,8 +71,8 @@ const Badge: Component = () =>
|
|
|
74
71
|
### 类型流(props 泛型 + ctx 注入)
|
|
75
72
|
|
|
76
73
|
```tsx
|
|
77
|
-
import type { Component } from 'weifuwu/
|
|
78
|
-
import type { ApiInjected, RouteInjected } from 'weifuwu/
|
|
74
|
+
import type { Component } from 'weifuwu/ui-dom'
|
|
75
|
+
import type { ApiInjected, RouteInjected } from 'weifuwu/ui-dom'
|
|
79
76
|
|
|
80
77
|
// ① props 泛型:JSX 使用时自动类型检查(传错类型编译期报错)
|
|
81
78
|
interface DeckCardProps { title: string; pages: number }
|
|
@@ -92,13 +89,13 @@ const Home: Component<{}, ApiInjected & RouteInjected> = (_init, ctx) => {
|
|
|
92
89
|
}
|
|
93
90
|
// 未声明的注入字段编译期报错——注入从"文档约定"变成"类型保证"
|
|
94
91
|
|
|
95
|
-
|
|
92
|
+
const app = new UIRouter<{}>()
|
|
96
93
|
.use(api()) // 注入 ctx.api
|
|
97
|
-
.use(
|
|
98
|
-
|
|
94
|
+
.use(toast()) // 注入 ctx.toast
|
|
95
|
+
uiServe(app, { root: '#root' }) // 类型累积完整(UIRouter<C & O>)
|
|
99
96
|
```
|
|
100
97
|
|
|
101
|
-
> 各中间件的注入接口:`api()` → `ApiInjected`、`auth()` → `AuthInjected`、`ws()` → `WsInjected`、`i18n()` → `I18nInjected`、`router()` → `RouteInjected`(均可从 `weifuwu/
|
|
98
|
+
> 各中间件的注入接口:`api()` → `ApiInjected`、`auth()` → `AuthInjected`、`ws()` → `WsInjected`、`i18n()` → `I18nInjected`、`router()` → `RouteInjected`(均可从 `weifuwu/ui-dom` 导入)。
|
|
102
99
|
|
|
103
100
|
| 规则 | 说明 |
|
|
104
101
|
|------|------|
|
|
@@ -113,8 +110,8 @@ createApp()
|
|
|
113
110
|
### JSX 工厂
|
|
114
111
|
|
|
115
112
|
```tsx
|
|
116
|
-
// 由 esbuild 自动调用(jsxImportSource: 'weifuwu/
|
|
117
|
-
import { h, jsx, jsxs, jsxDEV, Fragment } from 'weifuwu/
|
|
113
|
+
// 由 esbuild 自动调用(jsxImportSource: 'weifuwu/ui-dom')
|
|
114
|
+
import { h, jsx, jsxs, jsxDEV, Fragment } from 'weifuwu/ui-dom'
|
|
118
115
|
|
|
119
116
|
// h 支持 variadic children
|
|
120
117
|
h('div', { class: 'x' }, child1, child2)
|
|
@@ -131,7 +128,7 @@ h('div', { class: 'x' }, child1, child2)
|
|
|
131
128
|
| `Portal` / `createPortal(children, portalKey?)` | 渲染到 `document.body#__wf_portal` 独立容器(弹层/对话框,脱离父级 overflow 裁剪) |
|
|
132
129
|
|
|
133
130
|
```tsx
|
|
134
|
-
import { createPortal } from 'weifuwu/
|
|
131
|
+
import { createPortal } from 'weifuwu/ui-dom'
|
|
135
132
|
|
|
136
133
|
// 内容渲染到 body 下的独立容器(不在父组件的 DOM 树内)
|
|
137
134
|
const Tooltip = (_init, ctx) =>
|
|
@@ -754,7 +751,7 @@ const UserProfile: Component = (initProps, ctx) => {
|
|
|
754
751
|
}
|
|
755
752
|
```
|
|
756
753
|
|
|
757
|
-
### async
|
|
754
|
+
### async 组件(原生)
|
|
758
755
|
|
|
759
756
|
组件 = 函数,async 组件 = async 函数:签名与同步组件一致 `(initProps, ctx) => renderFn`,渲染器按「返回值是 Promise」原生判别。数据经闭包注入,渲染无 loading 分支:
|
|
760
757
|
|
|
@@ -771,9 +768,9 @@ const UserProfile = async (initProps, ctx) => {
|
|
|
771
768
|
}
|
|
772
769
|
```
|
|
773
770
|
|
|
774
|
-
-
|
|
771
|
+
- **客户端**:首次渲染占位(`Placeholder`)→ 工厂 resolve 后整树重渲染补全;`_asyncDef` 按实例缓存(diff 传递继承,补全不重跑工厂)——N 处实例 = N 次工厂调用,数据走 `ctx.data` 则零成本(缓存 + 并发合并)
|
|
775
772
|
- **服务端**:`ctx.ui.ssr()` 直接 await 工厂 → 数据进 HTML(无占位)
|
|
776
|
-
-
|
|
773
|
+
- **占位显示**:无边界 → null;`<Suspense fallback={...}>` 边界 → 子树内占位处显示 fallback(可选)
|
|
777
774
|
- 会变的数据:初始值 seed 自服务端数据(`$.count = data.count`),交互改 `$`;初始状态必须确定性(禁止 `window.innerWidth` 直接初始化 → SSR/hydration mismatch)
|
|
778
775
|
|
|
779
776
|
---
|
|
@@ -781,15 +778,15 @@ const UserProfile = async (initProps, ctx) => {
|
|
|
781
778
|
## 前端类型
|
|
782
779
|
|
|
783
780
|
```tsx
|
|
784
|
-
import type { VNode, VNodeType, Component, WfuiContext, AppMiddleware, RouteDef } from 'weifuwu/
|
|
785
|
-
import type { ApiClient, ApiOptions, ApiRequestOptions, ApiError } from 'weifuwu/
|
|
786
|
-
import type { AuthClient, AuthOptions } from 'weifuwu/
|
|
787
|
-
import type { ErrorBoundaryProps } from 'weifuwu/
|
|
788
|
-
import type { I18nOptions, I18nState, LocalePackage } from 'weifuwu/
|
|
789
|
-
import type { PopupPositionOptions, PopupPosition } from 'weifuwu/
|
|
781
|
+
import type { VNode, VNodeType, Component, WfuiContext, AppMiddleware, RouteDef } from 'weifuwu/ui-dom'
|
|
782
|
+
import type { ApiClient, ApiOptions, ApiRequestOptions, ApiError } from 'weifuwu/ui-dom'
|
|
783
|
+
import type { AuthClient, AuthOptions } from 'weifuwu/ui-dom'
|
|
784
|
+
import type { ErrorBoundaryProps } from 'weifuwu/ui-dom'
|
|
785
|
+
import type { I18nOptions, I18nState, LocalePackage } from 'weifuwu/ui-dom'
|
|
786
|
+
import type { PopupPositionOptions, PopupPosition } from 'weifuwu/ui-dom'
|
|
790
787
|
import type { ConfirmProps, ConfirmOptions } from 'weifuwu/components'
|
|
791
788
|
import type { ToastOptions, ToastPosition } from 'weifuwu/components'
|
|
792
|
-
import type { RouterOptions } from 'weifuwu/
|
|
789
|
+
import type { RouterOptions } from 'weifuwu/ui-dom'
|
|
793
790
|
```
|
|
794
791
|
|
|
795
792
|
| 类型 | 说明 |
|
package/docs/realtime.md
CHANGED
|
@@ -70,13 +70,12 @@ app.get('/page', (req, ctx) => ctx.ui.html`
|
|
|
70
70
|
```ts
|
|
71
71
|
app.get('/app.js', (req, ctx) => ctx.ui.js('./src/main.tsx')) // 相对路径
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
app.get('/app.js', (req, ctx) => ctx.ui.js('weifuwu/client')) // 或包名
|
|
73
|
+
app.get('/app.js', (req, ctx) => ctx.ui.js('weifuwu/ui-dom')) // 或包名(exports map 解析)
|
|
75
74
|
```
|
|
76
75
|
|
|
77
76
|
使用 esbuild 编译:
|
|
78
77
|
- `bundle: true`, `format: 'esm'`, `platform: 'browser'`
|
|
79
|
-
- `jsx: 'automatic'`, `jsxImportSource: 'weifuwu/
|
|
78
|
+
- `jsx: 'automatic'`, `jsxImportSource: 'weifuwu/ui-dom'`
|
|
80
79
|
- 带 mtime 缓存验证(开发时编辑文件后自动失效)
|
|
81
80
|
|
|
82
81
|
### ctx.ui.css — CSS 编译
|
|
@@ -129,10 +128,11 @@ app.get('/blog/:slug', async (req, ctx) => {
|
|
|
129
128
|
服务端 HTML + `window.__DATA__`(ctx.data 种子)到达客户端后,`mount(..., { hydrate: true })` **收养现有 DOM**(不重建、不闪跳),只接线事件/ref/$:
|
|
130
129
|
|
|
131
130
|
```ts
|
|
132
|
-
import {
|
|
131
|
+
import { UIRouter, uiServe } from 'weifuwu/ui-dom'
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
const app = new UIRouter()
|
|
134
|
+
app.get('/blog/:slug', (loc, ctx) => h(BlogPage, { slug: ctx.params.slug }))
|
|
135
|
+
uiServe(app, { root: '#root', hydrate: true }) // 容器已有服务端 HTML → 收养
|
|
136
136
|
```
|
|
137
137
|
|
|
138
138
|
- **游标收养**:元素/文本按位置匹配现有 DOM;tag 不匹配 → 局部替换;文本不一致 → 就地修正;服务端多余节点 → 收尾清理
|
|
@@ -140,32 +140,27 @@ createApp()
|
|
|
140
140
|
- hydration 后 `$`/dirty/事件全量可用(与纯 SPA 无差别)
|
|
141
141
|
- 诚实裁剪:Portal 内容就地收养(不移动到 `#__wf_portal`);渲染期非确定性(Date/random)会导致 mismatch(dev 警告)
|
|
142
142
|
|
|
143
|
-
###
|
|
143
|
+
### 路由级 SSR(UIRouter 两端共享)
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
同一份 UIRouter 路由定义,后端匹配即自动 SSR,无需手写 handler/模板/序列化:
|
|
146
146
|
|
|
147
147
|
```tsx
|
|
148
|
-
//
|
|
149
|
-
import
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// blog-hydrate.ts —— 客户端:同一份 routes,router() 注入 ctx.route.params(两端同源)
|
|
161
|
-
createApp()
|
|
162
|
-
.use(router({ routes }))
|
|
163
|
-
.mount('#root', routes[0].component, { hydrate: true })
|
|
148
|
+
// router.ts —— 前后端共用
|
|
149
|
+
import { UIRouter, h } from 'weifuwu/ui-dom'
|
|
150
|
+
|
|
151
|
+
export const app = new UIRouter()
|
|
152
|
+
app.get('/blog/:slug', (loc, ctx) => h(BlogPage, { slug: ctx.params.slug }))
|
|
153
|
+
|
|
154
|
+
// server.ts —— ssrPage 服务端落地:完整 HTML + __DATA__ + styles
|
|
155
|
+
import { ssrPage } from 'weifuwu/ui-dom'
|
|
156
|
+
const { page } = await ssrPage(app, { url: '/blog/hello', styles: ['/components.css'] })
|
|
157
|
+
|
|
158
|
+
// client.ts —— uiServe 客户端收养(hydrate: true)
|
|
159
|
+
uiServe(app, { root: '#root', hydrate: true })
|
|
164
160
|
```
|
|
165
161
|
|
|
166
|
-
- 组件工厂读 `ctx.
|
|
167
|
-
-
|
|
168
|
-
- 可自定义 `title` / `template`
|
|
162
|
+
- 组件工厂读 `ctx.params`(`/blog/:slug` → `ctx.params.slug`)——两端同源注入
|
|
163
|
+
- `ssrPage` 选项:`styles`(stylesheet link)/ `title` / `lang` / `rootId`
|
|
169
164
|
|
|
170
165
|
### weifuwu/dev — 服务端直接跑 .tsx
|
|
171
166
|
|
|
@@ -180,7 +175,7 @@ Node 原生 TS 只剥离类型(不支持 JSX)。`weifuwu/dev` 注册 esbuild
|
|
|
180
175
|
}
|
|
181
176
|
```
|
|
182
177
|
|
|
183
|
-
- 前后端同一 JSX 运行时(`jsxImportSource: weifuwu/
|
|
178
|
+
- 前后端同一 JSX 运行时(`jsxImportSource: weifuwu/ui-dom`)→ 两端 VNode 一致 → hydration 可靠
|
|
184
179
|
- 与 `ctx.ui.js` 前端动态编译同一理念:无构建、无产物、改代码即生效
|
|
185
180
|
|
|
186
181
|
---
|
package/docs/saas.md
CHANGED
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
```ts
|
|
13
13
|
import { rateLimit } from 'weifuwu'
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
app.use(
|
|
15
|
+
const r = redis() // 注意:REDIS_URL 缺 env 构造抛错
|
|
16
|
+
app.use(r) // 依赖 ctx.redis
|
|
17
|
+
app.use(rateLimit({ redis: r.redis, windowMs: 60_000, max: 100 })) // redis 必传(模式 A 显式注入)
|
|
17
18
|
|
|
18
19
|
app.get('/api/search', async (req, ctx) => {
|
|
19
20
|
await ctx.limit('search', { max: 30, windowMs: 60_000 }) // 手动限流,超限抛 429
|
|
@@ -77,7 +78,7 @@ app.post('/secure', (req, ctx) => { ctx.auth.requireAuth(); ... })
|
|
|
77
78
|
|
|
78
79
|
|
|
79
80
|
> ⚠️ **weifuwu/client 已删除**——前端运行时唯一入口为 `weifuwu/ui-dom`,见 [frontend-ui-dom.md](frontend-ui-dom.md)。
|
|
80
|
-
- **安全基线**:scrypt 密码哈希(per-user salt + timing-safe,异步不阻塞);access token = HMAC-SHA256 JWT(与 `weifuwu/
|
|
81
|
+
- **安全基线**:scrypt 密码哈希(per-user salt + timing-safe,异步不阻塞);access token = HMAC-SHA256 JWT(与 `weifuwu/ui-dom` 的 `auth()` 天然配对);refresh token = 不透明随机串,DB 只存哈希,logout/轮换即撤销
|
|
81
82
|
- **防枚举**:登录失败统一 401(不泄露邮箱是否存在)
|
|
82
83
|
- **`ctx.auth` 方法面**:`register` / `login` / `logout` / `requireAuth` / `setPassword(userId, newPwd)` / `createToken(type, payload, { ttlSeconds })`(邮箱验证/密码重置自接)
|
|
83
84
|
- **多租户感知**:`issueSession` 的 token payload 携带 `tenantId`(来自 `user.tenant`)——中间件自动注入 `ctx.tenantId`,并将会话字段(userId/tenantId/email/name/role)合并到 `ctx.auth`,多租户应用免写 token 解码/租户中间件(数据隔离 SQL 是应用职责)
|
|
@@ -117,7 +118,8 @@ ctx.msg.sendTo('u2', { type: 'mention' }) // 用户维度点对点
|
|
|
117
118
|
```ts
|
|
118
119
|
import { queue } from 'weifuwu'
|
|
119
120
|
|
|
120
|
-
const
|
|
121
|
+
const r = redis(); app.use(r) // Redis 中间件(注入 ctx.redis)
|
|
122
|
+
const q = queue({ redis: r.redis }) // queue 必传 Redis(池命令走轮询连接)
|
|
121
123
|
app.use(q) // 注入 ctx.queue
|
|
122
124
|
|
|
123
125
|
app.post('/api/generate', async (req, ctx) => {
|
|
@@ -184,10 +186,10 @@ app.post('/api/approve', async (req, ctx) => {
|
|
|
184
186
|
})
|
|
185
187
|
```
|
|
186
188
|
|
|
187
|
-
前端解码(`weifuwu/
|
|
189
|
+
前端解码(`weifuwu/ui-dom`):
|
|
188
190
|
|
|
189
191
|
```ts
|
|
190
|
-
import { aiStream } from 'weifuwu/
|
|
192
|
+
import { aiStream } from 'weifuwu/ui-dom'
|
|
191
193
|
|
|
192
194
|
const handle = aiStream('/api/chat', { messages }, {
|
|
193
195
|
onToken: (text) => { /* 增量 append 到消息 */ },
|
package/package.json
CHANGED
package/dist/ui/ssr-page.d.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* uiSsr — 路由级 SSR 中间件:前端路由表驱动的自动服务端渲染
|
|
3
|
-
*
|
|
4
|
-
* 开发者只声明路由(path + component),GET 匹配即自动 SSR:
|
|
5
|
-
* 匹配 → 注入 params → ctx.ui.ssr 渲染组件(async 工厂 await)→ 页面模板 + __DATA__
|
|
6
|
-
* 未匹配 → next()(交给后续 API/静态/404)
|
|
7
|
-
*
|
|
8
|
-
* 与客户端 router() 共用同一份路由定义(route-match.ts),SPA/SSR 一个声明。
|
|
9
|
-
*/
|
|
10
|
-
import type { Middleware } from '../types.ts';
|
|
11
|
-
import type { RouteDef } from '../ui-dom/types.ts';
|
|
12
|
-
export interface UiSsrOptions {
|
|
13
|
-
/** 前端路由定义(与 router() 共享同一份) */
|
|
14
|
-
routes: RouteDef[];
|
|
15
|
-
/** 客户端 bundle 路径(自动注入 <script src>,如 '/static/app.js') */
|
|
16
|
-
bundle?: string;
|
|
17
|
-
/** CSS 路径数组(自动注入 <link rel="stylesheet">,如 ['/static/style.css']) */
|
|
18
|
-
styles?: string[];
|
|
19
|
-
/** 自定义 title(默认取路由定义 title 或 'weifuwu') */
|
|
20
|
-
title?: (ctx: {
|
|
21
|
-
params: Record<string, string>;
|
|
22
|
-
def: RouteDef;
|
|
23
|
-
}) => string;
|
|
24
|
-
/** 自定义页面模板:返回完整 HTML(默认内置,含 head/root/__DATA__/styles/bundle) */
|
|
25
|
-
template?: (parts: {
|
|
26
|
-
html: string;
|
|
27
|
-
dataScript: string;
|
|
28
|
-
title: string;
|
|
29
|
-
bundle?: string;
|
|
30
|
-
styles?: string[];
|
|
31
|
-
}) => string;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* 路由级 SSR 中间件:
|
|
35
|
-
*
|
|
36
|
-
* ```ts
|
|
37
|
-
* const app = new Router()
|
|
38
|
-
* app.use(uiSsr({ routes, bundle: '/static/app.js' }))
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
export declare function uiSsr(opts: UiSsrOptions): Middleware;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 路由匹配纯函数 — 客户端 router 与服务端 uiSsr 共用(无 DOM 依赖)
|
|
3
|
-
*
|
|
4
|
-
* 支持路径参数 :id、通配符 *、嵌套路由(children → chain)。
|
|
5
|
-
* 与后端 Router(Trie)独立——本模块服务于"前端路由表驱动的页面"。
|
|
6
|
-
*/
|
|
7
|
-
import type { RouteDef } from './types.ts';
|
|
8
|
-
export interface FlattenedRoute {
|
|
9
|
-
re: RegExp;
|
|
10
|
-
keys: string[];
|
|
11
|
-
def: RouteDef;
|
|
12
|
-
chain: RouteDef[];
|
|
13
|
-
}
|
|
14
|
-
export declare function joinPaths(a: string, b: string): string;
|
|
15
|
-
export declare function compilePath(path: string): {
|
|
16
|
-
re: RegExp;
|
|
17
|
-
keys: string[];
|
|
18
|
-
};
|
|
19
|
-
export declare function flattenRoutes(routes: RouteDef[], basePath?: string, chain?: RouteDef[]): FlattenedRoute[];
|
|
20
|
-
export declare function matchRoute(path: string, routes: FlattenedRoute[]): FlattenedRoute | null;
|
|
21
|
-
/** 从匹配结果提取路径参数 */
|
|
22
|
-
export declare function extractParams(path: string, match: FlattenedRoute): Record<string, string>;
|