@ubean/vue 0.4.5 → 0.4.6
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 +40 -3
- package/README.zh-CN.md +41 -3
- package/dist/generator.d.ts +7 -8
- package/dist/index.d.ts +109 -59
- package/dist/index.js +58 -2
- package/dist/vite.d.ts +19 -19
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ import { routes } from 'virtual:ubean-vue-routes'; // generated by @ubean/vue/vi
|
|
|
15
15
|
const router = createRouter({ history: createWebHistory(), routes });
|
|
16
16
|
const app = createApp(App);
|
|
17
17
|
app.use(router);
|
|
18
|
-
app.use(ubeanVue, { routes }); // registers Link/PageView/SlotView + seeds page cache
|
|
18
|
+
app.use(ubeanVue, { routes }); // registers Link/PageView/SlotView/ClientOnly + seeds page cache
|
|
19
19
|
app.mount('#app');
|
|
20
20
|
```
|
|
21
21
|
|
|
@@ -61,7 +61,7 @@ app.mount('#app');
|
|
|
61
61
|
**Included:**
|
|
62
62
|
|
|
63
63
|
- `ubeanVue` Vue plugin (the only wiring entry — plugin-first, no factory)
|
|
64
|
-
- `<PageView>` / `<Link>` / `<SlotView>` / `<LayoutChainRenderer>` / `<ErrorBoundary>`
|
|
64
|
+
- `<PageView>` / `<Link>` / `<SlotView>` / `<LayoutChainRenderer>` / `<ErrorBoundary>` / `<ClientOnly>`
|
|
65
65
|
- Page cache (keep-alive) store with imperative + declarative APIs
|
|
66
66
|
- Page transitions & reload signal (per-page remount with fresh cache)
|
|
67
67
|
- View Transitions helpers (feature detection + wrapper)
|
|
@@ -345,7 +345,7 @@ The `RouteFileGenerator` class is also exported for incremental use (`new RouteF
|
|
|
345
345
|
app.use(ubeanVue, { routes });
|
|
346
346
|
```
|
|
347
347
|
|
|
348
|
-
Registers `Link` / `PageView` / `SlotView` as global components and seeds the keep-alive include list from `meta.cache: true` declarations (`initCachedViewsFromRoutes`). This is the **only** wiring entry of the package (main subpath — distinct from the same-named Vite plugin in the `/vite` subpath).
|
|
348
|
+
Registers `Link` / `PageView` / `SlotView` / `ClientOnly` as global components and seeds the keep-alive include list from `meta.cache: true` declarations (`initCachedViewsFromRoutes`). This is the **only** wiring entry of the package (main subpath — distinct from the same-named Vite plugin in the `/vite` subpath).
|
|
349
349
|
|
|
350
350
|
## Components
|
|
351
351
|
|
|
@@ -379,6 +379,43 @@ Recursively renders a chain of nested layout components (framework factories bui
|
|
|
379
379
|
|
|
380
380
|
Catches render/async/setup errors from descendants and renders the configured error component; resets on route change.
|
|
381
381
|
|
|
382
|
+
### `<ClientOnly>`
|
|
383
|
+
|
|
384
|
+
Renders its content ONLY on the client, after hydration. Useful for browser-only markup or template fragments (the islands `v-client.*` transform only applies to capitalized component tags).
|
|
385
|
+
|
|
386
|
+
```vue
|
|
387
|
+
<template>
|
|
388
|
+
<!-- string fallback prop -->
|
|
389
|
+
<ClientOnly fallback="loading…">
|
|
390
|
+
<BrowserChart />
|
|
391
|
+
</ClientOnly>
|
|
392
|
+
|
|
393
|
+
<!-- fallback slot (reserve space to avoid CLS) -->
|
|
394
|
+
<ClientOnly>
|
|
395
|
+
<template #fallback>
|
|
396
|
+
<div class="skeleton" />
|
|
397
|
+
</template>
|
|
398
|
+
<BrowserMap />
|
|
399
|
+
</ClientOnly>
|
|
400
|
+
|
|
401
|
+
<!-- plain fragments work too -->
|
|
402
|
+
<ClientOnly>Window width: {{ width }}px</ClientOnly>
|
|
403
|
+
</template>
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
**Hydration safety:** the placeholder (fallback / nothing) is rendered during SSR AND during the client's hydration render; the real content is patched in right after mount. There is no `typeof window` branching, so no hydration mismatch warnings.
|
|
407
|
+
|
|
408
|
+
**When to use what:**
|
|
409
|
+
|
|
410
|
+
| Scenario | Use |
|
|
411
|
+
| -------------------------------------------------------------------- | ----------------------------------------------------------- |
|
|
412
|
+
| Template fragments / plain HTML, same-tree rendering, fallback slots | `<ClientOnly>` |
|
|
413
|
+
| Component-level deferred hydration strategies (idle/visible/media) | islands `v-client.*` |
|
|
414
|
+
| Programmatic islands / client-only wrappers | `defineIsland` / `defineClientComponent` (`@ubean/islands`) |
|
|
415
|
+
| File-level server/client split | `.server.vue` / `.client.vue` |
|
|
416
|
+
|
|
417
|
+
> Notes: default-slot content is absent from the SSR HTML — don't use `<ClientOnly>` for SEO-critical content. `<ClientOnly>` inside an island placeholder's children is wiped when the island hydrates (islands hydrate via a separate app).
|
|
418
|
+
|
|
382
419
|
## Composables
|
|
383
420
|
|
|
384
421
|
- **`usePage<T>()`** — lean page-data access: returns the `PAGE_KEY` injected data (`props` / `component` / `errors`) as-is, or a shared empty object when nothing was provided. Route state (`url` / `params` / `query` / `meta`) is NOT included — use vue-router's `useRoute()` directly. The framework runtime (`@ubean/client`) layers its own route-aware `usePage` on top for the full PageObject protocol.
|
package/README.zh-CN.md
CHANGED
|
@@ -15,7 +15,7 @@ import { routes } from 'virtual:ubean-vue-routes'; // 由 @ubean/vue/vite 生成
|
|
|
15
15
|
const router = createRouter({ history: createWebHistory(), routes });
|
|
16
16
|
const app = createApp(App);
|
|
17
17
|
app.use(router);
|
|
18
|
-
app.use(ubeanVue, { routes }); // 注册 Link/PageView/SlotView + 播种页面缓存
|
|
18
|
+
app.use(ubeanVue, { routes }); // 注册 Link/PageView/SlotView/ClientOnly + 播种页面缓存
|
|
19
19
|
app.mount('#app');
|
|
20
20
|
```
|
|
21
21
|
|
|
@@ -45,6 +45,7 @@ app.mount('#app');
|
|
|
45
45
|
- [`<SlotView>`](#slotview)
|
|
46
46
|
- [`<LayoutChainRenderer>`](#layoutchainrenderer)
|
|
47
47
|
- [`<ErrorBoundary>`](#errorboundary)
|
|
48
|
+
- [`<ClientOnly>`](#clientonly)
|
|
48
49
|
- [组合式函数](#组合式函数)
|
|
49
50
|
- [页面缓存(keep-alive)](#页面缓存keep-alive)
|
|
50
51
|
- [页面过渡与重载](#页面过渡与重载)
|
|
@@ -68,7 +69,7 @@ app.mount('#app');
|
|
|
68
69
|
**包含:**
|
|
69
70
|
|
|
70
71
|
- `ubeanVue` Vue 插件(唯一接线入口 —— 插件优先,无工厂)
|
|
71
|
-
- `<PageView>` / `<Link>` / `<SlotView>` / `<LayoutChainRenderer>` / `<ErrorBoundary>`
|
|
72
|
+
- `<PageView>` / `<Link>` / `<SlotView>` / `<LayoutChainRenderer>` / `<ErrorBoundary>` / `<ClientOnly>`
|
|
72
73
|
- 页面缓存(keep-alive)store:命令式 + 声明式 API
|
|
73
74
|
- 页面过渡与重载信号(按页重载 + 缓存新鲜重建)
|
|
74
75
|
- View Transitions 工具(特性检测 + 包装器)
|
|
@@ -352,7 +353,7 @@ const result = await generateRouteFiles(
|
|
|
352
353
|
app.use(ubeanVue, { routes });
|
|
353
354
|
```
|
|
354
355
|
|
|
355
|
-
注册 `Link` / `PageView` / `SlotView` 全局组件,并从 `meta.cache: true` 声明播种 keep-alive include 列表(`initCachedViewsFromRoutes`)。这是本包主入口**唯一**的接线入口(与 `/vite` 子路径的同名 Vite 插件是两个不同入口)。
|
|
356
|
+
注册 `Link` / `PageView` / `SlotView` / `ClientOnly` 全局组件,并从 `meta.cache: true` 声明播种 keep-alive include 列表(`initCachedViewsFromRoutes`)。这是本包主入口**唯一**的接线入口(与 `/vite` 子路径的同名 Vite 插件是两个不同入口)。
|
|
356
357
|
|
|
357
358
|
## 组件
|
|
358
359
|
|
|
@@ -386,6 +387,43 @@ Props:`to`(字符串或位置对象)、`href`、`replace`、`activeClass`
|
|
|
386
387
|
|
|
387
388
|
捕获后代组件的渲染/异步/setup 错误并渲染配置的错误组件;路由变更时自动重置。
|
|
388
389
|
|
|
390
|
+
### `<ClientOnly>`
|
|
391
|
+
|
|
392
|
+
内容仅在客户端、水合之后渲染。适合浏览器态内容与模板片段(islands 的 `v-client.*` 编译转换只作用于大写开头的组件标签)。
|
|
393
|
+
|
|
394
|
+
```vue
|
|
395
|
+
<template>
|
|
396
|
+
<!-- 字符串 fallback prop -->
|
|
397
|
+
<ClientOnly fallback="加载中…">
|
|
398
|
+
<BrowserChart />
|
|
399
|
+
</ClientOnly>
|
|
400
|
+
|
|
401
|
+
<!-- #fallback slot(预留空间避免 CLS) -->
|
|
402
|
+
<ClientOnly>
|
|
403
|
+
<template #fallback>
|
|
404
|
+
<div class="skeleton" />
|
|
405
|
+
</template>
|
|
406
|
+
<BrowserMap />
|
|
407
|
+
</ClientOnly>
|
|
408
|
+
|
|
409
|
+
<!-- 裸模板片段也可以 -->
|
|
410
|
+
<ClientOnly>窗口宽度:{{ width }}px</ClientOnly>
|
|
411
|
+
</template>
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**水合安全**:占位内容(fallback / 空)在 SSR 与客户端水合首帧渲染**完全一致**,真实内容在挂载后 patch 进来。实现不做 `typeof window` 分支,因此不会产生水合 mismatch 警告。
|
|
415
|
+
|
|
416
|
+
**场景选择**:
|
|
417
|
+
|
|
418
|
+
| 场景 | 用法 |
|
|
419
|
+
| ------------------------------------------------------------- | ------------------------------------------------------------ |
|
|
420
|
+
| 模板片段 / 裸 HTML、同树渲染(完整应用上下文)、fallback slot | `<ClientOnly>` |
|
|
421
|
+
| 组件级延迟水合策略(idle/visible/media) | islands `v-client.*` |
|
|
422
|
+
| 编程式 island / 客户端组件包装 | `defineIsland` / `defineClientComponent`(`@ubean/islands`) |
|
|
423
|
+
| 文件级 server/client 拆分 | `.server.vue` / `.client.vue` |
|
|
424
|
+
|
|
425
|
+
> 注意:默认 slot 内容不进 SSR HTML —— SEO 关键内容不要用 `<ClientOnly>`。出现在 island 占位元素子内容中的 `<ClientOnly>` 会在 island 水合时一并被覆盖(islands 通过独立 app 水合)。
|
|
426
|
+
|
|
389
427
|
## 组合式函数
|
|
390
428
|
|
|
391
429
|
- **`usePage<T>()`** —— 精简版页面数据访问:原样返回 `PAGE_KEY` 注入的数据(`props` / `component` / `errors`),未注入时返回共享空对象。**不含**路由态(`url` / `params` / `query` / `meta`)—— 请直接用 vue-router 的 `useRoute()`。框架运行时(`@ubean/client`)在其上叠加自己的路由感知 `usePage` 提供完整 PageObject 协议。
|
package/dist/generator.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { a as ScannedLayout, o as ScannedPage } from "./types-CeX4SJit.js";
|
|
|
17
17
|
* `ScanPagesResult`(`@ubean/vue/vite` 的 `scanPages` 返回值)与聚合层的
|
|
18
18
|
* `ScanResult`(`@ubean/scan` 的 `scanProject`)都结构性满足本接口。
|
|
19
19
|
*/
|
|
20
|
-
interface GeneratorScanInput {
|
|
20
|
+
export interface GeneratorScanInput {
|
|
21
21
|
pages: ScannedPage[];
|
|
22
22
|
layouts: ScannedLayout[];
|
|
23
23
|
}
|
|
@@ -41,7 +41,7 @@ interface GeneratorScanInput {
|
|
|
41
41
|
* 在 `@ubean/vue` 不改变产物契约。精简 SPA 用虚拟模式的 `typed-router.d.ts`
|
|
42
42
|
* (`@ubean/vue/vite` 的 `generateTypedRouter`),不经过本生成器。
|
|
43
43
|
*/
|
|
44
|
-
interface GeneratorOptions {
|
|
44
|
+
export interface GeneratorOptions {
|
|
45
45
|
/** Project root directory (absolute). */
|
|
46
46
|
cwd: string;
|
|
47
47
|
/**
|
|
@@ -93,16 +93,16 @@ interface GeneratorOptions {
|
|
|
93
93
|
*/
|
|
94
94
|
headerComment?: string;
|
|
95
95
|
}
|
|
96
|
-
interface GeneratorResult {
|
|
96
|
+
export interface GeneratorResult {
|
|
97
97
|
routesPath?: string;
|
|
98
98
|
importsPath?: string;
|
|
99
99
|
dtsPath?: string;
|
|
100
100
|
routeCount: number;
|
|
101
101
|
layoutCount: number;
|
|
102
102
|
}
|
|
103
|
-
declare const DEFAULT_HEADER_COMMENT: string;
|
|
104
|
-
declare function generateRouteFiles(scan: GeneratorScanInput, options: GeneratorOptions): Promise<GeneratorResult>;
|
|
105
|
-
declare class RouteFileGenerator {
|
|
103
|
+
export declare const DEFAULT_HEADER_COMMENT: string;
|
|
104
|
+
export declare function generateRouteFiles(scan: GeneratorScanInput, options: GeneratorOptions): Promise<GeneratorResult>;
|
|
105
|
+
export declare class RouteFileGenerator {
|
|
106
106
|
private readonly opts;
|
|
107
107
|
constructor(options: GeneratorOptions);
|
|
108
108
|
generate(scan: GeneratorScanInput): Promise<GeneratorResult>;
|
|
@@ -127,5 +127,4 @@ declare class RouteFileGenerator {
|
|
|
127
127
|
*/
|
|
128
128
|
private renderRouteRecordInfo;
|
|
129
129
|
}
|
|
130
|
-
//#endregion
|
|
131
|
-
export { DEFAULT_HEADER_COMMENT, GeneratorOptions, GeneratorResult, GeneratorScanInput, RouteFileGenerator, generateRouteFiles };
|
|
130
|
+
//#endregion
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as ScannedLayout, i as ScanPagesResult, n as PageMeta, o as ScannedPage, r as ScanPagesOptions, t as PageHead } from "./types-CeX4SJit.js";
|
|
2
|
-
import { Component, ComputedRef, InjectionKey, Plugin, PropType, Ref, VNode } from "vue";
|
|
2
|
+
import { Component, ComputedRef, InjectionKey, Plugin, PropType, Ref, SlotsType, VNode } from "vue";
|
|
3
3
|
import { NavigationGuard, RouteMeta, Router } from "vue-router";
|
|
4
4
|
//#region src/view-transitions.d.ts
|
|
5
5
|
interface ViewTransitionOptions {
|
|
@@ -7,10 +7,10 @@ interface ViewTransitionOptions {
|
|
|
7
7
|
fallback?: 'none' | 'crossfade';
|
|
8
8
|
types?: string[];
|
|
9
9
|
}
|
|
10
|
-
declare function supportsViewTransitions(): boolean;
|
|
11
|
-
declare function withViewTransition<T>(callback: () => Promise<T> | T, options?: ViewTransitionOptions): Promise<T>;
|
|
12
|
-
declare function getNavigationType(): 'traverse' | 'push' | 'replace' | 'reload';
|
|
13
|
-
declare function useViewTransitionState(name: string): Record<string, string>;
|
|
10
|
+
export declare function supportsViewTransitions(): boolean;
|
|
11
|
+
export declare function withViewTransition<T>(callback: () => Promise<T> | T, options?: ViewTransitionOptions): Promise<T>;
|
|
12
|
+
export declare function getNavigationType(): 'traverse' | 'push' | 'replace' | 'reload';
|
|
13
|
+
export declare function useViewTransitionState(name: string): Record<string, string>;
|
|
14
14
|
//#endregion
|
|
15
15
|
//#region src/components.d.ts
|
|
16
16
|
/**
|
|
@@ -25,17 +25,17 @@ declare function useViewTransitionState(name: string): Record<string, string>;
|
|
|
25
25
|
* hydration href mismatch). Registered keys are shared globally, so every
|
|
26
26
|
* copy resolves to the same Symbol.
|
|
27
27
|
*/
|
|
28
|
-
declare const PAGE_KEY: unique symbol;
|
|
29
|
-
declare const TRANSITION_KEY: unique symbol;
|
|
30
|
-
declare const SSR_KEY: InjectionKey<boolean>;
|
|
31
|
-
declare const LOADING_KEY: InjectionKey<Component | null>;
|
|
32
|
-
declare const ERROR_KEY: InjectionKey<Component | null>;
|
|
28
|
+
export declare const PAGE_KEY: unique symbol;
|
|
29
|
+
export declare const TRANSITION_KEY: unique symbol;
|
|
30
|
+
export declare const SSR_KEY: InjectionKey<boolean>;
|
|
31
|
+
export declare const LOADING_KEY: InjectionKey<Component | null>;
|
|
32
|
+
export declare const ERROR_KEY: InjectionKey<Component | null>;
|
|
33
33
|
/**
|
|
34
34
|
* Injectable path localizer (optional i18n bridge). The framework runtime
|
|
35
35
|
* (`@ubean/client`) provides the reactive `localizePath`; when absent,
|
|
36
36
|
* `Link` renders paths verbatim — this package never imports i18n itself.
|
|
37
37
|
*/
|
|
38
|
-
declare const LOCALIZE_PATH_KEY: InjectionKey<(path: string, locale?: string) => string>;
|
|
38
|
+
export declare const LOCALIZE_PATH_KEY: InjectionKey<(path: string, locale?: string) => string>;
|
|
39
39
|
/**
|
|
40
40
|
* Layout chain context — provided by `LayoutChainRenderer` so that `<PageView />`
|
|
41
41
|
* inside a layout knows whether to render the next nested layout or the actual
|
|
@@ -46,13 +46,13 @@ interface LayoutChainContext {
|
|
|
46
46
|
components: Array<Component | null>;
|
|
47
47
|
depth: number;
|
|
48
48
|
}
|
|
49
|
-
declare const LAYOUT_CHAIN_KEY: InjectionKey<LayoutChainContext | null>;
|
|
49
|
+
export declare const LAYOUT_CHAIN_KEY: InjectionKey<LayoutChainContext | null>;
|
|
50
50
|
/**
|
|
51
51
|
* LayoutChainRenderer — recursively renders a chain of nested layout
|
|
52
52
|
* components (framework factories build the chain; lean SPAs may use it
|
|
53
53
|
* directly for nested layouts via render functions).
|
|
54
54
|
*/
|
|
55
|
-
declare const LayoutChainRenderer: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
55
|
+
export declare const LayoutChainRenderer: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
56
56
|
components: {
|
|
57
57
|
type: PropType<Array<Component | null>>;
|
|
58
58
|
required: true;
|
|
@@ -79,7 +79,7 @@ declare const LayoutChainRenderer: import("vue").DefineComponent<import("vue").E
|
|
|
79
79
|
* configured `errorComponent` instead of the slot content. The error state
|
|
80
80
|
* resets automatically on route change.
|
|
81
81
|
*/
|
|
82
|
-
declare const ErrorBoundary: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
82
|
+
export declare const ErrorBoundary: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
83
83
|
component: {
|
|
84
84
|
type: PropType<Component | null>;
|
|
85
85
|
default: null;
|
|
@@ -96,6 +96,56 @@ declare const ErrorBoundary: import("vue").DefineComponent<import("vue").Extract
|
|
|
96
96
|
}>> & Readonly<{}>, {
|
|
97
97
|
component: Component | null;
|
|
98
98
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
99
|
+
interface ClientOnlyProps {
|
|
100
|
+
/** Placeholder text rendered during SSR and client hydration render. */
|
|
101
|
+
fallback?: string;
|
|
102
|
+
}
|
|
103
|
+
interface ClientOnlySlots {
|
|
104
|
+
/** Content rendered only on the client, after hydration. */
|
|
105
|
+
default?: () => VNode[];
|
|
106
|
+
/** Placeholder rendered during SSR and the client's hydration render. */
|
|
107
|
+
fallback?: () => VNode[];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* ClientOnly — renders its default slot ONLY on the client, after hydration.
|
|
111
|
+
*
|
|
112
|
+
* ## Hydration-safety contract
|
|
113
|
+
*
|
|
114
|
+
* The implementation must NEVER branch on `typeof window` (that renders
|
|
115
|
+
* different trees on server vs client first paint → hydration mismatch).
|
|
116
|
+
* Instead it follows the same pattern as the islands runtime's
|
|
117
|
+
* `defineClientComponent`: the placeholder (fallback) is rendered during SSR
|
|
118
|
+
* AND during the client's hydration render, then `onMounted` flips
|
|
119
|
+
* `mounted` and Vue patches in the real content.
|
|
120
|
+
*
|
|
121
|
+
* ## Output shape
|
|
122
|
+
*
|
|
123
|
+
* - Default (no fallback): renders a comment vnode on BOTH server and client
|
|
124
|
+
* first paint — no wrapper element, no layout pollution.
|
|
125
|
+
* - `fallback` prop: renders the string as a text node.
|
|
126
|
+
* - `#fallback` slot: renders arbitrary placeholder content (reserve space to
|
|
127
|
+
* avoid CLS — the default slot content is absent from the SSR HTML).
|
|
128
|
+
*
|
|
129
|
+
* ## Typing
|
|
130
|
+
*
|
|
131
|
+
* Props and slots are fully typed (`ClientOnlyProps` / `ClientOnlySlots`) so
|
|
132
|
+
* templates get `#fallback` autocompletion and vue-tsc rejects unknown slots.
|
|
133
|
+
* Uses the function-style `defineComponent<Props, E, S>` overload — the
|
|
134
|
+
* object-style overload cannot carry slot types.
|
|
135
|
+
*
|
|
136
|
+
* ## When to use what (vs islands)
|
|
137
|
+
*
|
|
138
|
+
* - `<ClientOnly>` — template fragments / browser-only markup, same-tree
|
|
139
|
+
* rendering (full app context), works on plain HTML (the islands
|
|
140
|
+
* `v-client.*` transform only matches capitalized component tags).
|
|
141
|
+
* - `v-client.only` — islands architecture: registry-driven, hydrated via a
|
|
142
|
+
* separate app by `hydrateIslands()` (content inside an island placeholder
|
|
143
|
+
* is replaced on hydration, so `<ClientOnly>` inside island children is
|
|
144
|
+
* wiped with it).
|
|
145
|
+
* - `defineClientComponent` — programmatic wrapper (always renders a
|
|
146
|
+
* `<div data-client-only>` wrapper element).
|
|
147
|
+
*/
|
|
148
|
+
export declare const ClientOnly: import("vue").DefineSetupFnComponent<ClientOnlyProps, {}, SlotsType<ClientOnlySlots>, ClientOnlyProps & {}, import("vue").PublicProps>;
|
|
99
149
|
/**
|
|
100
150
|
* PageView — renders the matched route page wrapped with `<Transition>` and
|
|
101
151
|
* `<KeepAlive>`. Layouts use `<PageView />` (instead of `<slot />`) as the
|
|
@@ -123,7 +173,7 @@ declare const ErrorBoundary: import("vue").DefineComponent<import("vue").Extract
|
|
|
123
173
|
* On SSR (via `SSR_KEY`), KeepAlive/Transition/Suspense/ErrorBoundary are
|
|
124
174
|
* skipped (client-only concepts).
|
|
125
175
|
*/
|
|
126
|
-
declare const PageView: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
176
|
+
export declare const PageView: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
127
177
|
transition: {
|
|
128
178
|
type: (BooleanConstructor | StringConstructor)[];
|
|
129
179
|
default: undefined;
|
|
@@ -165,7 +215,7 @@ declare const PageView: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
165
215
|
* component (a nested `<RouterView>` there would look at depth + 1 and
|
|
166
216
|
* never find the named view).
|
|
167
217
|
*/
|
|
168
|
-
declare const SlotView: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
218
|
+
export declare const SlotView: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
169
219
|
name: {
|
|
170
220
|
type: StringConstructor;
|
|
171
221
|
required: true;
|
|
@@ -183,7 +233,7 @@ declare const SlotView: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
183
233
|
* Path localization is opt-in via `LOCALIZE_PATH_KEY` (provided by the
|
|
184
234
|
* framework runtime); lean SPAs render paths verbatim.
|
|
185
235
|
*/
|
|
186
|
-
declare const Link: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
236
|
+
export declare const Link: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
187
237
|
to: {
|
|
188
238
|
type: PropType<string | Record<string, unknown>>;
|
|
189
239
|
default: undefined;
|
|
@@ -288,9 +338,9 @@ interface UbeanVuePageData<TProps = Record<string, unknown>> {
|
|
|
288
338
|
*
|
|
289
339
|
* 未提供 `PAGE_KEY` 时返回共享的空对象(字段均为 undefined)。
|
|
290
340
|
*/
|
|
291
|
-
declare function usePage<TProps = Record<string, unknown>>(): UbeanVuePageData<TProps>;
|
|
341
|
+
export declare function usePage<TProps = Record<string, unknown>>(): UbeanVuePageData<TProps>;
|
|
292
342
|
/** `useViewTransition()` — whether native View Transitions are available/enabled. */
|
|
293
|
-
declare function useViewTransition(): {
|
|
343
|
+
export declare function useViewTransition(): {
|
|
294
344
|
enabled: boolean;
|
|
295
345
|
supports: boolean;
|
|
296
346
|
options: ViewTransitionOptions;
|
|
@@ -308,7 +358,7 @@ declare function useViewTransition(): {
|
|
|
308
358
|
* const router = createRouter({ history: createWebHistory(), routes });
|
|
309
359
|
* const app = createApp(App);
|
|
310
360
|
* app.use(router);
|
|
311
|
-
* app.use(ubeanVue, { routes }); // Link/PageView/SlotView + cache seeding
|
|
361
|
+
* app.use(ubeanVue, { routes }); // Link/PageView/SlotView/ClientOnly + cache seeding
|
|
312
362
|
* app.mount('#app');
|
|
313
363
|
* ```
|
|
314
364
|
*/
|
|
@@ -325,7 +375,7 @@ interface UbeanVueOptions {
|
|
|
325
375
|
};
|
|
326
376
|
}>;
|
|
327
377
|
}
|
|
328
|
-
declare const ubeanVue: Plugin<UbeanVueOptions>;
|
|
378
|
+
export declare const ubeanVue: Plugin<UbeanVueOptions>;
|
|
329
379
|
//#endregion
|
|
330
380
|
//#region src/cache-views.d.ts
|
|
331
381
|
interface UseCacheViewsReturn {
|
|
@@ -364,11 +414,11 @@ interface UseCacheViewsReturn {
|
|
|
364
414
|
* Safe to call inside any component `setup()`; the returned computeds are
|
|
365
415
|
* tracked by the rendering effect so `<keep-alive :include>` stays in sync.
|
|
366
416
|
*/
|
|
367
|
-
declare function useCacheViews(): UseCacheViewsReturn;
|
|
417
|
+
export declare function useCacheViews(): UseCacheViewsReturn;
|
|
368
418
|
/** Enable keep-alive caching for a specific page (by route name). */
|
|
369
|
-
declare function enablePageCache(name: string): void;
|
|
419
|
+
export declare function enablePageCache(name: string): void;
|
|
370
420
|
/** Disable keep-alive caching for a specific page (by route name). */
|
|
371
|
-
declare function disablePageCache(name: string): void;
|
|
421
|
+
export declare function disablePageCache(name: string): void;
|
|
372
422
|
/**
|
|
373
423
|
* Temporarily exclude a page from keep-alive (forces its cached instance
|
|
374
424
|
* to be pruned on next render). Pair with `includePageCache()` to restore.
|
|
@@ -377,11 +427,11 @@ declare function disablePageCache(name: string): void;
|
|
|
377
427
|
* the exclude list, wait a tick, then remove it — the cached instance is
|
|
378
428
|
* pruned and the page remounts fresh on next render.
|
|
379
429
|
*/
|
|
380
|
-
declare function excludePageCache(name: string): void;
|
|
430
|
+
export declare function excludePageCache(name: string): void;
|
|
381
431
|
/** Remove a route name from the cache exclude list (restores caching). */
|
|
382
|
-
declare function includePageCache(name: string): void;
|
|
432
|
+
export declare function includePageCache(name: string): void;
|
|
383
433
|
/** Whether a specific page is currently excluded from cache. */
|
|
384
|
-
declare function isPageExcluded(name: string): boolean;
|
|
434
|
+
export declare function isPageExcluded(name: string): boolean;
|
|
385
435
|
/**
|
|
386
436
|
* Reload a cached page by pruning its keep-alive instance.
|
|
387
437
|
*
|
|
@@ -403,7 +453,7 @@ declare function isPageExcluded(name: string): boolean;
|
|
|
403
453
|
* @param delay Ignored — kept for signature compatibility with the previous
|
|
404
454
|
* exclude→include implementation.
|
|
405
455
|
*/
|
|
406
|
-
declare function resetRouteCache(name?: string, _delay?: number): Promise<void>;
|
|
456
|
+
export declare function resetRouteCache(name?: string, _delay?: number): Promise<void>;
|
|
407
457
|
/**
|
|
408
458
|
* Invalidate cached page instance(s).
|
|
409
459
|
*
|
|
@@ -414,17 +464,17 @@ declare function resetRouteCache(name?: string, _delay?: number): Promise<void>;
|
|
|
414
464
|
* Note: the page will be re-cached on next visit if its `definePage({ cache: true })`
|
|
415
465
|
* is still in effect or `enablePageCache(name)` is called again.
|
|
416
466
|
*/
|
|
417
|
-
declare function invalidatePageCache(name?: string): void;
|
|
467
|
+
export declare function invalidatePageCache(name?: string): void;
|
|
418
468
|
/** Whether a specific page is currently cached. */
|
|
419
|
-
declare function isPageCached(name: string): boolean;
|
|
469
|
+
export declare function isPageCached(name: string): boolean;
|
|
420
470
|
/** Whether page caching is globally enabled. */
|
|
421
|
-
declare function isCacheEnabled(): boolean;
|
|
471
|
+
export declare function isCacheEnabled(): boolean;
|
|
422
472
|
/** Reactive ref of cached route names — for advanced use cases. */
|
|
423
|
-
declare function getCachedViewNames(): Ref<string[]>;
|
|
473
|
+
export declare function getCachedViewNames(): Ref<string[]>;
|
|
424
474
|
/** Reactive ref of excluded route names — for advanced use cases. */
|
|
425
|
-
declare function getExcludedViewNames(): Ref<string[]>;
|
|
475
|
+
export declare function getExcludedViewNames(): Ref<string[]>;
|
|
426
476
|
/** Reactive ref of the global cache-enabled flag. */
|
|
427
|
-
declare function getCacheEnabled(): Ref<boolean>;
|
|
477
|
+
export declare function getCacheEnabled(): Ref<boolean>;
|
|
428
478
|
/**
|
|
429
479
|
* Seed the cache include list from route metadata.
|
|
430
480
|
*
|
|
@@ -432,7 +482,7 @@ declare function getCacheEnabled(): Ref<boolean>;
|
|
|
432
482
|
* declared `definePage({ cache: true })`. Routes without `meta.cache` are
|
|
433
483
|
* left untouched so runtime toggling remains possible.
|
|
434
484
|
*/
|
|
435
|
-
declare function initCachedViewsFromRoutes(routes: Array<{
|
|
485
|
+
export declare function initCachedViewsFromRoutes(routes: Array<{
|
|
436
486
|
name?: string | symbol;
|
|
437
487
|
meta?: {
|
|
438
488
|
cache?: boolean;
|
|
@@ -448,14 +498,14 @@ declare function initCachedViewsFromRoutes(routes: Array<{
|
|
|
448
498
|
*
|
|
449
499
|
* @internal — used by `createLayoutWrapper` in `app.ts`.
|
|
450
500
|
*/
|
|
451
|
-
declare function getNamedPageWrapper(routeName: string, original: Component): Component;
|
|
501
|
+
export declare function getNamedPageWrapper(routeName: string, original: Component): Component;
|
|
452
502
|
/**
|
|
453
503
|
* Clear the wrapper registry. Mainly useful in tests or when the entire
|
|
454
504
|
* route table is rebuilt.
|
|
455
505
|
*
|
|
456
506
|
* @internal
|
|
457
507
|
*/
|
|
458
|
-
declare function resetNamedPageWrappers(): void;
|
|
508
|
+
export declare function resetNamedPageWrappers(): void;
|
|
459
509
|
//#endregion
|
|
460
510
|
//#region src/page-runtime.d.ts
|
|
461
511
|
interface UsePageTransitionReturn {
|
|
@@ -483,13 +533,13 @@ interface UsePageTransitionReturn {
|
|
|
483
533
|
* transition.clear();
|
|
484
534
|
* ```
|
|
485
535
|
*/
|
|
486
|
-
declare function usePageTransition(): UsePageTransitionReturn;
|
|
536
|
+
export declare function usePageTransition(): UsePageTransitionReturn;
|
|
487
537
|
/** Imperatively set the global page transition name. */
|
|
488
|
-
declare function setPageTransition(name: string): void;
|
|
538
|
+
export declare function setPageTransition(name: string): void;
|
|
489
539
|
/** Imperatively clear the global page transition name (disables transitions). */
|
|
490
|
-
declare function clearPageTransition(): void;
|
|
540
|
+
export declare function clearPageTransition(): void;
|
|
491
541
|
/** Reactive ref of the global transition name — for advanced use cases. */
|
|
492
|
-
declare function getPageTransitionName(): Ref<string>;
|
|
542
|
+
export declare function getPageTransitionName(): Ref<string>;
|
|
493
543
|
interface UseReloadSignalReturn {
|
|
494
544
|
/** Reactive counter that increments on each `reloadPage()` call. */
|
|
495
545
|
counter: ComputedRef<number>;
|
|
@@ -498,7 +548,7 @@ interface UseReloadSignalReturn {
|
|
|
498
548
|
/** Trigger a reload of the current page. */
|
|
499
549
|
reload: (routeName?: string, duration?: number) => Promise<void>;
|
|
500
550
|
}
|
|
501
|
-
declare function useReloadSignal(routeNameGetter?: () => string | undefined | null): UseReloadSignalReturn;
|
|
551
|
+
export declare function useReloadSignal(routeNameGetter?: () => string | undefined | null): UseReloadSignalReturn;
|
|
502
552
|
/**
|
|
503
553
|
* Imperatively trigger a page reload.
|
|
504
554
|
*
|
|
@@ -510,11 +560,11 @@ declare function useReloadSignal(routeNameGetter?: () => string | undefined | nu
|
|
|
510
560
|
* `useReloadSignal().counter` consumers; nothing remounts).
|
|
511
561
|
* - `duration`: milliseconds to wait for transition/cleanup. Defaults to 300.
|
|
512
562
|
*/
|
|
513
|
-
declare function reloadPage(name?: string, duration?: number): Promise<void>;
|
|
563
|
+
export declare function reloadPage(name?: string, duration?: number): Promise<void>;
|
|
514
564
|
/** Reactive ref of the reload counter — for advanced use cases. */
|
|
515
|
-
declare function getReloadCounter(): Ref<number>;
|
|
565
|
+
export declare function getReloadCounter(): Ref<number>;
|
|
516
566
|
/** Whether a reload is currently in progress. */
|
|
517
|
-
declare function isReloading(): boolean;
|
|
567
|
+
export declare function isReloading(): boolean;
|
|
518
568
|
//#endregion
|
|
519
569
|
//#region src/define-page.d.ts
|
|
520
570
|
/**
|
|
@@ -607,7 +657,7 @@ interface DefineClientPageOptions {
|
|
|
607
657
|
* });
|
|
608
658
|
* ```
|
|
609
659
|
*/
|
|
610
|
-
declare function definePage(options?: DefineClientPageOptions): void;
|
|
660
|
+
export declare function definePage(options?: DefineClientPageOptions): void;
|
|
611
661
|
//#endregion
|
|
612
662
|
//#region src/router-location.d.ts
|
|
613
663
|
interface RouteLocationRaw {
|
|
@@ -618,7 +668,7 @@ interface RouteLocationRaw {
|
|
|
618
668
|
hash?: string;
|
|
619
669
|
}
|
|
620
670
|
type RouteLocation = string | RouteLocationRaw;
|
|
621
|
-
declare function resolveRoute(to: RouteLocation, routeMap?: Record<string, {
|
|
671
|
+
export declare function resolveRoute(to: RouteLocation, routeMap?: Record<string, {
|
|
622
672
|
path: string;
|
|
623
673
|
route: string;
|
|
624
674
|
}>): string;
|
|
@@ -631,7 +681,7 @@ type TypedLinkProps = {
|
|
|
631
681
|
activeClass?: string;
|
|
632
682
|
exactActiveClass?: string;
|
|
633
683
|
};
|
|
634
|
-
declare function isActiveRoute(currentPath: string, targetHref: string, exact?: boolean): boolean;
|
|
684
|
+
export declare function isActiveRoute(currentPath: string, targetHref: string, exact?: boolean): boolean;
|
|
635
685
|
//#endregion
|
|
636
686
|
//#region src/matchers.d.ts
|
|
637
687
|
/**
|
|
@@ -656,25 +706,25 @@ type MatcherFunction = (value: string) => boolean | null | undefined;
|
|
|
656
706
|
* export default defineMatcher('numeric', (value) => /^\d+$/.test(value));
|
|
657
707
|
* ```
|
|
658
708
|
*/
|
|
659
|
-
declare function defineMatcher(name: string, fn: MatcherFunction): MatcherFunction;
|
|
709
|
+
export declare function defineMatcher(name: string, fn: MatcherFunction): MatcherFunction;
|
|
660
710
|
/**
|
|
661
711
|
* 按名称获取已注册的 matcher。未注册时返回 `undefined`。
|
|
662
712
|
*/
|
|
663
|
-
declare function getMatcher(name: string): MatcherFunction | undefined;
|
|
713
|
+
export declare function getMatcher(name: string): MatcherFunction | undefined;
|
|
664
714
|
/**
|
|
665
715
|
* 判断指定名称的 matcher 是否已注册。
|
|
666
716
|
*/
|
|
667
|
-
declare function hasMatcher(name: string): boolean;
|
|
717
|
+
export declare function hasMatcher(name: string): boolean;
|
|
668
718
|
/**
|
|
669
719
|
* 获取所有已注册 matcher 的名称列表(主要用于调试 / DevTools)。
|
|
670
720
|
*/
|
|
671
|
-
declare function listMatcherNames(): string[];
|
|
721
|
+
export declare function listMatcherNames(): string[];
|
|
672
722
|
/**
|
|
673
723
|
* 清空所有已注册的 matcher。
|
|
674
724
|
*
|
|
675
725
|
* **仅供测试使用** —— 应用代码不应调用,避免误删其他模块注册的 matcher。
|
|
676
726
|
*/
|
|
677
|
-
declare function clearMatchers(): void;
|
|
727
|
+
export declare function clearMatchers(): void;
|
|
678
728
|
/**
|
|
679
729
|
* 校验一组路由参数是否通过对应的 matcher。
|
|
680
730
|
*
|
|
@@ -689,7 +739,7 @@ declare function clearMatchers(): void;
|
|
|
689
739
|
* - 若参数不存在于 `params` 中,视为校验失败返回 `false`
|
|
690
740
|
* - matcher 函数抛异常时,捕获并视为不匹配
|
|
691
741
|
*/
|
|
692
|
-
declare function validateParams(matchers: Record<string, string> | undefined, params: Record<string, string | string[] | undefined>): boolean;
|
|
742
|
+
export declare function validateParams(matchers: Record<string, string> | undefined, params: Record<string, string | string[] | undefined>): boolean;
|
|
693
743
|
interface MatcherGuardOptions {
|
|
694
744
|
/**
|
|
695
745
|
* matcher 校验失败时跳转的路由名。默认 `'NotFound'`。
|
|
@@ -728,7 +778,7 @@ interface MatcherGuardOptions {
|
|
|
728
778
|
* - 对于纯 SPA 应用(`ssr: false`),强烈建议注册此守卫,否则客户端导航到
|
|
729
779
|
* `/users/abc`(应当被 `[id=numeric]` 拒绝)会渲染页面而非 404。
|
|
730
780
|
*/
|
|
731
|
-
declare function createMatcherGuard(options?: MatcherGuardOptions): NavigationGuard;
|
|
781
|
+
export declare function createMatcherGuard(options?: MatcherGuardOptions): NavigationGuard;
|
|
732
782
|
//#endregion
|
|
733
783
|
//#region src/head.d.ts
|
|
734
784
|
/**
|
|
@@ -751,7 +801,7 @@ interface PageHeadClient {
|
|
|
751
801
|
* 将静态 `PageHead` push 进 head 实例(falsy 字段自动跳过)。
|
|
752
802
|
* 与 SSR 侧 `pushPageHead`(@ubean/client/ssr)语义一致,保证双端同构。
|
|
753
803
|
*/
|
|
754
|
-
declare function pushPageHead(head: PageHeadClient, pageHead: PageHead): void;
|
|
804
|
+
export declare function pushPageHead(head: PageHeadClient, pageHead: PageHead): void;
|
|
755
805
|
/**
|
|
756
806
|
* SPA 页面级 head 守卫:导航完成后读取 `route.meta.head` 并 push 进 head 实例。
|
|
757
807
|
*
|
|
@@ -766,14 +816,14 @@ declare function pushPageHead(head: PageHeadClient, pageHead: PageHead): void;
|
|
|
766
816
|
*
|
|
767
817
|
* 初始导航同样触发(vue-router 首次导航完成后 afterEach 执行)。
|
|
768
818
|
*/
|
|
769
|
-
declare function setupPageHeadGuard(router: Router, head: PageHeadClient): void;
|
|
819
|
+
export declare function setupPageHeadGuard(router: Router, head: PageHeadClient): void;
|
|
770
820
|
/**
|
|
771
821
|
* 按需创建 head 实例(懒加载 `@unhead/vue`,optional peer dependency)。
|
|
772
822
|
*
|
|
773
823
|
* 项目已自带 unhead 实例时无需调用 —— 直接把实例传给
|
|
774
824
|
* `setupPageHeadGuard` 即可,零额外加载。
|
|
775
825
|
*/
|
|
776
|
-
declare function createPageHead(): Promise<PageHeadClient>;
|
|
826
|
+
export declare function createPageHead(): Promise<PageHeadClient>;
|
|
777
827
|
//#endregion
|
|
778
828
|
//#region src/index.d.ts
|
|
779
829
|
declare module 'vue-router' {
|
|
@@ -805,4 +855,4 @@ declare module 'vue-router' {
|
|
|
805
855
|
}
|
|
806
856
|
}
|
|
807
857
|
//#endregion
|
|
808
|
-
export { type
|
|
858
|
+
export { type ClientOnlyProps, type ClientOnlySlots, type DefineClientPageOptions, type LayoutChainContext, type MatcherFunction, type MatcherGuardOptions, type PageHead, type PageHeadClient, type PageMeta, type RouteLocation, type RouteLocationRaw, type ScanPagesOptions, type ScanPagesResult, type ScannedLayout, type ScannedPage, type TypedLinkProps, type UbeanVueOptions, type UbeanVuePageData, type UseCacheViewsReturn, type UsePageTransitionReturn, type UseReloadSignalReturn, type ViewTransitionOptions, ubeanVue as default };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KeepAlive, Suspense, Transition, computed, createCommentVNode, defineAsyncComponent, defineComponent, h, inject, nextTick, onErrorCaptured, provide, reactive, ref, shallowRef, watch } from "vue";
|
|
1
|
+
import { KeepAlive, Suspense, Transition, computed, createCommentVNode, createTextVNode, defineAsyncComponent, defineComponent, h, inject, nextTick, onErrorCaptured, onMounted, provide, reactive, ref, shallowRef, watch } from "vue";
|
|
2
2
|
import { RouterLink, RouterView, useRoute } from "vue-router";
|
|
3
3
|
//#region src/cache-views.ts
|
|
4
4
|
/**
|
|
@@ -624,6 +624,61 @@ const ErrorBoundary = defineComponent({
|
|
|
624
624
|
}
|
|
625
625
|
});
|
|
626
626
|
/**
|
|
627
|
+
* ClientOnly — renders its default slot ONLY on the client, after hydration.
|
|
628
|
+
*
|
|
629
|
+
* ## Hydration-safety contract
|
|
630
|
+
*
|
|
631
|
+
* The implementation must NEVER branch on `typeof window` (that renders
|
|
632
|
+
* different trees on server vs client first paint → hydration mismatch).
|
|
633
|
+
* Instead it follows the same pattern as the islands runtime's
|
|
634
|
+
* `defineClientComponent`: the placeholder (fallback) is rendered during SSR
|
|
635
|
+
* AND during the client's hydration render, then `onMounted` flips
|
|
636
|
+
* `mounted` and Vue patches in the real content.
|
|
637
|
+
*
|
|
638
|
+
* ## Output shape
|
|
639
|
+
*
|
|
640
|
+
* - Default (no fallback): renders a comment vnode on BOTH server and client
|
|
641
|
+
* first paint — no wrapper element, no layout pollution.
|
|
642
|
+
* - `fallback` prop: renders the string as a text node.
|
|
643
|
+
* - `#fallback` slot: renders arbitrary placeholder content (reserve space to
|
|
644
|
+
* avoid CLS — the default slot content is absent from the SSR HTML).
|
|
645
|
+
*
|
|
646
|
+
* ## Typing
|
|
647
|
+
*
|
|
648
|
+
* Props and slots are fully typed (`ClientOnlyProps` / `ClientOnlySlots`) so
|
|
649
|
+
* templates get `#fallback` autocompletion and vue-tsc rejects unknown slots.
|
|
650
|
+
* Uses the function-style `defineComponent<Props, E, S>` overload — the
|
|
651
|
+
* object-style overload cannot carry slot types.
|
|
652
|
+
*
|
|
653
|
+
* ## When to use what (vs islands)
|
|
654
|
+
*
|
|
655
|
+
* - `<ClientOnly>` — template fragments / browser-only markup, same-tree
|
|
656
|
+
* rendering (full app context), works on plain HTML (the islands
|
|
657
|
+
* `v-client.*` transform only matches capitalized component tags).
|
|
658
|
+
* - `v-client.only` — islands architecture: registry-driven, hydrated via a
|
|
659
|
+
* separate app by `hydrateIslands()` (content inside an island placeholder
|
|
660
|
+
* is replaced on hydration, so `<ClientOnly>` inside island children is
|
|
661
|
+
* wiped with it).
|
|
662
|
+
* - `defineClientComponent` — programmatic wrapper (always renders a
|
|
663
|
+
* `<div data-client-only>` wrapper element).
|
|
664
|
+
*/
|
|
665
|
+
const ClientOnly = defineComponent((props, { slots }) => {
|
|
666
|
+
const mounted = ref(false);
|
|
667
|
+
onMounted(() => {
|
|
668
|
+
mounted.value = true;
|
|
669
|
+
});
|
|
670
|
+
return () => {
|
|
671
|
+
if (mounted.value) return slots.default?.() ?? null;
|
|
672
|
+
if (slots.fallback) return slots.fallback();
|
|
673
|
+
if (props.fallback !== void 0) return createTextVNode(props.fallback);
|
|
674
|
+
return createCommentVNode("client-only");
|
|
675
|
+
};
|
|
676
|
+
}, {
|
|
677
|
+
name: "ClientOnly",
|
|
678
|
+
inheritAttrs: false,
|
|
679
|
+
props: ["fallback"]
|
|
680
|
+
});
|
|
681
|
+
/**
|
|
627
682
|
* PageView — renders the matched route page wrapped with `<Transition>` and
|
|
628
683
|
* `<KeepAlive>`. Layouts use `<PageView />` (instead of `<slot />`) as the
|
|
629
684
|
* page outlet.
|
|
@@ -868,6 +923,7 @@ const ubeanVue = { install(app, options) {
|
|
|
868
923
|
app.component("Link", Link);
|
|
869
924
|
app.component("PageView", PageView);
|
|
870
925
|
app.component("SlotView", SlotView);
|
|
926
|
+
app.component("ClientOnly", ClientOnly);
|
|
871
927
|
if (options?.routes) initCachedViewsFromRoutes(options.routes);
|
|
872
928
|
} };
|
|
873
929
|
//#endregion
|
|
@@ -1092,4 +1148,4 @@ async function createPageHead() {
|
|
|
1092
1148
|
throw new Error("[ubean/vue] createPageHead() requires `@unhead/vue` to be installed (optional peer dependency)");
|
|
1093
1149
|
}
|
|
1094
1150
|
//#endregion
|
|
1095
|
-
export { ERROR_KEY, ErrorBoundary, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, LayoutChainRenderer, Link, PAGE_KEY, PageView, SSR_KEY, SlotView, TRANSITION_KEY, clearMatchers, clearPageTransition, createMatcherGuard, createPageHead, ubeanVue as default, ubeanVue, defineMatcher, definePage, disablePageCache, enablePageCache, excludePageCache, getCacheEnabled, getCachedViewNames, getExcludedViewNames, getMatcher, getNamedPageWrapper, getNavigationType, getPageTransitionName, getReloadCounter, hasMatcher, includePageCache, initCachedViewsFromRoutes, invalidatePageCache, isActiveRoute, isCacheEnabled, isPageCached, isPageExcluded, isReloading, listMatcherNames, pushPageHead, reloadPage, resetNamedPageWrappers, resetRouteCache, resolveRoute, setPageTransition, setupPageHeadGuard, supportsViewTransitions, useCacheViews, usePage, usePageTransition, useReloadSignal, useViewTransition, useViewTransitionState, validateParams, withViewTransition };
|
|
1151
|
+
export { ClientOnly, ERROR_KEY, ErrorBoundary, LAYOUT_CHAIN_KEY, LOADING_KEY, LOCALIZE_PATH_KEY, LayoutChainRenderer, Link, PAGE_KEY, PageView, SSR_KEY, SlotView, TRANSITION_KEY, clearMatchers, clearPageTransition, createMatcherGuard, createPageHead, ubeanVue as default, ubeanVue, defineMatcher, definePage, disablePageCache, enablePageCache, excludePageCache, getCacheEnabled, getCachedViewNames, getExcludedViewNames, getMatcher, getNamedPageWrapper, getNavigationType, getPageTransitionName, getReloadCounter, hasMatcher, includePageCache, initCachedViewsFromRoutes, invalidatePageCache, isActiveRoute, isCacheEnabled, isPageCached, isPageExcluded, isReloading, listMatcherNames, pushPageHead, reloadPage, resetNamedPageWrappers, resetRouteCache, resolveRoute, setPageTransition, setupPageHeadGuard, supportsViewTransitions, useCacheViews, usePage, usePageTransition, useReloadSignal, useViewTransition, useViewTransitionState, validateParams, withViewTransition };
|
package/dist/vite.d.ts
CHANGED
|
@@ -10,13 +10,13 @@ import { Plugin } from "vite";
|
|
|
10
10
|
* `(.)target/page.vue` → intercept from same level, target = 'target'
|
|
11
11
|
* `(...)target/page.vue` → intercept from root, target = 'target'
|
|
12
12
|
*/
|
|
13
|
-
declare function extractSlotAndIntercept(fileBase: string): {
|
|
13
|
+
export declare function extractSlotAndIntercept(fileBase: string): {
|
|
14
14
|
cleanedBase: string;
|
|
15
15
|
slot?: string;
|
|
16
16
|
interceptFrom?: string;
|
|
17
17
|
interceptTarget?: string;
|
|
18
18
|
};
|
|
19
|
-
declare function scanPages(options: ScanPagesOptions): Promise<ScanPagesResult>;
|
|
19
|
+
export declare function scanPages(options: ScanPagesOptions): Promise<ScanPagesResult>;
|
|
20
20
|
//#endregion
|
|
21
21
|
//#region src/virtual-pages.d.ts
|
|
22
22
|
type PagesModuleInput = Pick<ScanPagesResult, 'pages' | 'layouts' | 'notFoundPage' | 'loadingPage' | 'errorPage'>;
|
|
@@ -28,7 +28,7 @@ interface LocaleRouteCompileOptions {
|
|
|
28
28
|
vueParam?: string;
|
|
29
29
|
}
|
|
30
30
|
/** Generate the virtual module source(plain JS — no TS syntax, SSR-safe)。 */
|
|
31
|
-
declare function generatePagesModuleSource(input: PagesModuleInput, locale?: LocaleRouteCompileOptions): string;
|
|
31
|
+
export declare function generatePagesModuleSource(input: PagesModuleInput, locale?: LocaleRouteCompileOptions): string;
|
|
32
32
|
/**
|
|
33
33
|
* Generate the ambient module declaration d.ts for the virtual module.
|
|
34
34
|
*
|
|
@@ -36,7 +36,7 @@ declare function generatePagesModuleSource(input: PagesModuleInput, locale?: Loc
|
|
|
36
36
|
* script 上下文中才注册「环境模块声明」;在 module 文件中会被当作模块增强,
|
|
37
37
|
* 对不存在的模块无法生效。由 `/vite` 插件写入 `<root>/ubean-vue-routes.d.ts`。
|
|
38
38
|
*/
|
|
39
|
-
declare function generateVirtualModuleDts(input: PagesModuleInput, moduleId?: string): string;
|
|
39
|
+
export declare function generateVirtualModuleDts(input: PagesModuleInput, moduleId?: string): string;
|
|
40
40
|
/**
|
|
41
41
|
* Generate `typed-router.d.ts` content for the virtual module.
|
|
42
42
|
*
|
|
@@ -48,10 +48,10 @@ declare function generateVirtualModuleDts(input: PagesModuleInput, moduleId?: st
|
|
|
48
48
|
* 若以 script 形式输出,同名环境模块声明会整体遮蔽真实包的类型。
|
|
49
49
|
* 虚拟模块自身的环境声明见 `generateVirtualModuleDts`。
|
|
50
50
|
*/
|
|
51
|
-
declare function generateTypedRouter(input: PagesModuleInput, _moduleId?: string): string;
|
|
51
|
+
export declare function generateTypedRouter(input: PagesModuleInput, _moduleId?: string): string;
|
|
52
52
|
//#endregion
|
|
53
53
|
//#region src/route-path.d.ts
|
|
54
|
-
declare function stripRouteGroups(path: string): string;
|
|
54
|
+
export declare function stripRouteGroups(path: string): string;
|
|
55
55
|
interface ParsedRoutePath {
|
|
56
56
|
route: string;
|
|
57
57
|
method?: string;
|
|
@@ -78,11 +78,11 @@ interface ParsedRoutePath {
|
|
|
78
78
|
* parseMatchers('blog/[...slug=any]') → { cleaned: 'blog/[...slug]', matchers: { slug: 'any' } }
|
|
79
79
|
* parseMatchers('users/[id]') → { cleaned: 'users/[id]', matchers: undefined }
|
|
80
80
|
*/
|
|
81
|
-
declare function parseMatchers(filePath: string): {
|
|
81
|
+
export declare function parseMatchers(filePath: string): {
|
|
82
82
|
cleaned: string;
|
|
83
83
|
matchers?: Record<string, string>;
|
|
84
84
|
};
|
|
85
|
-
declare function filePathToRoute(filePath: string, prefix?: string): ParsedRoutePath;
|
|
85
|
+
export declare function filePathToRoute(filePath: string, prefix?: string): ParsedRoutePath;
|
|
86
86
|
//#endregion
|
|
87
87
|
//#region src/route-name.d.ts
|
|
88
88
|
/**
|
|
@@ -90,8 +90,8 @@ declare function filePathToRoute(filePath: string, prefix?: string): ParsedRoute
|
|
|
90
90
|
* `@ubean/scan` 聚合层 re-export 保持向后兼容;服务端专用的
|
|
91
91
|
* `generateApiRouteId` 保留在 `@ubean/scan`。
|
|
92
92
|
*/
|
|
93
|
-
declare function generateRouteName(routePath: string): string;
|
|
94
|
-
declare function generateLayoutName(layoutPath: string): string;
|
|
93
|
+
export declare function generateRouteName(routePath: string): string;
|
|
94
|
+
export declare function generateLayoutName(layoutPath: string): string;
|
|
95
95
|
//#endregion
|
|
96
96
|
//#region src/extract-page.d.ts
|
|
97
97
|
/**
|
|
@@ -99,7 +99,7 @@ declare function generateLayoutName(layoutPath: string): string;
|
|
|
99
99
|
* 对象字面量参数。供本包的 `definePage` 提取与 `@ubean/scan` 的
|
|
100
100
|
* `defineHandlerMeta` 提取共用(单一解析器实现)。
|
|
101
101
|
*/
|
|
102
|
-
declare function extractCallObject(code: string, funcName: string): Record<string, unknown> | null;
|
|
102
|
+
export declare function extractCallObject(code: string, funcName: string): Record<string, unknown> | null;
|
|
103
103
|
/**
|
|
104
104
|
* Normalize a parsed `head` value into a `PageHead` object。
|
|
105
105
|
*
|
|
@@ -107,8 +107,8 @@ declare function extractCallObject(code: string, funcName: string): Record<strin
|
|
|
107
107
|
* `definePage`) and Markdown pages (via frontmatter) share the same rules.
|
|
108
108
|
*/
|
|
109
109
|
declare function normalizePageHead(raw: unknown): PageHead | undefined;
|
|
110
|
-
declare function extractDefinePageFromCode(code: string): PageMeta | null;
|
|
111
|
-
declare function extractDefinePage(filePath: string): Promise<PageMeta | null>;
|
|
110
|
+
export declare function extractDefinePageFromCode(code: string): PageMeta | null;
|
|
111
|
+
export declare function extractDefinePage(filePath: string): Promise<PageMeta | null>;
|
|
112
112
|
//#endregion
|
|
113
113
|
//#region src/vite.d.ts
|
|
114
114
|
/**
|
|
@@ -125,8 +125,8 @@ declare function extractDefinePage(filePath: string): Promise<PageMeta | null>;
|
|
|
125
125
|
* 框架层(`@ubean/vite`)通过 `generatePagesModuleSource` /
|
|
126
126
|
* `scanPages` / `generateTypedRouter` 复用同一套生成器。
|
|
127
127
|
*/
|
|
128
|
-
declare const VUE_ROUTES_MODULE_ID = "virtual:ubean-vue-routes";
|
|
129
|
-
interface UbeanVueViteOptions {
|
|
128
|
+
export declare const VUE_ROUTES_MODULE_ID = "virtual:ubean-vue-routes";
|
|
129
|
+
export interface UbeanVueViteOptions {
|
|
130
130
|
/** 页面目录(相对项目根或绝对),支持多目录。默认 `'src/pages'`。 */
|
|
131
131
|
pagesDir?: string | string[];
|
|
132
132
|
/** 布局目录(相对项目根或绝对),支持多目录。默认 `'src/layouts'`。 */
|
|
@@ -161,11 +161,11 @@ interface UbeanVueViteOptions {
|
|
|
161
161
|
head?: boolean;
|
|
162
162
|
}
|
|
163
163
|
/** 剥离 `definePage({...})` 宏调用(构建期,扫描后源码中不再需要)。 */
|
|
164
|
-
declare function stripDefinePageCalls(code: string): string;
|
|
164
|
+
export declare function stripDefinePageCalls(code: string): string;
|
|
165
165
|
/**
|
|
166
166
|
* 扫描客户端页面/布局(对外工具函数,`root` 为项目根)。
|
|
167
167
|
*/
|
|
168
|
-
declare function scanClientPages(root: string, options?: UbeanVueViteOptions): Promise<ScanPagesResult>;
|
|
169
|
-
declare function ubeanVueVite(options?: UbeanVueViteOptions): Plugin;
|
|
168
|
+
export declare function scanClientPages(root: string, options?: UbeanVueViteOptions): Promise<ScanPagesResult>;
|
|
169
|
+
export declare function ubeanVueVite(options?: UbeanVueViteOptions): Plugin;
|
|
170
170
|
//#endregion
|
|
171
|
-
export { type LocaleRouteCompileOptions, type PagesModuleInput, type ParsedRoutePath,
|
|
171
|
+
export { type LocaleRouteCompileOptions, type PagesModuleInput, type ParsedRoutePath, ubeanVueVite as default, normalizePageHead };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ubean/vue",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "Lean Vue client kernel & page-routing owner for ubean — plugin-first page routing outlet, page cache (keep-alive), transitions, reload signal, definePage macro + file-routing Vite plugin (/vite: multi-dir scan, reuse routes, markdown & page-head opt-in). Runtime deps: vue + vue-router only.",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -33,13 +33,13 @@
|
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^26.5.0",
|
|
36
|
-
"@ubean/markdown": "0.4.
|
|
36
|
+
"@ubean/markdown": "0.4.6",
|
|
37
37
|
"@unhead/vue": "^3.4.0",
|
|
38
38
|
"@vue/server-renderer": "^3.5.42",
|
|
39
39
|
"happy-dom": "^20.14.0",
|
|
40
|
-
"typescript": "
|
|
41
|
-
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.
|
|
42
|
-
"vite-plus": "0.3.
|
|
40
|
+
"typescript": "npm:typescript-native-bridge@latest",
|
|
41
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.1",
|
|
42
|
+
"vite-plus": "0.3.1",
|
|
43
43
|
"vitest": "5.0.0"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|