@lytjs/renderer 6.5.0 → 6.6.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 +385 -385
- package/dist/index.cjs +5 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +5 -20
- package/dist/index.mjs.map +1 -1
- package/dist/ssr.cjs.map +1 -1
- package/dist/ssr.mjs.map +1 -1
- package/dist/vapor/vapor-app.cjs.map +1 -1
- package/dist/vapor/vapor-app.mjs.map +1 -1
- package/package.json +68 -68
package/README.md
CHANGED
|
@@ -1,385 +1,385 @@
|
|
|
1
|
-
# @lytjs/renderer
|
|
2
|
-
|
|
3
|
-
> LytJS 渲染后端,提供 DOM 渲染、SSR 渲染、Signal 渲染、Hydration 和 Island Architecture 支持
|
|
4
|
-
|
|
5
|
-
## 安装
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @lytjs/renderer
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## 渲染器类型
|
|
12
|
-
|
|
13
|
-
### DOM 渲染器
|
|
14
|
-
|
|
15
|
-
用于浏览器端的 DOM 渲染
|
|
16
|
-
|
|
17
|
-
```typescript
|
|
18
|
-
import { createDOMRenderer } from '@lytjs/renderer';
|
|
19
|
-
|
|
20
|
-
const renderer = createDOMRenderer();
|
|
21
|
-
renderer.render(vnode, document.getElementById('app'));
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
### Signal 渲染器
|
|
25
|
-
|
|
26
|
-
用于细粒度响应式更新(Vapor 模式)
|
|
27
|
-
|
|
28
|
-
```typescript
|
|
29
|
-
import { createSignalRenderer, createVaporRenderer } from '@lytjs/renderer';
|
|
30
|
-
|
|
31
|
-
// Signal 渲染器
|
|
32
|
-
const signalRenderer = await createSignalRenderer();
|
|
33
|
-
|
|
34
|
-
// Vapor 渲染器(Signal 渲染器的别名)
|
|
35
|
-
const vaporRenderer = await createVaporRenderer();
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### SSR 渲染器
|
|
39
|
-
|
|
40
|
-
用于服务端渲染
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
import { renderToString, renderToStream } from '@lytjs/renderer';
|
|
44
|
-
|
|
45
|
-
// 渲染为字符串
|
|
46
|
-
const html = await renderToString(vnode);
|
|
47
|
-
|
|
48
|
-
// 流式渲染
|
|
49
|
-
const stream = await renderToStream(vnode, {
|
|
50
|
-
onChunk: (chunk) => res.write(chunk),
|
|
51
|
-
});
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## 核心 API
|
|
55
|
-
|
|
56
|
-
### createRenderer
|
|
57
|
-
|
|
58
|
-
创建自定义渲染器实例
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
import { createRenderer, type RendererOptions } from '@lytjs/renderer';
|
|
62
|
-
|
|
63
|
-
const options: RendererOptions = {
|
|
64
|
-
createElement: (tag) => document.createElement(tag),
|
|
65
|
-
insert: (el, parent, anchor) => parent.insertBefore(el, anchor),
|
|
66
|
-
// ...
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const renderer = createRenderer(options);
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### createDOMRenderer
|
|
73
|
-
|
|
74
|
-
创建 DOM 渲染器实例
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
import { createDOMRenderer } from '@lytjs/renderer';
|
|
78
|
-
|
|
79
|
-
const renderer = createDOMRenderer();
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### patchProp / patchClass / patchStyle / patchEvent / patchAttr
|
|
83
|
-
|
|
84
|
-
DOM 属性更新工具函数
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
import {
|
|
88
|
-
patchProp,
|
|
89
|
-
patchClass,
|
|
90
|
-
patchStyle,
|
|
91
|
-
patchEvent,
|
|
92
|
-
patchAttr,
|
|
93
|
-
normalizeEventName,
|
|
94
|
-
getEventKey,
|
|
95
|
-
parseEventModifier,
|
|
96
|
-
createInvoker,
|
|
97
|
-
} from '@lytjs/renderer';
|
|
98
|
-
|
|
99
|
-
// 更新属性
|
|
100
|
-
patchProp(el, 'class', prevValue, nextValue);
|
|
101
|
-
|
|
102
|
-
// 更新 class
|
|
103
|
-
patchClass(el, 'active visible');
|
|
104
|
-
|
|
105
|
-
// 更新 style
|
|
106
|
-
patchStyle(el, { color: 'red', fontSize: '14px' });
|
|
107
|
-
|
|
108
|
-
// 更新事件
|
|
109
|
-
patchEvent(el, 'onClick', prevHandler, nextHandler);
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## Hydration
|
|
113
|
-
|
|
114
|
-
### createHydrationFunctions
|
|
115
|
-
|
|
116
|
-
创建服务端渲染 Hydration 函数
|
|
117
|
-
|
|
118
|
-
```typescript
|
|
119
|
-
import { createHydrationFunctions } from '@lytjs/renderer';
|
|
120
|
-
|
|
121
|
-
const { hydrate, hydrateNode } = createHydrationFunctions(options);
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
## Island Architecture
|
|
125
|
-
|
|
126
|
-
Island Architecture 用于在 SSR 页面中实现部分区域的客户端交互
|
|
127
|
-
|
|
128
|
-
### hydrateIsland
|
|
129
|
-
|
|
130
|
-
水合单个 Island 组件
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
import { hydrateIsland, registerIslandComponent } from '@lytjs/renderer';
|
|
134
|
-
|
|
135
|
-
// 注册 Island 组件
|
|
136
|
-
await registerIslandComponent('Counter', CounterComponent);
|
|
137
|
-
|
|
138
|
-
// 水合 Island
|
|
139
|
-
const islandEl = document.querySelector('[data-island="Counter"]');
|
|
140
|
-
await hydrateIsland(islandEl, CounterComponent, { initialCount: 0 });
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### registerIslandComponent
|
|
144
|
-
|
|
145
|
-
注册 Island 组件供后续水合使用
|
|
146
|
-
|
|
147
|
-
```typescript
|
|
148
|
-
import { registerIslandComponent } from '@lytjs/renderer';
|
|
149
|
-
|
|
150
|
-
await registerIslandComponent('MyComponent', MyComponent);
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
### createIslandSSRContent
|
|
154
|
-
|
|
155
|
-
创建 Island SSR 内容
|
|
156
|
-
|
|
157
|
-
```typescript
|
|
158
|
-
import { createIslandSSRContent } from '@lytjs/renderer';
|
|
159
|
-
|
|
160
|
-
const html = await createIslandSSRContent(vnode, {
|
|
161
|
-
islandId: 'Counter',
|
|
162
|
-
});
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
## 懒加载
|
|
166
|
-
|
|
167
|
-
渲染器支持懒加载大型模块,减少初始包体积:
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
// SSR 相关函数使用动态导入
|
|
171
|
-
import { renderToString, renderToStream } from '@lytjs/renderer';
|
|
172
|
-
|
|
173
|
-
// 这些函数会按需加载 SSR 模块
|
|
174
|
-
const html = await renderToString(vnode);
|
|
175
|
-
|
|
176
|
-
// Signal/Vapor 渲染器也是懒加载的
|
|
177
|
-
const signalRenderer = await createSignalRenderer();
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
## 渲染器插件系统
|
|
181
|
-
|
|
182
|
-
### use
|
|
183
|
-
|
|
184
|
-
安装渲染器插件
|
|
185
|
-
|
|
186
|
-
```typescript
|
|
187
|
-
import { use, type RendererPlugin } from '@lytjs/renderer';
|
|
188
|
-
|
|
189
|
-
const myPlugin: RendererPlugin = {
|
|
190
|
-
name: 'MyPlugin',
|
|
191
|
-
install(context) {
|
|
192
|
-
context.on('beforeMount', (vnode) => {
|
|
193
|
-
console.log('Before mount:', vnode);
|
|
194
|
-
});
|
|
195
|
-
},
|
|
196
|
-
beforeMount(vnode) {
|
|
197
|
-
console.log('Will mount:', vnode);
|
|
198
|
-
},
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
use(myPlugin);
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### 插件生命周期钩子
|
|
205
|
-
|
|
206
|
-
```typescript
|
|
207
|
-
interface RendererPlugin {
|
|
208
|
-
name: string;
|
|
209
|
-
install: (context: PluginContext) => void;
|
|
210
|
-
beforeMount?: (vnode: VNode) => void;
|
|
211
|
-
afterMount?: (vnode: VNode, container: unknown) => void;
|
|
212
|
-
beforePatch?: (oldVNode: VNode, newVNode: VNode) => void;
|
|
213
|
-
afterPatch?: (vnode: VNode) => void;
|
|
214
|
-
beforeUnmount?: (vnode: VNode) => void;
|
|
215
|
-
afterUnmount?: (vnode: VNode) => void;
|
|
216
|
-
}
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
### 插件管理
|
|
220
|
-
|
|
221
|
-
```typescript
|
|
222
|
-
import { use, getInstalledPlugins, isPluginInstalled, removePlugin } from '@lytjs/renderer';
|
|
223
|
-
|
|
224
|
-
// 安装插件
|
|
225
|
-
use(myPlugin);
|
|
226
|
-
|
|
227
|
-
// 检查插件是否已安装
|
|
228
|
-
if (isPluginInstalled('MyPlugin')) {
|
|
229
|
-
console.log('Plugin is installed');
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// 获取所有已安装插件
|
|
233
|
-
const plugins = getInstalledPlugins();
|
|
234
|
-
|
|
235
|
-
// 移除插件
|
|
236
|
-
removePlugin('MyPlugin');
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
## Vapor 模式
|
|
240
|
-
|
|
241
|
-
Vapor 模式提供无虚拟 DOM 的细粒度响应式渲染
|
|
242
|
-
|
|
243
|
-
### createVaporApp
|
|
244
|
-
|
|
245
|
-
创建 Vapor 应用
|
|
246
|
-
|
|
247
|
-
```typescript
|
|
248
|
-
import { createVaporApp, defineVaporComponent } from '@lytjs/renderer';
|
|
249
|
-
|
|
250
|
-
const MyComponent = await defineVaporComponent({
|
|
251
|
-
setup() {
|
|
252
|
-
const count = signal(0);
|
|
253
|
-
return () => html`<div>${count()}</div>`;
|
|
254
|
-
},
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
const app = await createVaporApp(MyComponent);
|
|
258
|
-
app.mount('#app');
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
### defineVaporComponent
|
|
262
|
-
|
|
263
|
-
定义 Vapor 组件
|
|
264
|
-
|
|
265
|
-
```typescript
|
|
266
|
-
import { defineVaporComponent } from '@lytjs/renderer';
|
|
267
|
-
|
|
268
|
-
const Counter = await defineVaporComponent({
|
|
269
|
-
props: {
|
|
270
|
-
initialCount: { type: Number, default: 0 },
|
|
271
|
-
},
|
|
272
|
-
setup(props) {
|
|
273
|
-
// 组件逻辑
|
|
274
|
-
},
|
|
275
|
-
});
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
## 组件资源清理
|
|
279
|
-
|
|
280
|
-
```typescript
|
|
281
|
-
import {
|
|
282
|
-
registerComponentEventListener,
|
|
283
|
-
registerComponentEffectSubscription,
|
|
284
|
-
registerComponentCleanup,
|
|
285
|
-
cleanupComponentResources,
|
|
286
|
-
} from '@lytjs/renderer';
|
|
287
|
-
|
|
288
|
-
// 注册事件监听器(卸载时自动清理)
|
|
289
|
-
registerComponentEventListener(instance, target, 'click', handler);
|
|
290
|
-
|
|
291
|
-
// 注册 effect 订阅(卸载时自动清理)
|
|
292
|
-
registerComponentEffectSubscription(instance, subscription);
|
|
293
|
-
|
|
294
|
-
// 注册自定义清理函数
|
|
295
|
-
registerComponentCleanup(instance, () => {
|
|
296
|
-
// 清理逻辑
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
// 手动清理所有资源
|
|
300
|
-
cleanupComponentResources(instance);
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
## 首次渲染优化
|
|
304
|
-
|
|
305
|
-
```typescript
|
|
306
|
-
import {
|
|
307
|
-
withFirstRenderOptimization,
|
|
308
|
-
shouldSkipTracking,
|
|
309
|
-
getSkippedTrackingCount,
|
|
310
|
-
resetSkippedTrackingCount,
|
|
311
|
-
} from '@lytjs/renderer';
|
|
312
|
-
|
|
313
|
-
// 使用首次渲染优化
|
|
314
|
-
withFirstRenderOptimization(() => {
|
|
315
|
-
// 首次渲染时跳过不必要的追踪
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
// 检查是否跳过追踪
|
|
319
|
-
if (shouldSkipTracking()) {
|
|
320
|
-
console.log('Tracking is skipped');
|
|
321
|
-
}
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
## 工具函数
|
|
325
|
-
|
|
326
|
-
```typescript
|
|
327
|
-
import { escapeHtml, isBooleanAttr, isVoidElement } from '@lytjs/renderer';
|
|
328
|
-
|
|
329
|
-
// HTML 转义
|
|
330
|
-
const safe = escapeHtml('<script>alert("xss")</script>');
|
|
331
|
-
|
|
332
|
-
// 检查布尔属性
|
|
333
|
-
if (isBooleanAttr('disabled')) {
|
|
334
|
-
// 处理布尔属性
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// 检查自闭合标签
|
|
338
|
-
if (isVoidElement('img')) {
|
|
339
|
-
// 自闭合标签处理
|
|
340
|
-
}
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
## 类型定义
|
|
344
|
-
|
|
345
|
-
```typescript
|
|
346
|
-
import type {
|
|
347
|
-
VNode,
|
|
348
|
-
RendererOptions,
|
|
349
|
-
RendererPlugin,
|
|
350
|
-
PluginContext,
|
|
351
|
-
LifecycleEvent,
|
|
352
|
-
DOMRenderer,
|
|
353
|
-
SignalRenderer,
|
|
354
|
-
VaporRenderer,
|
|
355
|
-
VaporApp,
|
|
356
|
-
VaporComponentOptions,
|
|
357
|
-
HydrationRenderer,
|
|
358
|
-
RendererHost,
|
|
359
|
-
HostRect,
|
|
360
|
-
HostStyleDeclaration,
|
|
361
|
-
HostEvent,
|
|
362
|
-
HostEventHandler,
|
|
363
|
-
} from '@lytjs/renderer';
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
## 相关包
|
|
367
|
-
|
|
368
|
-
- [@lytjs/vdom](../vdom) - 虚拟 DOM 实现,渲染器的基础
|
|
369
|
-
- [@lytjs/core](../core) - 框架核心入口,整合所有子包
|
|
370
|
-
- [@lytjs/adapter-web](../adapter-web) - Web 平台适配器
|
|
371
|
-
- [@lytjs/host-contract](../host-contract) - 渲染器宿主抽象
|
|
372
|
-
|
|
373
|
-
## 依赖版本
|
|
374
|
-
|
|
375
|
-
- [@lytjs/reactivity](https://www.npmjs.com/package/@lytjs/reactivity): ^6.4.0
|
|
376
|
-
- [@lytjs/vdom](https://www.npmjs.com/package/@lytjs/vdom): ^6.4.0
|
|
377
|
-
- [@lytjs/common-is](https://www.npmjs.com/package/@lytjs/common-is): ^6.4.0
|
|
378
|
-
- [@lytjs/common-string](https://www.npmjs.com/package/@lytjs/common-string): ^6.4.0
|
|
379
|
-
- [@lytjs/common-events](https://www.npmjs.com/package/@lytjs/common-events): ^6.4.0
|
|
380
|
-
- [@lytjs/common-dom](https://www.npmjs.com/package/@lytjs/common-dom): ^6.4.0
|
|
381
|
-
- [@lytjs/common-error](https://www.npmjs.com/package/@lytjs/common-error): ^6.4.0
|
|
382
|
-
- [@lytjs/dom-runtime](https://www.npmjs.com/package/@lytjs/dom-runtime): ^6.4.0
|
|
383
|
-
- [@lytjs/compiler](https://www.npmjs.com/package/@lytjs/compiler): ^6.4.0
|
|
384
|
-
- [@lytjs/host-contract](https://www.npmjs.com/package/@lytjs/host-contract): ^6.4.0
|
|
385
|
-
- [@lytjs/adapter-web](https://www.npmjs.com/package/@lytjs/adapter-web): ^6.4.0
|
|
1
|
+
# @lytjs/renderer
|
|
2
|
+
|
|
3
|
+
> LytJS 渲染后端,提供 DOM 渲染、SSR 渲染、Signal 渲染、Hydration 和 Island Architecture 支持
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @lytjs/renderer
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 渲染器类型
|
|
12
|
+
|
|
13
|
+
### DOM 渲染器
|
|
14
|
+
|
|
15
|
+
用于浏览器端的 DOM 渲染
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { createDOMRenderer } from '@lytjs/renderer';
|
|
19
|
+
|
|
20
|
+
const renderer = createDOMRenderer();
|
|
21
|
+
renderer.render(vnode, document.getElementById('app'));
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Signal 渲染器
|
|
25
|
+
|
|
26
|
+
用于细粒度响应式更新(Vapor 模式)
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { createSignalRenderer, createVaporRenderer } from '@lytjs/renderer';
|
|
30
|
+
|
|
31
|
+
// Signal 渲染器
|
|
32
|
+
const signalRenderer = await createSignalRenderer();
|
|
33
|
+
|
|
34
|
+
// Vapor 渲染器(Signal 渲染器的别名)
|
|
35
|
+
const vaporRenderer = await createVaporRenderer();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### SSR 渲染器
|
|
39
|
+
|
|
40
|
+
用于服务端渲染
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { renderToString, renderToStream } from '@lytjs/renderer';
|
|
44
|
+
|
|
45
|
+
// 渲染为字符串
|
|
46
|
+
const html = await renderToString(vnode);
|
|
47
|
+
|
|
48
|
+
// 流式渲染
|
|
49
|
+
const stream = await renderToStream(vnode, {
|
|
50
|
+
onChunk: (chunk) => res.write(chunk),
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 核心 API
|
|
55
|
+
|
|
56
|
+
### createRenderer
|
|
57
|
+
|
|
58
|
+
创建自定义渲染器实例
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { createRenderer, type RendererOptions } from '@lytjs/renderer';
|
|
62
|
+
|
|
63
|
+
const options: RendererOptions = {
|
|
64
|
+
createElement: (tag) => document.createElement(tag),
|
|
65
|
+
insert: (el, parent, anchor) => parent.insertBefore(el, anchor),
|
|
66
|
+
// ...
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const renderer = createRenderer(options);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### createDOMRenderer
|
|
73
|
+
|
|
74
|
+
创建 DOM 渲染器实例
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { createDOMRenderer } from '@lytjs/renderer';
|
|
78
|
+
|
|
79
|
+
const renderer = createDOMRenderer();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### patchProp / patchClass / patchStyle / patchEvent / patchAttr
|
|
83
|
+
|
|
84
|
+
DOM 属性更新工具函数
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import {
|
|
88
|
+
patchProp,
|
|
89
|
+
patchClass,
|
|
90
|
+
patchStyle,
|
|
91
|
+
patchEvent,
|
|
92
|
+
patchAttr,
|
|
93
|
+
normalizeEventName,
|
|
94
|
+
getEventKey,
|
|
95
|
+
parseEventModifier,
|
|
96
|
+
createInvoker,
|
|
97
|
+
} from '@lytjs/renderer';
|
|
98
|
+
|
|
99
|
+
// 更新属性
|
|
100
|
+
patchProp(el, 'class', prevValue, nextValue);
|
|
101
|
+
|
|
102
|
+
// 更新 class
|
|
103
|
+
patchClass(el, 'active visible');
|
|
104
|
+
|
|
105
|
+
// 更新 style
|
|
106
|
+
patchStyle(el, { color: 'red', fontSize: '14px' });
|
|
107
|
+
|
|
108
|
+
// 更新事件
|
|
109
|
+
patchEvent(el, 'onClick', prevHandler, nextHandler);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Hydration
|
|
113
|
+
|
|
114
|
+
### createHydrationFunctions
|
|
115
|
+
|
|
116
|
+
创建服务端渲染 Hydration 函数
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import { createHydrationFunctions } from '@lytjs/renderer';
|
|
120
|
+
|
|
121
|
+
const { hydrate, hydrateNode } = createHydrationFunctions(options);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Island Architecture
|
|
125
|
+
|
|
126
|
+
Island Architecture 用于在 SSR 页面中实现部分区域的客户端交互
|
|
127
|
+
|
|
128
|
+
### hydrateIsland
|
|
129
|
+
|
|
130
|
+
水合单个 Island 组件
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { hydrateIsland, registerIslandComponent } from '@lytjs/renderer';
|
|
134
|
+
|
|
135
|
+
// 注册 Island 组件
|
|
136
|
+
await registerIslandComponent('Counter', CounterComponent);
|
|
137
|
+
|
|
138
|
+
// 水合 Island
|
|
139
|
+
const islandEl = document.querySelector('[data-island="Counter"]');
|
|
140
|
+
await hydrateIsland(islandEl, CounterComponent, { initialCount: 0 });
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### registerIslandComponent
|
|
144
|
+
|
|
145
|
+
注册 Island 组件供后续水合使用
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import { registerIslandComponent } from '@lytjs/renderer';
|
|
149
|
+
|
|
150
|
+
await registerIslandComponent('MyComponent', MyComponent);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### createIslandSSRContent
|
|
154
|
+
|
|
155
|
+
创建 Island SSR 内容
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import { createIslandSSRContent } from '@lytjs/renderer';
|
|
159
|
+
|
|
160
|
+
const html = await createIslandSSRContent(vnode, {
|
|
161
|
+
islandId: 'Counter',
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## 懒加载
|
|
166
|
+
|
|
167
|
+
渲染器支持懒加载大型模块,减少初始包体积:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// SSR 相关函数使用动态导入
|
|
171
|
+
import { renderToString, renderToStream } from '@lytjs/renderer';
|
|
172
|
+
|
|
173
|
+
// 这些函数会按需加载 SSR 模块
|
|
174
|
+
const html = await renderToString(vnode);
|
|
175
|
+
|
|
176
|
+
// Signal/Vapor 渲染器也是懒加载的
|
|
177
|
+
const signalRenderer = await createSignalRenderer();
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## 渲染器插件系统
|
|
181
|
+
|
|
182
|
+
### use
|
|
183
|
+
|
|
184
|
+
安装渲染器插件
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import { use, type RendererPlugin } from '@lytjs/renderer';
|
|
188
|
+
|
|
189
|
+
const myPlugin: RendererPlugin = {
|
|
190
|
+
name: 'MyPlugin',
|
|
191
|
+
install(context) {
|
|
192
|
+
context.on('beforeMount', (vnode) => {
|
|
193
|
+
console.log('Before mount:', vnode);
|
|
194
|
+
});
|
|
195
|
+
},
|
|
196
|
+
beforeMount(vnode) {
|
|
197
|
+
console.log('Will mount:', vnode);
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
use(myPlugin);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 插件生命周期钩子
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
interface RendererPlugin {
|
|
208
|
+
name: string;
|
|
209
|
+
install: (context: PluginContext) => void;
|
|
210
|
+
beforeMount?: (vnode: VNode) => void;
|
|
211
|
+
afterMount?: (vnode: VNode, container: unknown) => void;
|
|
212
|
+
beforePatch?: (oldVNode: VNode, newVNode: VNode) => void;
|
|
213
|
+
afterPatch?: (vnode: VNode) => void;
|
|
214
|
+
beforeUnmount?: (vnode: VNode) => void;
|
|
215
|
+
afterUnmount?: (vnode: VNode) => void;
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### 插件管理
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import { use, getInstalledPlugins, isPluginInstalled, removePlugin } from '@lytjs/renderer';
|
|
223
|
+
|
|
224
|
+
// 安装插件
|
|
225
|
+
use(myPlugin);
|
|
226
|
+
|
|
227
|
+
// 检查插件是否已安装
|
|
228
|
+
if (isPluginInstalled('MyPlugin')) {
|
|
229
|
+
console.log('Plugin is installed');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// 获取所有已安装插件
|
|
233
|
+
const plugins = getInstalledPlugins();
|
|
234
|
+
|
|
235
|
+
// 移除插件
|
|
236
|
+
removePlugin('MyPlugin');
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Vapor 模式
|
|
240
|
+
|
|
241
|
+
Vapor 模式提供无虚拟 DOM 的细粒度响应式渲染
|
|
242
|
+
|
|
243
|
+
### createVaporApp
|
|
244
|
+
|
|
245
|
+
创建 Vapor 应用
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { createVaporApp, defineVaporComponent } from '@lytjs/renderer';
|
|
249
|
+
|
|
250
|
+
const MyComponent = await defineVaporComponent({
|
|
251
|
+
setup() {
|
|
252
|
+
const count = signal(0);
|
|
253
|
+
return () => html`<div>${count()}</div>`;
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const app = await createVaporApp(MyComponent);
|
|
258
|
+
app.mount('#app');
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### defineVaporComponent
|
|
262
|
+
|
|
263
|
+
定义 Vapor 组件
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
import { defineVaporComponent } from '@lytjs/renderer';
|
|
267
|
+
|
|
268
|
+
const Counter = await defineVaporComponent({
|
|
269
|
+
props: {
|
|
270
|
+
initialCount: { type: Number, default: 0 },
|
|
271
|
+
},
|
|
272
|
+
setup(props) {
|
|
273
|
+
// 组件逻辑
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## 组件资源清理
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
import {
|
|
282
|
+
registerComponentEventListener,
|
|
283
|
+
registerComponentEffectSubscription,
|
|
284
|
+
registerComponentCleanup,
|
|
285
|
+
cleanupComponentResources,
|
|
286
|
+
} from '@lytjs/renderer';
|
|
287
|
+
|
|
288
|
+
// 注册事件监听器(卸载时自动清理)
|
|
289
|
+
registerComponentEventListener(instance, target, 'click', handler);
|
|
290
|
+
|
|
291
|
+
// 注册 effect 订阅(卸载时自动清理)
|
|
292
|
+
registerComponentEffectSubscription(instance, subscription);
|
|
293
|
+
|
|
294
|
+
// 注册自定义清理函数
|
|
295
|
+
registerComponentCleanup(instance, () => {
|
|
296
|
+
// 清理逻辑
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// 手动清理所有资源
|
|
300
|
+
cleanupComponentResources(instance);
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## 首次渲染优化
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
import {
|
|
307
|
+
withFirstRenderOptimization,
|
|
308
|
+
shouldSkipTracking,
|
|
309
|
+
getSkippedTrackingCount,
|
|
310
|
+
resetSkippedTrackingCount,
|
|
311
|
+
} from '@lytjs/renderer';
|
|
312
|
+
|
|
313
|
+
// 使用首次渲染优化
|
|
314
|
+
withFirstRenderOptimization(() => {
|
|
315
|
+
// 首次渲染时跳过不必要的追踪
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// 检查是否跳过追踪
|
|
319
|
+
if (shouldSkipTracking()) {
|
|
320
|
+
console.log('Tracking is skipped');
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## 工具函数
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
import { escapeHtml, isBooleanAttr, isVoidElement } from '@lytjs/renderer';
|
|
328
|
+
|
|
329
|
+
// HTML 转义
|
|
330
|
+
const safe = escapeHtml('<script>alert("xss")</script>');
|
|
331
|
+
|
|
332
|
+
// 检查布尔属性
|
|
333
|
+
if (isBooleanAttr('disabled')) {
|
|
334
|
+
// 处理布尔属性
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// 检查自闭合标签
|
|
338
|
+
if (isVoidElement('img')) {
|
|
339
|
+
// 自闭合标签处理
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## 类型定义
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
import type {
|
|
347
|
+
VNode,
|
|
348
|
+
RendererOptions,
|
|
349
|
+
RendererPlugin,
|
|
350
|
+
PluginContext,
|
|
351
|
+
LifecycleEvent,
|
|
352
|
+
DOMRenderer,
|
|
353
|
+
SignalRenderer,
|
|
354
|
+
VaporRenderer,
|
|
355
|
+
VaporApp,
|
|
356
|
+
VaporComponentOptions,
|
|
357
|
+
HydrationRenderer,
|
|
358
|
+
RendererHost,
|
|
359
|
+
HostRect,
|
|
360
|
+
HostStyleDeclaration,
|
|
361
|
+
HostEvent,
|
|
362
|
+
HostEventHandler,
|
|
363
|
+
} from '@lytjs/renderer';
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## 相关包
|
|
367
|
+
|
|
368
|
+
- [@lytjs/vdom](../vdom) - 虚拟 DOM 实现,渲染器的基础
|
|
369
|
+
- [@lytjs/core](../core) - 框架核心入口,整合所有子包
|
|
370
|
+
- [@lytjs/adapter-web](../adapter-web) - Web 平台适配器
|
|
371
|
+
- [@lytjs/host-contract](../host-contract) - 渲染器宿主抽象
|
|
372
|
+
|
|
373
|
+
## 依赖版本
|
|
374
|
+
|
|
375
|
+
- [@lytjs/reactivity](https://www.npmjs.com/package/@lytjs/reactivity): ^6.4.0
|
|
376
|
+
- [@lytjs/vdom](https://www.npmjs.com/package/@lytjs/vdom): ^6.4.0
|
|
377
|
+
- [@lytjs/common-is](https://www.npmjs.com/package/@lytjs/common-is): ^6.4.0
|
|
378
|
+
- [@lytjs/common-string](https://www.npmjs.com/package/@lytjs/common-string): ^6.4.0
|
|
379
|
+
- [@lytjs/common-events](https://www.npmjs.com/package/@lytjs/common-events): ^6.4.0
|
|
380
|
+
- [@lytjs/common-dom](https://www.npmjs.com/package/@lytjs/common-dom): ^6.4.0
|
|
381
|
+
- [@lytjs/common-error](https://www.npmjs.com/package/@lytjs/common-error): ^6.4.0
|
|
382
|
+
- [@lytjs/dom-runtime](https://www.npmjs.com/package/@lytjs/dom-runtime): ^6.4.0
|
|
383
|
+
- [@lytjs/compiler](https://www.npmjs.com/package/@lytjs/compiler): ^6.4.0
|
|
384
|
+
- [@lytjs/host-contract](https://www.npmjs.com/package/@lytjs/host-contract): ^6.4.0
|
|
385
|
+
- [@lytjs/adapter-web](https://www.npmjs.com/package/@lytjs/adapter-web): ^6.4.0
|