@finesoft/front 0.1.34 → 0.1.36
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 +215 -137
- package/dist/browser.cjs +2 -2
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1 -1
- package/dist/{chunk-OE5BU5MR.js → chunk-J43ASC3C.js} +3 -3
- package/dist/chunk-J43ASC3C.js.map +1 -0
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-OE5BU5MR.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
`@finesoft/front` 是一个面向 SSR Web 应用的聚合包,统一导出了以下四层能力:
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
5
|
+
- `@finesoft/core`:路由、Intent、Controller、依赖注入、Framework
|
|
6
|
+
- `@finesoft/browser`:浏览器启动、导航、hydrate、prefetched data
|
|
7
|
+
- `@finesoft/ssr`:SSR 渲染与服务端数据注入
|
|
8
|
+
- `@finesoft/server`:Hono 服务端集成、Vite 插件、声明式代理、渲染模式覆盖、部署适配器
|
|
9
9
|
|
|
10
10
|
它适合这样一类应用:
|
|
11
11
|
|
|
@@ -21,16 +21,16 @@ $$
|
|
|
21
21
|
|
|
22
22
|
`@finesoft/front` 更适合以下类型的项目:
|
|
23
23
|
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
24
|
+
- 需要 SSR 的内容型站点
|
|
25
|
+
- 有明确 URL 语义的多页面 Web 应用
|
|
26
|
+
- 希望将页面获取逻辑集中在 Controller 中的项目
|
|
27
|
+
- 需要同一套页面模型同时服务 SSR 和客户端导航的项目
|
|
28
28
|
|
|
29
29
|
例如:
|
|
30
30
|
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
31
|
+
- 内容聚合站点
|
|
32
|
+
- 应用商店、媒体展示、排行榜、搜索、详情页
|
|
33
|
+
- 需要 SEO 的展示型前端
|
|
34
34
|
|
|
35
35
|
如果你的项目非常轻量、完全不需要 SSR,也可以只使用其中的 Browser/Core 能力。
|
|
36
36
|
|
|
@@ -40,52 +40,54 @@ $$
|
|
|
40
40
|
|
|
41
41
|
### Core
|
|
42
42
|
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
43
|
+
- `Framework`
|
|
44
|
+
- `Router`
|
|
45
|
+
- `Container`
|
|
46
|
+
- `BaseController`
|
|
47
|
+
- `defineRoutes`
|
|
48
|
+
- `ActionDispatcher`
|
|
49
|
+
- `IntentDispatcher`
|
|
50
|
+
- `HttpClient`
|
|
51
|
+
- `LruMap`
|
|
52
|
+
- `buildUrl`
|
|
53
53
|
|
|
54
54
|
### Browser
|
|
55
55
|
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
56
|
+
- `startBrowserApp`
|
|
57
|
+
- `History`
|
|
58
|
+
- `registerActionHandlers`
|
|
59
|
+
- `registerFlowActionHandler`
|
|
60
|
+
- `registerExternalUrlHandler`
|
|
61
|
+
- `deserializeServerData`
|
|
62
|
+
- `createPrefetchedIntentsFromDom`
|
|
63
|
+
- `tryScroll`
|
|
64
64
|
|
|
65
65
|
### SSR
|
|
66
66
|
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
67
|
+
- `createSSRRender`
|
|
68
|
+
- `ssrRender`
|
|
69
|
+
- `injectSSRContent`
|
|
70
|
+
- `serializeServerData`
|
|
71
|
+
- `SSR_PLACEHOLDERS`
|
|
72
72
|
|
|
73
73
|
### Server / Deployment
|
|
74
74
|
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
75
|
+
- `createServer`
|
|
76
|
+
- `createSSRApp`
|
|
77
|
+
- `startServer`
|
|
78
|
+
- `parseAcceptLanguage`
|
|
79
|
+
- `detectRuntime`
|
|
80
|
+
- `resolveRoot`
|
|
81
|
+
- `finesoftFrontViteConfig`
|
|
82
|
+
- `registerProxyRoutes`
|
|
83
|
+
- `ProxyRouteConfig`
|
|
84
|
+
- `nodeAdapter`
|
|
85
|
+
- `vercelAdapter`
|
|
86
|
+
- `cloudflareAdapter`
|
|
87
|
+
- `netlifyAdapter`
|
|
88
|
+
- `staticAdapter`
|
|
89
|
+
- `autoAdapter`
|
|
90
|
+
- `resolveAdapter`
|
|
89
91
|
|
|
90
92
|
---
|
|
91
93
|
|
|
@@ -97,25 +99,28 @@ $$
|
|
|
97
99
|
|
|
98
100
|
优点:
|
|
99
101
|
|
|
100
|
-
-
|
|
101
|
-
-
|
|
102
|
-
-
|
|
103
|
-
-
|
|
102
|
+
- `vite` 可直接用于开发
|
|
103
|
+
- `vite build` 会同时完成客户端与 SSR 构建
|
|
104
|
+
- 可以直接接入平台适配器输出部署产物
|
|
105
|
+
- `vite preview` 可用于本地预览 SSR 构建结果
|
|
106
|
+
- 可通过 `renderModes` 统一覆盖路由的 `ssr` / `csr` / `prerender` 策略
|
|
107
|
+
- 可通过 `proxies` 声明式配置代理路由,避免在 `setup` 中手写转发逻辑
|
|
108
|
+
- 开发模式会尽量把入口依赖的全局 CSS 提前注入到 SSR HTML,减少首屏布局抖动
|
|
104
109
|
|
|
105
110
|
### 方式 B:手动使用 `createServer()`
|
|
106
111
|
|
|
107
112
|
适合以下情况:
|
|
108
113
|
|
|
109
|
-
-
|
|
110
|
-
-
|
|
111
|
-
-
|
|
114
|
+
- 你需要完全控制服务启动流程
|
|
115
|
+
- 你已经有自定义的 Hono / Node 集成方式
|
|
116
|
+
- 你不希望依赖 Vite 插件生命周期
|
|
112
117
|
|
|
113
118
|
### 方式 C:仅使用 Browser/Core
|
|
114
119
|
|
|
115
120
|
适合以下情况:
|
|
116
121
|
|
|
117
|
-
-
|
|
118
|
-
-
|
|
122
|
+
- 你不需要 SSR
|
|
123
|
+
- 你只希望复用 Router / Intent / Controller / Framework 模型
|
|
119
124
|
|
|
120
125
|
---
|
|
121
126
|
|
|
@@ -141,9 +146,9 @@ pnpm add dotenv
|
|
|
141
146
|
|
|
142
147
|
### 当前 peer dependencies
|
|
143
148
|
|
|
144
|
-
-
|
|
145
|
-
-
|
|
146
|
-
-
|
|
149
|
+
- `hono`
|
|
150
|
+
- `@hono/node-server`
|
|
151
|
+
- `vite`
|
|
147
152
|
|
|
148
153
|
如果你只使用浏览器侧能力,不一定需要全部安装;如果你使用 SSR、Server 或 Vite 插件,则建议全部安装。
|
|
149
154
|
|
|
@@ -155,8 +160,8 @@ pnpm add dotenv
|
|
|
155
160
|
|
|
156
161
|
这意味着:
|
|
157
162
|
|
|
158
|
-
-
|
|
159
|
-
-
|
|
163
|
+
- 浏览器构建时,会优先解析 browser-only entry,避免引入服务端实现
|
|
164
|
+
- Node / SSR 环境下,会解析完整入口,包含 Browser、SSR、Server 全部导出
|
|
160
165
|
|
|
161
166
|
因此大多数情况下你可以直接这样写:
|
|
162
167
|
|
|
@@ -190,12 +195,14 @@ index.html
|
|
|
190
195
|
vite.config.ts
|
|
191
196
|
```
|
|
192
197
|
|
|
193
|
-
|
|
198
|
+
如果你需要注册额外 Hono 路由,可以增加:
|
|
194
199
|
|
|
195
200
|
```text
|
|
196
201
|
src/setup.ts
|
|
197
202
|
```
|
|
198
203
|
|
|
204
|
+
如果只是配置 API 代理,优先使用 `vite.config.ts` / `createServer()` 中的 `proxies`,通常不再需要单独写 `src/setup.ts`。
|
|
205
|
+
|
|
199
206
|
---
|
|
200
207
|
|
|
201
208
|
### 2. 编写 `index.html`
|
|
@@ -203,7 +210,7 @@ src/setup.ts
|
|
|
203
210
|
你的 HTML 模板必须包含以下 SSR 占位符:
|
|
204
211
|
|
|
205
212
|
```html
|
|
206
|
-
<!
|
|
213
|
+
<!DOCTYPE html>
|
|
207
214
|
<html lang="<!--ssr-lang-->">
|
|
208
215
|
<head>
|
|
209
216
|
<meta charset="utf-8" />
|
|
@@ -218,12 +225,12 @@ src/setup.ts
|
|
|
218
225
|
</html>
|
|
219
226
|
```
|
|
220
227
|
|
|
221
|
-
| 占位符
|
|
222
|
-
|
|
|
223
|
-
| `<!--ssr-lang-->` | 当前语言
|
|
228
|
+
| 占位符 | 用途 |
|
|
229
|
+
| ----------------- | ----------------------- |
|
|
230
|
+
| `<!--ssr-lang-->` | 当前语言 |
|
|
224
231
|
| `<!--ssr-head-->` | SSR `<head>` 内容与样式 |
|
|
225
|
-
| `<!--ssr-body-->` | 服务端渲染后的 HTML
|
|
226
|
-
| `<!--ssr-data-->` | 序列化后的服务端数据
|
|
232
|
+
| `<!--ssr-body-->` | 服务端渲染后的 HTML |
|
|
233
|
+
| `<!--ssr-data-->` | 序列化后的服务端数据 |
|
|
227
234
|
|
|
228
235
|
---
|
|
229
236
|
|
|
@@ -343,7 +350,7 @@ export { serializeServerData };
|
|
|
343
350
|
|
|
344
351
|
---
|
|
345
352
|
|
|
346
|
-
### 6. 如果你有自定义 Hono
|
|
353
|
+
### 6. 如果你有自定义 Hono 路由(非代理类),编写 `src/setup.ts`
|
|
347
354
|
|
|
348
355
|
```ts
|
|
349
356
|
import type { Hono } from "hono";
|
|
@@ -355,10 +362,14 @@ export default function setup(app: Hono) {
|
|
|
355
362
|
|
|
356
363
|
建议优先导出 `default` 函数。
|
|
357
364
|
|
|
365
|
+
`setup` 更适合放健康检查、业务回调、实验性接口等自定义路由。
|
|
366
|
+
|
|
367
|
+
如果你的需求是“把 `/api/foo/*` 转发到某个 HTTPS 上游”,请优先使用 `proxies` 选项,让框架统一处理路径校验、认证头、缓存头和错误响应。
|
|
368
|
+
|
|
358
369
|
`setup` 在插件中有两种用法:
|
|
359
370
|
|
|
360
|
-
-
|
|
361
|
-
-
|
|
371
|
+
- 传入函数:适用于 `dev` / `preview`
|
|
372
|
+
- 传入文件路径字符串:适用于 `dev` / `build` / `preview` / adapter
|
|
362
373
|
|
|
363
374
|
如果你需要让构建产物也包含这些路由,建议传入文件路径字符串。
|
|
364
375
|
|
|
@@ -376,6 +387,18 @@ export default defineConfig({
|
|
|
376
387
|
locales: ["zh", "en"],
|
|
377
388
|
defaultLocale: "en",
|
|
378
389
|
ssr: { entry: "src/ssr.ts" },
|
|
390
|
+
renderModes: {
|
|
391
|
+
"/search": "csr",
|
|
392
|
+
"/stories/*": "prerender",
|
|
393
|
+
},
|
|
394
|
+
proxies: [
|
|
395
|
+
{
|
|
396
|
+
prefix: "/api/itunes",
|
|
397
|
+
target: "https://itunes.apple.com",
|
|
398
|
+
cache: "public, max-age=300, s-maxage=600",
|
|
399
|
+
followRedirects: true,
|
|
400
|
+
},
|
|
401
|
+
],
|
|
379
402
|
setup: "src/setup.ts",
|
|
380
403
|
adapter: "node",
|
|
381
404
|
}),
|
|
@@ -385,13 +408,52 @@ export default defineConfig({
|
|
|
385
408
|
|
|
386
409
|
当前支持的 adapter:
|
|
387
410
|
|
|
388
|
-
-
|
|
389
|
-
-
|
|
390
|
-
-
|
|
391
|
-
-
|
|
392
|
-
-
|
|
393
|
-
-
|
|
394
|
-
-
|
|
411
|
+
- `"node"`
|
|
412
|
+
- `"vercel"`
|
|
413
|
+
- `"cloudflare"`
|
|
414
|
+
- `"netlify"`
|
|
415
|
+
- `"static"`
|
|
416
|
+
- `"auto"`
|
|
417
|
+
- 或自定义 `Adapter` 对象
|
|
418
|
+
|
|
419
|
+
### `renderModes` 与 `proxies` 的推荐用法
|
|
420
|
+
|
|
421
|
+
#### `renderModes`
|
|
422
|
+
|
|
423
|
+
`renderModes` 用于在 Vite 插件层按 URL 覆盖渲染模式,优先级高于路由定义里的 `renderMode`。
|
|
424
|
+
|
|
425
|
+
支持两种 key:
|
|
426
|
+
|
|
427
|
+
- 精确路径:如 `"/search"`
|
|
428
|
+
- glob 模式:如 `"/stories/*"`
|
|
429
|
+
|
|
430
|
+
可用值:
|
|
431
|
+
|
|
432
|
+
- `"ssr"`
|
|
433
|
+
- `"csr"`
|
|
434
|
+
- `"prerender"`
|
|
435
|
+
|
|
436
|
+
当你需要“页面逻辑不变,但按部署 / 产品策略切换渲染方式”时,优先改这里,而不是到各个路由定义里逐个改。
|
|
437
|
+
|
|
438
|
+
#### `proxies`
|
|
439
|
+
|
|
440
|
+
`proxies` 用于声明式注册代理路由,适合 Apple API、RSS、上游聚合接口这类“路径前缀固定、目标域名固定”的转发场景。
|
|
441
|
+
|
|
442
|
+
每一项支持这些关键字段:
|
|
443
|
+
|
|
444
|
+
- `prefix`:本地路由前缀,如 `"/api/itunes"`
|
|
445
|
+
- `target`:上游 HTTPS 地址
|
|
446
|
+
- `methods`:允许的方法列表,默认 `["all"]`
|
|
447
|
+
- `headers`:附加请求头
|
|
448
|
+
- `auth`:从环境变量读取 Bearer / Basic 认证
|
|
449
|
+
- `cache`:统一设置 `Cache-Control`
|
|
450
|
+
- `followRedirects`:是否跟随 3xx
|
|
451
|
+
|
|
452
|
+
相比在 `setup` 中手写 Hono 代理,`proxies` 的好处是:
|
|
453
|
+
|
|
454
|
+
- 框架统一做 SSRF 基础防护
|
|
455
|
+
- dev / preview / adapter 构建产物行为一致
|
|
456
|
+
- 代理逻辑更容易复用和审查
|
|
395
457
|
|
|
396
458
|
---
|
|
397
459
|
|
|
@@ -669,7 +731,7 @@ export const render = createSSRRender({
|
|
|
669
731
|
|
|
670
732
|
输出:
|
|
671
733
|
|
|
672
|
-
-
|
|
734
|
+
- `dist/server/index.mjs`
|
|
673
735
|
|
|
674
736
|
运行方式:
|
|
675
737
|
|
|
@@ -679,10 +741,10 @@ node dist/server/index.mjs
|
|
|
679
741
|
|
|
680
742
|
适合:
|
|
681
743
|
|
|
682
|
-
-
|
|
683
|
-
-
|
|
684
|
-
-
|
|
685
|
-
-
|
|
744
|
+
- Node 服务器
|
|
745
|
+
- Docker
|
|
746
|
+
- VPS
|
|
747
|
+
- PM2
|
|
686
748
|
|
|
687
749
|
---
|
|
688
750
|
|
|
@@ -690,14 +752,14 @@ node dist/server/index.mjs
|
|
|
690
752
|
|
|
691
753
|
输出:
|
|
692
754
|
|
|
693
|
-
-
|
|
694
|
-
-
|
|
695
|
-
-
|
|
755
|
+
- `.vercel/output/config.json`
|
|
756
|
+
- `.vercel/output/static/`
|
|
757
|
+
- `.vercel/output/functions/ssr.func/`
|
|
696
758
|
|
|
697
759
|
说明:
|
|
698
760
|
|
|
699
|
-
-
|
|
700
|
-
-
|
|
761
|
+
- 它不在 `dist/` 中,这是平台约定
|
|
762
|
+
- 对应 Vercel Build Output API v3
|
|
701
763
|
|
|
702
764
|
建议将 `.vercel/` 加入 `.gitignore`。
|
|
703
765
|
|
|
@@ -707,13 +769,13 @@ node dist/server/index.mjs
|
|
|
707
769
|
|
|
708
770
|
输出:
|
|
709
771
|
|
|
710
|
-
-
|
|
711
|
-
-
|
|
772
|
+
- `.netlify/functions-internal/ssr/index.mjs`
|
|
773
|
+
- `dist/client/_redirects`
|
|
712
774
|
|
|
713
775
|
说明:
|
|
714
776
|
|
|
715
|
-
-
|
|
716
|
-
-
|
|
777
|
+
- `.netlify/` 在 `dist/` 外同样属于平台约定
|
|
778
|
+
- 常见发布目录是 `dist/client/`
|
|
717
779
|
|
|
718
780
|
建议将 `.netlify/` 加入 `.gitignore`。
|
|
719
781
|
|
|
@@ -723,13 +785,13 @@ node dist/server/index.mjs
|
|
|
723
785
|
|
|
724
786
|
输出:
|
|
725
787
|
|
|
726
|
-
-
|
|
727
|
-
-
|
|
788
|
+
- `dist/cloudflare/_worker.js`
|
|
789
|
+
- `dist/cloudflare/assets/`
|
|
728
790
|
|
|
729
791
|
说明:
|
|
730
792
|
|
|
731
|
-
-
|
|
732
|
-
-
|
|
793
|
+
- Cloudflare Workers 不是完整 Node.js 环境
|
|
794
|
+
- 如果运行时代码依赖 Node API,可能需要额外兼容配置
|
|
733
795
|
|
|
734
796
|
---
|
|
735
797
|
|
|
@@ -737,13 +799,13 @@ node dist/server/index.mjs
|
|
|
737
799
|
|
|
738
800
|
输出:
|
|
739
801
|
|
|
740
|
-
-
|
|
802
|
+
- `dist/static/`
|
|
741
803
|
|
|
742
804
|
适合:
|
|
743
805
|
|
|
744
|
-
-
|
|
745
|
-
-
|
|
746
|
-
-
|
|
806
|
+
- 纯静态托管
|
|
807
|
+
- CDN / 对象存储 / Pages 类平台
|
|
808
|
+
- 不依赖运行时服务端逻辑的页面
|
|
747
809
|
|
|
748
810
|
它会执行:
|
|
749
811
|
|
|
@@ -758,14 +820,14 @@ node dist/server/index.mjs
|
|
|
758
820
|
|
|
759
821
|
例如这些通常会自动生成:
|
|
760
822
|
|
|
761
|
-
-
|
|
762
|
-
-
|
|
763
|
-
-
|
|
823
|
+
- `/`
|
|
824
|
+
- `/search`
|
|
825
|
+
- `/about`
|
|
764
826
|
|
|
765
827
|
这些通常不会自动生成:
|
|
766
828
|
|
|
767
|
-
-
|
|
768
|
-
-
|
|
829
|
+
- `/product/:id`
|
|
830
|
+
- `/list/:category`
|
|
769
831
|
|
|
770
832
|
如果你要预渲染动态地址,请补充具体 URL:
|
|
771
833
|
|
|
@@ -787,8 +849,8 @@ finesoftFrontViteConfig({
|
|
|
787
849
|
|
|
788
850
|
常见解决方式:
|
|
789
851
|
|
|
790
|
-
-
|
|
791
|
-
-
|
|
852
|
+
- 构建期确保 API 可访问
|
|
853
|
+
- 为 Controller 提供 fallback / mock 数据
|
|
792
854
|
|
|
793
855
|
##### 3)验证静态产物时,应直接查看 `dist/static/`
|
|
794
856
|
|
|
@@ -805,10 +867,10 @@ python3 -m http.server 3000
|
|
|
805
867
|
|
|
806
868
|
自动识别顺序:
|
|
807
869
|
|
|
808
|
-
-
|
|
809
|
-
-
|
|
810
|
-
-
|
|
811
|
-
-
|
|
870
|
+
- `VERCEL` → `vercel`
|
|
871
|
+
- `CF_PAGES` → `cloudflare`
|
|
872
|
+
- `NETLIFY` → `netlify`
|
|
873
|
+
- 默认 → `node`
|
|
812
874
|
|
|
813
875
|
适合 CI 或平台自动识别场景。
|
|
814
876
|
|
|
@@ -852,6 +914,13 @@ const { app, vite, runtime } = await createServer({
|
|
|
852
914
|
locales: ["zh", "en"],
|
|
853
915
|
defaultLocale: "en",
|
|
854
916
|
port: 3000,
|
|
917
|
+
proxies: [
|
|
918
|
+
{
|
|
919
|
+
prefix: "/api/itunes",
|
|
920
|
+
target: "https://itunes.apple.com",
|
|
921
|
+
followRedirects: true,
|
|
922
|
+
},
|
|
923
|
+
],
|
|
855
924
|
setup(app) {
|
|
856
925
|
app.get("/api/health", (c) => c.json({ ok: true }));
|
|
857
926
|
},
|
|
@@ -872,15 +941,15 @@ void runtime;
|
|
|
872
941
|
3. 检测当前运行时
|
|
873
942
|
4. 在开发模式下创建 Vite middleware server
|
|
874
943
|
5. 创建 Hono app
|
|
875
|
-
6.
|
|
944
|
+
6. 先注册声明式代理路由,再注册自定义业务路由
|
|
876
945
|
7. 再挂载 SSR catch-all
|
|
877
946
|
8. 启动服务
|
|
878
947
|
|
|
879
948
|
默认行为:
|
|
880
949
|
|
|
881
|
-
-
|
|
882
|
-
-
|
|
883
|
-
-
|
|
950
|
+
- `root` 默认值:`process.cwd()`
|
|
951
|
+
- `port` 默认值:`process.env.PORT ?? 3000`
|
|
952
|
+
- 根目录存在 `.env` 且安装了 `dotenv` 时,会尝试自动加载
|
|
884
953
|
|
|
885
954
|
---
|
|
886
955
|
|
|
@@ -920,16 +989,16 @@ startBrowserApp({
|
|
|
920
989
|
|
|
921
990
|
现象:
|
|
922
991
|
|
|
923
|
-
-
|
|
924
|
-
-
|
|
925
|
-
-
|
|
992
|
+
- 页面未正常 hydrate
|
|
993
|
+
- SSR 内容缺失
|
|
994
|
+
- 服务端数据未注入
|
|
926
995
|
|
|
927
996
|
请检查以下四个占位符是否全部存在:
|
|
928
997
|
|
|
929
|
-
-
|
|
930
|
-
-
|
|
931
|
-
-
|
|
932
|
-
-
|
|
998
|
+
- `<!--ssr-lang-->`
|
|
999
|
+
- `<!--ssr-head-->`
|
|
1000
|
+
- `<!--ssr-body-->`
|
|
1001
|
+
- `<!--ssr-data-->`
|
|
933
1002
|
|
|
934
1003
|
---
|
|
935
1004
|
|
|
@@ -937,47 +1006,56 @@ startBrowserApp({
|
|
|
937
1006
|
|
|
938
1007
|
原因:
|
|
939
1008
|
|
|
940
|
-
-
|
|
941
|
-
-
|
|
1009
|
+
- 直接传函数主要适用于 `dev` / `preview`
|
|
1010
|
+
- 构建期更适合通过文件路径构建 `setup` 模块
|
|
942
1011
|
|
|
943
1012
|
建议:
|
|
944
1013
|
|
|
945
|
-
-
|
|
1014
|
+
- 使用文件路径字符串,例如 `setup: "src/setup.ts"`
|
|
1015
|
+
|
|
1016
|
+
### 3. 什么时候该用 `proxies`,什么时候该用 `setup`
|
|
1017
|
+
|
|
1018
|
+
一般可以这样判断:
|
|
1019
|
+
|
|
1020
|
+
- **固定前缀 → 固定 HTTPS 上游**:用 `proxies`
|
|
1021
|
+
- **自定义 JSON 接口 / webhook / health check / 特殊中间件**:用 `setup`
|
|
1022
|
+
|
|
1023
|
+
如果你在 `setup` 里主要写的是“拼接目标 URL 再 `fetch` 转发”,那大概率更适合迁移到 `proxies`。
|
|
946
1024
|
|
|
947
1025
|
---
|
|
948
1026
|
|
|
949
|
-
###
|
|
1027
|
+
### 4. `static` 模式下动态路由没有页面
|
|
950
1028
|
|
|
951
1029
|
原因:
|
|
952
1030
|
|
|
953
|
-
-
|
|
1031
|
+
- `staticAdapter()` 只会自动预渲染无参数路由
|
|
954
1032
|
|
|
955
1033
|
解决方式:
|
|
956
1034
|
|
|
957
|
-
-
|
|
1035
|
+
- 使用 `dynamicRoutes` 提供具体 URL
|
|
958
1036
|
|
|
959
1037
|
---
|
|
960
1038
|
|
|
961
|
-
###
|
|
1039
|
+
### 5. `static` 模式构建出来的是错误页
|
|
962
1040
|
|
|
963
1041
|
原因通常是:
|
|
964
1042
|
|
|
965
|
-
-
|
|
966
|
-
-
|
|
1043
|
+
- 构建期 Controller 访问外部 API 失败
|
|
1044
|
+
- 但 fallback 没有提供可用的本地数据
|
|
967
1045
|
|
|
968
1046
|
解决方式:
|
|
969
1047
|
|
|
970
|
-
-
|
|
971
|
-
-
|
|
1048
|
+
- 保证构建期 API 可访问,或
|
|
1049
|
+
- 给 Controller 提供 fallback / mock 数据
|
|
972
1050
|
|
|
973
1051
|
---
|
|
974
1052
|
|
|
975
|
-
###
|
|
1053
|
+
### 6. `.vercel/` 和 `.netlify/` 为什么不在 `dist/`
|
|
976
1054
|
|
|
977
1055
|
这是平台约定,不是框架异常:
|
|
978
1056
|
|
|
979
|
-
-
|
|
980
|
-
-
|
|
1057
|
+
- Vercel 使用 `.vercel/output/`
|
|
1058
|
+
- Netlify 使用 `.netlify/functions-internal/`
|
|
981
1059
|
|
|
982
1060
|
建议将这些目录加入 `.gitignore`。
|
|
983
1061
|
|
package/dist/browser.cjs
CHANGED
|
@@ -948,7 +948,7 @@ var History = class {
|
|
|
948
948
|
this.log.info("saving scroll position", scrollTop);
|
|
949
949
|
}
|
|
950
950
|
onPopState(listener) {
|
|
951
|
-
window.addEventListener("popstate", (event) => {
|
|
951
|
+
window.addEventListener("popstate", async (event) => {
|
|
952
952
|
this.currentStateId = event.state?.id;
|
|
953
953
|
if (!this.currentStateId) {
|
|
954
954
|
this.log.warn(
|
|
@@ -958,7 +958,7 @@ var History = class {
|
|
|
958
958
|
}
|
|
959
959
|
this.log.info("popstate", this.entries, this.currentStateId);
|
|
960
960
|
const entry = this.currentStateId ? this.entries.get(this.currentStateId) : void 0;
|
|
961
|
-
listener(window.location.href, entry?.state);
|
|
961
|
+
await listener(window.location.href, entry?.state);
|
|
962
962
|
if (!entry) {
|
|
963
963
|
return;
|
|
964
964
|
}
|