weifuwu 0.34.0 → 0.35.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 +20 -14
- package/dist/client/app.d.ts +7 -0
- package/dist/client/error-boundary.d.ts +23 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +195 -95
- package/dist/client/router.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -315,28 +315,34 @@ esbuild.build({
|
|
|
315
315
|
})
|
|
316
316
|
```
|
|
317
317
|
|
|
318
|
-
### 状态管理
|
|
318
|
+
### 状态管理 — 深度 Proxy
|
|
319
|
+
|
|
320
|
+
`ctx.ui.$` 是**深度 Proxy**:任何属性/数组/对象层面的写入自动触发渲染,无需手动调用。
|
|
319
321
|
|
|
320
322
|
```tsx
|
|
321
|
-
const $ = ctx.ui.$
|
|
323
|
+
const $ = ctx.ui.$
|
|
324
|
+
|
|
325
|
+
// 顶层属性赋值
|
|
326
|
+
$.count = 0 // → 自动渲染
|
|
327
|
+
$.user = { name: 'alice' } // → 新值自动深度包装
|
|
322
328
|
|
|
323
|
-
|
|
324
|
-
$.items
|
|
325
|
-
$.items.
|
|
329
|
+
// 数组突变
|
|
330
|
+
$.items.push(newItem) // → 自动渲染
|
|
331
|
+
$.items.pop() // → 自动渲染
|
|
332
|
+
$.items.splice(i, 1) // → 自动渲染
|
|
326
333
|
|
|
327
|
-
//
|
|
328
|
-
|
|
329
|
-
|
|
334
|
+
// 对象属性突变(数组内部也支持)
|
|
335
|
+
$.items[0].done = true // → 自动渲染
|
|
336
|
+
$.msgs[idx].content += event.text // → 自动渲染
|
|
337
|
+
|
|
338
|
+
// 不可变更新(同样支持)
|
|
339
|
+
$.items = [...$.items, newItem]
|
|
330
340
|
```
|
|
331
341
|
|
|
332
342
|
| API | 说明 |
|
|
333
343
|
|------|------|
|
|
334
|
-
| `ctx.ui.$` |
|
|
335
|
-
| `ctx.ui.dirty()` |
|
|
336
|
-
| `ctx.ui.render()` | 同步立即渲染 |
|
|
337
|
-
|
|
338
|
-
**原理**:`ctx.ui.$` 是 Proxy 对象,任何属性赋值(`$.x = val`)自动调用 `queueMicrotask(render)`。\
|
|
339
|
-
多次赋值在同一个微任务内合并为一次渲染。
|
|
344
|
+
| `ctx.ui.$` | 深度 Proxy 对象,所有写入自动触发渲染 |
|
|
345
|
+
| `ctx.ui.dirty()` | ⚠ 极少需要 — 仅当绕过 Proxy 直接操作底层对象时 |
|
|
340
346
|
|
|
341
347
|
### JSX 运行时
|
|
342
348
|
|
package/dist/client/app.d.ts
CHANGED
|
@@ -7,6 +7,13 @@
|
|
|
7
7
|
* ctx.ui.render() 触发组件重渲染
|
|
8
8
|
* ctx.ui.$ 持久化组件状态(由渲染器管理)
|
|
9
9
|
* ctx.ui.ready 首次执行标记(由渲染器管理)
|
|
10
|
+
*
|
|
11
|
+
* ctx.ui.$ 是深度 Proxy:
|
|
12
|
+
* $.x = val → 自动渲染(顶层属性赋值)
|
|
13
|
+
* $.items.push(val) → 自动渲染(数组突变方法)
|
|
14
|
+
* $.items[0].x = val → 自动渲染(对象属性突变)
|
|
15
|
+
* $.items.push({x: 1}) → 新对象自动深度包装
|
|
16
|
+
* ctx.ui.dirty() → 极少需要(仅当绕过 Proxy 直接操作底层对象)
|
|
10
17
|
*/
|
|
11
18
|
import type { WfuiContext, AppMiddleware } from './types.ts';
|
|
12
19
|
import type { Component } from './vnode.ts';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ErrorBoundary — 错误边界组件
|
|
3
|
+
*
|
|
4
|
+
* 捕获子组件渲染时的错误,展示 fallback 替代崩溃白屏。
|
|
5
|
+
*
|
|
6
|
+
* ```tsx
|
|
7
|
+
* <ErrorBoundary fallback={<p>出错了</p>}>
|
|
8
|
+
* <UserProfile />
|
|
9
|
+
* </ErrorBoundary>
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* 基于 ctx.ui._errorHandler 机制:在渲染子组件前注入错误处理器,
|
|
13
|
+
* 子组件 render 抛错时设置 $.error → 重新渲染 fallback。
|
|
14
|
+
*/
|
|
15
|
+
import type { WfuiContext } from './types.ts';
|
|
16
|
+
import type { VNode } from './vnode.ts';
|
|
17
|
+
export interface ErrorBoundaryProps {
|
|
18
|
+
fallback?: VNode | ((props: {
|
|
19
|
+
error: unknown;
|
|
20
|
+
}) => VNode) | null;
|
|
21
|
+
children?: any;
|
|
22
|
+
}
|
|
23
|
+
export declare function ErrorBoundary(props: ErrorBoundaryProps, ctx: WfuiContext): VNode | null;
|
package/dist/client/index.d.ts
CHANGED
|
@@ -27,3 +27,5 @@ export { ApiError } from './middleware/api.ts';
|
|
|
27
27
|
export type { AuthClient, AuthOptions } from './middleware/auth.ts';
|
|
28
28
|
export { extendCtx } from './types.ts';
|
|
29
29
|
export type { WfuiContext, AppMiddleware, RouteDef } from './types.ts';
|
|
30
|
+
export { ErrorBoundary } from './error-boundary.ts';
|
|
31
|
+
export type { ErrorBoundaryProps } from './error-boundary.ts';
|
package/dist/client/index.js
CHANGED
|
@@ -59,7 +59,19 @@ function renderComponent(Comp, props, vnode, ctx) {
|
|
|
59
59
|
if (!prev$) vnode._$ = {};
|
|
60
60
|
ctx.ui = ctx.ui ?? {};
|
|
61
61
|
ctx.ui.ready = !!prev$;
|
|
62
|
-
|
|
62
|
+
let childVNode;
|
|
63
|
+
try {
|
|
64
|
+
childVNode = Comp(props, ctx);
|
|
65
|
+
} catch (e) {
|
|
66
|
+
const errHandler = ctx.ui?._errorHandler;
|
|
67
|
+
if (errHandler) {
|
|
68
|
+
errHandler(e);
|
|
69
|
+
childVNode = null;
|
|
70
|
+
} else {
|
|
71
|
+
console.error("Component render error:", e);
|
|
72
|
+
childVNode = null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
63
75
|
if (childVNode == null) {
|
|
64
76
|
vnode._child = null;
|
|
65
77
|
return document.createTextNode("");
|
|
@@ -150,11 +162,9 @@ function patchValue(parent, oldNode, oldInput, newInput, ctx) {
|
|
|
150
162
|
if (oldV._$) newV._$ = oldV._$;
|
|
151
163
|
ctx.ui = ctx.ui ?? {};
|
|
152
164
|
ctx.ui.ready = !!newV._$;
|
|
153
|
-
const childOld = oldV._child != null ? oldV._child : comp(oldV.props, ctx);
|
|
154
|
-
ctx.ui.ready = !!newV._$;
|
|
155
165
|
const childNew = comp(newV.props, ctx);
|
|
156
166
|
newV._child = childNew;
|
|
157
|
-
return patchValue(parent, oldNode,
|
|
167
|
+
return patchValue(parent, oldNode, oldV._child, childNew, ctx);
|
|
158
168
|
}
|
|
159
169
|
if (newV.type === Fragment) {
|
|
160
170
|
patchChildren(parent, oldV, newV, ctx);
|
|
@@ -330,88 +340,6 @@ function callRef(input, el) {
|
|
|
330
340
|
forEach(vnode.props?.children, (child) => callRef(child, el));
|
|
331
341
|
}
|
|
332
342
|
|
|
333
|
-
// src/client/app.ts
|
|
334
|
-
function createApp() {
|
|
335
|
-
const middlewares = [];
|
|
336
|
-
let ctx = {};
|
|
337
|
-
let container = null;
|
|
338
|
-
let rootComponent = null;
|
|
339
|
-
let oldVNode = null;
|
|
340
|
-
let rendered = false;
|
|
341
|
-
const app = {
|
|
342
|
-
get ctx() {
|
|
343
|
-
return ctx;
|
|
344
|
-
},
|
|
345
|
-
use(mw) {
|
|
346
|
-
middlewares.push(mw);
|
|
347
|
-
return app;
|
|
348
|
-
},
|
|
349
|
-
async mount(rootSelector, RootComponent) {
|
|
350
|
-
rootComponent = RootComponent;
|
|
351
|
-
for (const mw of middlewares) {
|
|
352
|
-
ctx = await mw(ctx);
|
|
353
|
-
}
|
|
354
|
-
const el = typeof rootSelector === "string" ? document.querySelector(rootSelector) : rootSelector;
|
|
355
|
-
if (!el) throw new Error(`mount target not found: ${rootSelector}`);
|
|
356
|
-
container = el;
|
|
357
|
-
container.innerHTML = "";
|
|
358
|
-
let _dirty = false;
|
|
359
|
-
const $target = {};
|
|
360
|
-
ctx.ui = {
|
|
361
|
-
render: () => {
|
|
362
|
-
if (!container || !rootComponent || !oldVNode) return;
|
|
363
|
-
_dirty = false;
|
|
364
|
-
ctx._rvDepth = 0;
|
|
365
|
-
const newVNode = wrapComponent(rootComponent, ctx);
|
|
366
|
-
const oldNode = container.firstChild;
|
|
367
|
-
if (oldNode) {
|
|
368
|
-
patchValue(container, oldNode, oldVNode, newVNode, ctx);
|
|
369
|
-
}
|
|
370
|
-
oldVNode = newVNode;
|
|
371
|
-
},
|
|
372
|
-
/** 显式标记脏(用于嵌套突变如 push、arr[i].x +=) */
|
|
373
|
-
dirty: () => {
|
|
374
|
-
if (_dirty) return;
|
|
375
|
-
_dirty = true;
|
|
376
|
-
queueMicrotask(() => {
|
|
377
|
-
if (!_dirty) return;
|
|
378
|
-
_dirty = false;
|
|
379
|
-
ctx.ui.render();
|
|
380
|
-
});
|
|
381
|
-
},
|
|
382
|
-
$: new Proxy($target, {
|
|
383
|
-
set(_target, key, val) {
|
|
384
|
-
_target[key] = val;
|
|
385
|
-
if (!_dirty) {
|
|
386
|
-
_dirty = true;
|
|
387
|
-
queueMicrotask(() => {
|
|
388
|
-
if (!_dirty) return;
|
|
389
|
-
_dirty = false;
|
|
390
|
-
ctx.ui.render();
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
return true;
|
|
394
|
-
}
|
|
395
|
-
}),
|
|
396
|
-
ready: false
|
|
397
|
-
};
|
|
398
|
-
oldVNode = wrapComponent(RootComponent, ctx);
|
|
399
|
-
const node = render(oldVNode, ctx);
|
|
400
|
-
if (node instanceof Node) container.appendChild(node);
|
|
401
|
-
rendered = true;
|
|
402
|
-
},
|
|
403
|
-
destroy() {
|
|
404
|
-
if (container) container.innerHTML = "";
|
|
405
|
-
container = null;
|
|
406
|
-
ctx = {};
|
|
407
|
-
}
|
|
408
|
-
};
|
|
409
|
-
return app;
|
|
410
|
-
}
|
|
411
|
-
function wrapComponent(Comp, _ctx) {
|
|
412
|
-
return { type: Comp, props: {}, key: void 0 };
|
|
413
|
-
}
|
|
414
|
-
|
|
415
343
|
// src/client/router.ts
|
|
416
344
|
function flattenRoutes(routes, basePath = "", chain = []) {
|
|
417
345
|
const result = [];
|
|
@@ -426,6 +354,7 @@ function flattenRoutes(routes, basePath = "", chain = []) {
|
|
|
426
354
|
}
|
|
427
355
|
return result;
|
|
428
356
|
}
|
|
357
|
+
var layoutDepth = /* @__PURE__ */ new WeakMap();
|
|
429
358
|
function joinPaths(a, b) {
|
|
430
359
|
if (!b || b === "/") return a || "/";
|
|
431
360
|
const left = a.endsWith("/") ? a.slice(0, -1) : a;
|
|
@@ -451,6 +380,14 @@ function matchRoute(path, routes) {
|
|
|
451
380
|
}
|
|
452
381
|
function router(opts) {
|
|
453
382
|
const flatRoutes = flattenRoutes(opts.routes);
|
|
383
|
+
const mode = opts.mode || "history";
|
|
384
|
+
function getPath() {
|
|
385
|
+
if (mode === "hash") {
|
|
386
|
+
const hash = window.location.hash.replace(/^#/, "") || "/";
|
|
387
|
+
return hash;
|
|
388
|
+
}
|
|
389
|
+
return window.location.pathname;
|
|
390
|
+
}
|
|
454
391
|
function resolve(path) {
|
|
455
392
|
const match = matchRoute(path, flatRoutes);
|
|
456
393
|
if (match) {
|
|
@@ -471,20 +408,30 @@ function router(opts) {
|
|
|
471
408
|
return { path, params: {}, query: {}, chain: [] };
|
|
472
409
|
}
|
|
473
410
|
return (ctx) => {
|
|
474
|
-
const resolved = resolve(
|
|
411
|
+
const resolved = resolve(getPath());
|
|
475
412
|
ctx.route = resolved;
|
|
476
413
|
if (!ctx.app) ctx.app = {};
|
|
477
414
|
ctx.app.navigate = (path) => {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
415
|
+
if (mode === "hash") {
|
|
416
|
+
window.location.hash = "#" + path;
|
|
417
|
+
const resolved2 = resolve(path);
|
|
418
|
+
ctx.route = resolved2;
|
|
419
|
+
} else {
|
|
420
|
+
window.history.pushState({}, "", path);
|
|
421
|
+
const resolved2 = resolve(path);
|
|
422
|
+
ctx.route = resolved2;
|
|
423
|
+
}
|
|
481
424
|
ctx.ui?.render();
|
|
482
425
|
};
|
|
483
|
-
|
|
484
|
-
const resolved2 = resolve(
|
|
426
|
+
const onPop = () => {
|
|
427
|
+
const resolved2 = resolve(getPath());
|
|
485
428
|
ctx.route = resolved2;
|
|
486
429
|
ctx.ui?.render();
|
|
487
|
-
}
|
|
430
|
+
};
|
|
431
|
+
window.addEventListener("popstate", onPop);
|
|
432
|
+
if (mode === "hash") {
|
|
433
|
+
window.addEventListener("hashchange", onPop);
|
|
434
|
+
}
|
|
488
435
|
return ctx;
|
|
489
436
|
};
|
|
490
437
|
}
|
|
@@ -492,12 +439,135 @@ function RouteView(_props, ctx) {
|
|
|
492
439
|
const route = ctx.route;
|
|
493
440
|
if (!route?.chain?.length) return null;
|
|
494
441
|
const ctxAny = ctx;
|
|
495
|
-
const depth = ctxAny
|
|
442
|
+
const depth = layoutDepth.get(ctxAny) ?? 0;
|
|
496
443
|
if (depth >= route.chain.length) return null;
|
|
497
444
|
const def = route.chain[depth];
|
|
498
445
|
const Comp = def.layout ?? def.component;
|
|
499
446
|
if (!Comp) return null;
|
|
500
|
-
if (def.layout)
|
|
447
|
+
if (def.layout) {
|
|
448
|
+
layoutDepth.set(ctxAny, depth + 1);
|
|
449
|
+
}
|
|
450
|
+
return { type: Comp, props: {}, key: void 0 };
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// src/client/app.ts
|
|
454
|
+
var mutationMethods = ["push", "pop", "splice", "shift", "unshift", "sort", "reverse"];
|
|
455
|
+
var wrappedCache = /* @__PURE__ */ new WeakMap();
|
|
456
|
+
function wrapDeep(val, dirty) {
|
|
457
|
+
if (val === null || typeof val !== "object") return val;
|
|
458
|
+
if (val instanceof Node) return val;
|
|
459
|
+
if (wrappedCache.has(val)) return wrappedCache.get(val);
|
|
460
|
+
let proxy;
|
|
461
|
+
if (Array.isArray(val)) {
|
|
462
|
+
proxy = new Proxy(val, {
|
|
463
|
+
get(target, key, receiver) {
|
|
464
|
+
const v = Reflect.get(target, key, receiver);
|
|
465
|
+
if (typeof key === "string" && mutationMethods.includes(key) && typeof v === "function") {
|
|
466
|
+
return function(...args) {
|
|
467
|
+
const r = v.apply(target, args);
|
|
468
|
+
dirty();
|
|
469
|
+
return r;
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
return wrapDeep(v, dirty);
|
|
473
|
+
},
|
|
474
|
+
set(target, key, val2) {
|
|
475
|
+
target[key] = wrapDeep(val2, dirty);
|
|
476
|
+
dirty();
|
|
477
|
+
return true;
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
} else {
|
|
481
|
+
proxy = new Proxy(val, {
|
|
482
|
+
get(_target, key, receiver) {
|
|
483
|
+
const v = Reflect.get(_target, key, receiver);
|
|
484
|
+
return wrapDeep(v, dirty);
|
|
485
|
+
},
|
|
486
|
+
set(target, key, val2) {
|
|
487
|
+
target[key] = wrapDeep(val2, dirty);
|
|
488
|
+
dirty();
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
wrappedCache.set(val, proxy);
|
|
494
|
+
return proxy;
|
|
495
|
+
}
|
|
496
|
+
function createApp() {
|
|
497
|
+
const middlewares = [];
|
|
498
|
+
let ctx = {};
|
|
499
|
+
let container = null;
|
|
500
|
+
let rootComponent = null;
|
|
501
|
+
let oldVNode = null;
|
|
502
|
+
let rendered = false;
|
|
503
|
+
const app = {
|
|
504
|
+
get ctx() {
|
|
505
|
+
return ctx;
|
|
506
|
+
},
|
|
507
|
+
use(mw) {
|
|
508
|
+
middlewares.push(mw);
|
|
509
|
+
return app;
|
|
510
|
+
},
|
|
511
|
+
async mount(rootSelector, RootComponent) {
|
|
512
|
+
rootComponent = RootComponent;
|
|
513
|
+
for (const mw of middlewares) {
|
|
514
|
+
ctx = await mw(ctx);
|
|
515
|
+
}
|
|
516
|
+
const el = typeof rootSelector === "string" ? document.querySelector(rootSelector) : rootSelector;
|
|
517
|
+
if (!el) throw new Error(`mount target not found: ${rootSelector}`);
|
|
518
|
+
container = el;
|
|
519
|
+
container.innerHTML = "";
|
|
520
|
+
let _dirty = false;
|
|
521
|
+
function scheduleRender() {
|
|
522
|
+
if (_dirty) return;
|
|
523
|
+
_dirty = true;
|
|
524
|
+
queueMicrotask(() => {
|
|
525
|
+
if (!_dirty) return;
|
|
526
|
+
_dirty = false;
|
|
527
|
+
ctx.ui.render();
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
const $target = {};
|
|
531
|
+
ctx.ui = {
|
|
532
|
+
render: () => {
|
|
533
|
+
if (!container || !rootComponent || !oldVNode) return;
|
|
534
|
+
_dirty = false;
|
|
535
|
+
layoutDepth.delete(ctx);
|
|
536
|
+
const newVNode = wrapComponent(rootComponent, ctx);
|
|
537
|
+
const oldNode = container.firstChild;
|
|
538
|
+
if (oldNode) {
|
|
539
|
+
patchValue(container, oldNode, oldVNode, newVNode, ctx);
|
|
540
|
+
}
|
|
541
|
+
oldVNode = newVNode;
|
|
542
|
+
},
|
|
543
|
+
/** 极少需要:仅当绕过 Proxy 直接操作底层对象时使用 */
|
|
544
|
+
dirty: () => {
|
|
545
|
+
scheduleRender();
|
|
546
|
+
},
|
|
547
|
+
$: new Proxy($target, {
|
|
548
|
+
set(_target, key, val) {
|
|
549
|
+
_target[key] = wrapDeep(val, scheduleRender);
|
|
550
|
+
scheduleRender();
|
|
551
|
+
return true;
|
|
552
|
+
}
|
|
553
|
+
}),
|
|
554
|
+
ready: false
|
|
555
|
+
};
|
|
556
|
+
oldVNode = wrapComponent(RootComponent, ctx);
|
|
557
|
+
const node = render(oldVNode, ctx);
|
|
558
|
+
if (node instanceof Node) container.appendChild(node);
|
|
559
|
+
_dirty = false;
|
|
560
|
+
rendered = true;
|
|
561
|
+
},
|
|
562
|
+
destroy() {
|
|
563
|
+
if (container) container.innerHTML = "";
|
|
564
|
+
container = null;
|
|
565
|
+
ctx = {};
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
return app;
|
|
569
|
+
}
|
|
570
|
+
function wrapComponent(Comp, _ctx) {
|
|
501
571
|
return { type: Comp, props: {}, key: void 0 };
|
|
502
572
|
}
|
|
503
573
|
|
|
@@ -761,8 +831,38 @@ function auth(options) {
|
|
|
761
831
|
return extendCtx(ctx, { auth: authClient });
|
|
762
832
|
};
|
|
763
833
|
}
|
|
834
|
+
|
|
835
|
+
// src/client/error-boundary.ts
|
|
836
|
+
function ErrorBoundary(props, ctx) {
|
|
837
|
+
const $ = ctx.ui.$;
|
|
838
|
+
if (!("error" in $)) $.error = null;
|
|
839
|
+
if ($.error) {
|
|
840
|
+
const fb = props.fallback;
|
|
841
|
+
if (typeof fb === "function") {
|
|
842
|
+
return fb({ error: $.error });
|
|
843
|
+
}
|
|
844
|
+
return fb ?? null;
|
|
845
|
+
}
|
|
846
|
+
;
|
|
847
|
+
ctx.ui._errorHandler = (err) => {
|
|
848
|
+
$.error = err;
|
|
849
|
+
ctx.ui.dirty();
|
|
850
|
+
};
|
|
851
|
+
try {
|
|
852
|
+
const result = props.children;
|
|
853
|
+
return result ?? null;
|
|
854
|
+
} catch (e) {
|
|
855
|
+
$.error = e;
|
|
856
|
+
const fb = props.fallback;
|
|
857
|
+
if (typeof fb === "function") {
|
|
858
|
+
return fb({ error: e });
|
|
859
|
+
}
|
|
860
|
+
return fb ?? null;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
764
863
|
export {
|
|
765
864
|
ApiError,
|
|
865
|
+
ErrorBoundary,
|
|
766
866
|
Fragment,
|
|
767
867
|
RouteView,
|
|
768
868
|
api,
|
package/dist/client/router.d.ts
CHANGED
|
@@ -9,5 +9,6 @@ export interface RouterOptions {
|
|
|
9
9
|
routes: RouteDef[];
|
|
10
10
|
notFound?: (props: any, ctx: WfuiContext) => any;
|
|
11
11
|
}
|
|
12
|
+
export declare const layoutDepth: WeakMap<any, number>;
|
|
12
13
|
export declare function router(opts: RouterOptions): AppMiddleware;
|
|
13
14
|
export declare function RouteView(_props: {}, ctx: WfuiContext): any;
|