- 前端
- UI组件
【免费下载链接】fast
The adaptive interface system for modern web experiences.
本篇技术指南围绕@microsoft/fast-element中FASTElementDefinition.shadowOptions属性展开,说明它是如何控制自定义元素 Shadow DOM 创建方式的。读者将掌握通过@customElement装饰器配置open/closedShadow DOM、渲染到 Light DOM、以及使用delegatesFocus等标准ShadowRootInit选项的完整实战方法,并理解这些配置在底层ElementController中的执行机制。
属性签名与定位
shadowOptions是FASTElementDefinition类上的一个只读属性,位于 packages/fast-element/src/components/fast-definitions.ts。其官方 API 描述为:
Options controlling the creation of the custom element's shadow DOM.
类型签名(出自 sites/website/src/docs/1.x/api/fast-element.fastelementdefinition.shadowoptions.md):
readonly shadowOptions?: ShadowRootInit;ShadowRootInit是 Web 平台标准接口,FASTElementDefinition对其进行了扩展(见fast-definitions.ts中的ShadowRootOptions接口):
export interface ShadowRootOptions extends ShadowRootInit { /** * A registry that provides the custom elements visible * from within this shadow root. * @beta */ registry?: CustomElementRegistry; }除标准ShadowRootInit字段外,FAST 还额外支持一个registry字段(标记为 beta),用于指定该 shadow root 内部可见的自定义元素注册表。
值得注意的是,同一仓库的 1.x API 体系中,PartialFASTElementDefinition.shadowOptions的类型是Partial<ShadowRootOptions> | null(见 fast-element.partialfastelementdefinition.shadowoptions.md),即配置阶段允许传入部分选项或null;而最终解析完成的FASTElementDefinition.shadowOptions类型为ShadowRootInit,这说明配置在定义组装过程中已被补齐为完整值。
默认行为:自动创建 open 模式 Shadow Root
FASTElement在元素实例化时会自动为组件创建 Shadow Root。从源码看,默认配置由常量定义(fast-definitions.ts):
const defaultShadowOptions: ShadowRootInit = { mode: "open" };在FASTElementDefinition构造函数中,shadowOptions的解析逻辑如下:
this.shadowOptions = nameOrConfig.shadowOptions === void 0 ? defaultShadowOptions : nameOrConfig.shadowOptions === null ? void 0 : { ...defaultShadowOptions, ...nameOrConfig.shadowOptions };三种取值对应三种行为:
| 配置值 | 结果 | 说明 |
|---|---|---|
不提供(undefined) | { mode: "open" } | 默认创建 open 模式的 Shadow Root |
null | undefined | 不创建 Shadow Root,模板渲染到 Light DOM |
| 对象字面量 | 与{ mode: "open" }浅合并 | 未指定的字段继承默认值 |
配置对象的合并采用浅合并(spread),因此你只需写出与默认值不同的字段。例如只写{ mode: "closed" }即可切换到封闭模式。
底层执行机制:ElementController 与 attachShadow
定义中的shadowOptions并非直接生效,而是由控制器在元素实例化时消费。ElementController构造函数将定义中的选项写入自身的shadowOptions字段(element-controller.ts):
this.shadowOptions = definition.shadowOptions;随后在 setter 中完成 Shadow Root 的实际创建(同一文件):
public set shadowOptions(value: ShadowRootOptions | undefined) { // options on the shadowRoot can only be set once if (this._shadowRootOptions === void 0 && value !== void 0) { this._shadowRootOptions = value; let shadowRoot = this.source.shadowRoot; if (shadowRoot) { this.hasExistingShadowRoot = true; } else { shadowRoot = this.source.attachShadow(value); if (value.mode === "closed") { shadowRoots.set(this.source, shadowRoot); } } } }从源码结构可以梳理出几个关键点:
- 创建时机:Shadow DOM 在元素构造函数阶段由控制器挂载,官方文档也明确指出"正是在构造函数中,
FASTElement为元素挂载 Shadow DOM"(见 working-with-shadow-dom.md 的 "Shadow DOM and the element lifecycle" 一节)。 - 只可设置一次:setter 只在
_shadowRootOptions尚未初始化时生效,避免重复挂载。 - 已有 Shadow Root 的复用:如果元素上已存在 Shadow Root(典型场景是服务端渲染/声明式模板预渲染),控制器会记录
hasExistingShadowRoot = true并跳过attachShadow,后续渲染逻辑会优先尝试水合(hydration)而非重建内容,参见renderTemplate中hasPrerenderedContent的判定。 - closed 模式的特殊处理:
closed模式下浏览器不暴露element.shadowRoot,因此 FAST 用模块级WeakMap(shadowRoots)保存对 Shadow Root 的引用,保证内部渲染管线仍能访问到根节点(getShadowRoot辅助函数优先返回element.shadowRoot,取不到时再回退到WeakMap)。
通过 @customElement 配置三种渲染模式
shadowOptions的常规使用入口是@customElement装饰器。以下示例均可在官方 1.x 文档 working-with-shadow-dom.md 中找到对应说明。
1. open 模式(默认)
import { FASTElement, customElement, attr, html } from '@microsoft/fast-element'; const template = html<NameTag>` <div class="header"> <h3>${x => x.greeting.toUpperCase()}</h3> <h4>my name is</h4> </div> <div class="body"> <slot></slot> </div> <div class="footer"></div> `; @customElement({ name: 'name-tag', template }) export class NameTag extends FASTElement { @attr greeting: string = 'Hello'; }这是最常见的写法,不传shadowOptions即可。open 模式下element.shadowRoot对外可见,方便调试与测试。
2. closed 模式
@customElement({ name: 'name-tag', template, shadowOptions: { mode: 'closed' } }) export class NameTag extends FASTElement { @attr greeting: string = 'Hello'; }官方文档给出的建议是:尽量避免使用closed模式,因为它会影响事件传播路径(closed模式下 Shadow DOM 内的目标不会出现在composedPath()中),并且让自定义元素更难以被开发者工具检视(working-with-shadow-dom.md 的 "Shadow DOM configuration" 一节明确标注了该 tip)。仓库中的控制器测试也对两种模式均有覆盖,参见 element-controller.pw.spec.ts 中shadowOptions: { mode: "closed" }与{ mode: "open" }的用例。
3. Light DOM(不创建 Shadow Root)
@customElement({ name: 'name-tag', template, shadowOptions: null }) export class NameTag extends FASTElement { @attr greeting: string = 'Hello'; }传null时定义解析为undefined,控制器不会调用attachShadow,模板将直接渲染到元素自身的 Light DOM。官方文档对此给出了明确警告(working-with-shadow-dom.md):
如果选择渲染到 Light DOM,你将无法组合内容、使用 slot,也无法利用封装的样式。Light DOM 渲染不建议用于可复用组件,仅在小应用的根组件场景下有有限用途。
从源码印证,renderTemplate中渲染目标的选择正是通过getShadowRoot(element) ?? element实现的——没有 Shadow Root 时直接以元素自身为宿主(element-controller.ts)。
使用其他 ShadowRootInit 选项:delegatesFocus 等
由于shadowOptions本质上是标准ShadowRootInit(并可能被合并进默认mode: "open"),你可以传入浏览器attachShadow支持的任何选项,例如delegatesFocus:
@customElement({ name: 'my-input', template, shadowOptions: { delegatesFocus: true } }) export class MyInput extends FASTElement { @attr value: string = ''; }官方文档明确说明:"除了 Shadow DOM 模式之外,shadowOptions暴露了所有可通过标准attachShadowAPI 设置的选项。这意味着你也可以用它指定delegatesFocus: true这样的新选项。你只需指定与上述默认值不同的选项即可。"(working-with-shadow-dom.md)
delegatesFocus: true会让焦点委托到 Shadow DOM 内部的可聚焦元素,对实现可访问性良好的表单控件(如按钮、输入框)非常实用。类似地,slotAssignment(控制 slot 分配算法)等现代ShadowRootInit选项也可按需传入;由于浅合并机制,未提供的字段始终回落到{ mode: "open" }默认值。
在组件库中的实际应用
shadowOptions不仅在单个元素定义中使用,也是 FAST 组件体系的重要配置面。1.x 文档的组件库设计指南(creating-a-component-library.md)指出,FoundationElementDefinition允许组件作者检查聚合后的选项,其中包括shadowOptions、elementOptions等。fast-components 系列组件的 API 文档(如 fast-components.fastbutton.md、fast-components.fastanchor.md)同样展示了shadowOptions: { mode: "closed" }之类的配置形态,说明封闭模式在组件库中用于强化样式与结构隔离。
此外,测试代码中反复出现三种典型用法(element-controller.pw.spec.ts):
// 显式 open static definition = { name, shadowOptions: { mode: "open" } }; // Light DOM static definition = { name, shadowOptions: null }; // closed shadowOptions: { mode: "closed" }这恰好对应前文三种渲染模式,可以作为配置正确性的回归验证参考。
小结与最佳实践
| 需求场景 | shadowOptions 配置 | 备注 |
|---|---|---|
| 默认组件渲染 | 省略该字段 | 自动 open 模式 |
需要element.shadowRoot外部可访问、便于调试 | { mode: "open" } | 默认即此,显式写出可增强可读性 |
| 严格隔离内部结构 | { mode: "closed" } | 官方建议慎用,影响事件路径与可检查性 |
| 小应用根组件、无需 slot/样式封装 | null | 渲染到 Light DOM,不可用于可复用组件 |
| 表单控件焦点委托 | { delegatesFocus: true } | 与默认mode浅合并 |
| 限定 shadow root 内部可见的自定义元素 | { registry }(beta) | FAST 对ShadowRootInit的扩展,见ShadowRootOptions |
核心要点回顾:
FASTElementDefinition.shadowOptions是定义元数据的一部分,类型为readonly shadowOptions?: ShadowRootInit,控制 Shadow DOM 的创建方式。- 默认值为
{ mode: "open" };传null则渲染到 Light DOM;传对象时与默认值浅合并。 - 实际创建由
ElementController在构造函数阶段调用attachShadow完成,且只执行一次;closed模式下的 Shadow Root 引用由内部WeakMap维护。 - 完整实战示例与使用边界,可继续参阅 working-with-shadow-dom.md、fast-element.partialfastelementdefinition.shadowoptions.md 以及源码实现 fast-definitions.ts 和 element-controller.ts。
- 前端
- UI组件
【免费下载链接】fast
The adaptive interface system for modern web experiences.
相关推荐
CANN混合量化稀疏Flash MLA元数据
aclnnMixedQuantSparseFlashMlaMetadata 📄 查看源码 https://link.gitcode.com/i/ef68dba
前端UI组件es-toolkit 兼容层 updateWith 详解:用 customizer 精确控制嵌套对象路径的创建
es toolkit 兼容层 updateWith 详解:用 customizer 精确控制嵌套对象路径的创建 导读 本文以 es toolkit 兼容层(co
前端后端Flet Padding 详解:用 Python 精确控制控件内边距
Flet Padding 详解:用 Python 精确控制控件内边距 Padding(内边距)是 Flet 布局体系中最高频使用的空间控制手段之一,它决定了子控
前端跨平台桌面应用移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考