第一个内容组件
落地页演示里,编辑器存自己的草稿,目录记自己的选中项,属性面板有各自己的计数。这篇讲怎么注册这样的内容类型、给它声明初始状态,以及状态如何跟着布局操作迁移。
注册一种内容类型
每种内容用 contentType 字符串标识。用 registerContent 声明这种内容的初始状态和渲染组件:
// MyEditor.tsx
import { registerContent } from "@drahamo/tiling-layout";
import type { ContentProps } from "@drahamo/tiling-layout";
function MyEditor({ state, setState }: ContentProps<{ text?: string }>) {
// 首帧渲染时 state 可能还没有 defaults 注入,读取时对缺字段兜底
const text = state.text ?? "";
return (
<textarea
value={text}
onChange={(e) => setState({ text: e.target.value })}
style={{ width: "100%", height: "100%" }}
/>
);
}
registerContent({
type: "myeditor",
title: "编辑器", // 区域头部标题,不填则回退到 type 原文
defaults: { text: "在这写…" },
Comp: MyEditor,
});
defaults 声明的对象决定了 ContentProps 的类型参数:组件里读到的 state 和写用的 setState 都是这个形状,不需要类型断言。registerContent 要在渲染前执行(比如放在模块顶层)。
在声明式布局里内联内容
除了在模块顶层 registerContent,initialLayout 的 content 字段也可以直接写内联定义——库会帮你自动注册,type 就是最终的内容类型:
<LayoutViewDom
initialLayout={{
areas: [
{
rect: [0, 0, 1, 1],
content: {
type: "myeditor", title: "编辑器", defaults: { text: "在这写…" },
Comp: MyEditor,
},
},
],
}}
/>
和 registerContent 是同一件事的两种写法,可以混用——initialLayout 里既引用已注册的类型名,也内联新定义。内联定义在布局校验通过之后才注册:无效布局(越界、重叠、id 重复)被拒绝时,注册表不会留下残留。type 是唯一身份,实例状态、命令式查询都按它寻址。更完整的声明式布局用法见声明式初始布局。
内容实例生命周期
每个「区域 × 内容类型」组合都是一个内容实例,它有自己的生命周期。注册时的 onMount / onUnmount 回调让你在实例创建和销毁时介入(挂三方库实例、开监听器等),回调收到的 ContentLifecycleCtx 包含 areaId、contentType 和该区域内容容器的 DOM 节点 el:
import { registerContent } from "@drahamo/tiling-layout";
const charts = new WeakMap<HTMLElement, Chart>();
registerContent({
type: "chart",
title: "图表",
Comp: ChartPanel,
onMount({ el }) {
// 面板挂载后调用(defaults 已注入);把三方实例绑到容器 DOM 上
const chart = createChart(el);
charts.set(el, chart);
},
onUnmount({ el }) {
// 与 onMount 成对;此时 el 仍可用,可安全 dispose
charts.get(el)?.dispose();
},
});
一次完整生命周期是:区域几何被创建 → 该类型内容首次渲染(defaults 注入 → onMount)→ 存活期(组件用 setState 读写实例状态)→ 区域销毁或内容切换(实例状态在操作落位时同步迁移或丢弃,随后 onUnmount → 容器 ref 注销)。
什么时候触发
| 事件 | 触发时机 |
|---|---|
onMount | 区域首次渲染出该类型;分割/停靠产生新区域;切回该类型;恢复快照重建布局 |
onUnmount | 切换到别的类型;区域被合并吞并;恢复快照重建前的旧实例;组件树卸载;类型被注销(unregisterContent) |
defaults 注入 | 仅当该槽位为空;由交换/停靠迁移来的状态不覆盖 |
注意 onMount/onUnmount 是每次挂载/卸载都会触发(同一个区域切走再切回,就是一对新的 onMount/onUnmount),不是进程内一次。需要跨挂载存活的数据放实例状态(defaults/setState)或共享层,回调里只做 DOM/三方资源的建立与释放。