☰
FAST Element `shadowOptions` 配置完全指南:控制自定义元素 Shadow DOM 的创建方式
2026/9/29 2:28:59 网站建设 项目流程
  • 前端
  • UI组件

【免费下载链接】fast

The adaptive interface system for modern web experiences.

项目地址:https://gitcode.com/gh_mirrors/fa/fast
点击查看免费下载

导读

shadowOptions是@microsoft/fast-element中PartialFASTElementDefinition的核心配置属性,它决定了 FAST 自定义元素创建 Shadow DOM 的方式:默认开放(open)模式、封闭(closed)模式,或是直接渲染到 Light DOM。本文将以 fast-element.partialfastelementdefinition.shadowoptions.md 为骨架,结合 fast-definitions.ts 与 element-controller.ts 的源码实现,完整讲解该属性的签名、默认值语义、三种取值行为、与attachShadow的对应关系,以及实际组件中的配置范例。

属性签名与定义位置

shadowOptions是PartialFASTElementDefinition接口的成员属性,该接口位于 fast-definitions.ts。其类型签名如下:

readonly shadowOptions?: Partial<ShadowRootOptions> | null;

几个关键点:

  • readonly:该属性在定义阶段只读,用于向FASTElementDefinition提供元数据,不会在运行期被用户代码直接修改。
  • Partial<ShadowRootOptions>:只需提供与默认值有差异的字段,未提供的字段会与框架默认值合并。
  • | null:显式传入null表示不使用 Shadow DOM,元素模板将渲染到 Light DOM。

值得留意的是,仓库源码中的ShadowRootOptions接口比浏览器原生ShadowRootInit多扩展了一个registry字段(见 fast-definitions.ts),用于为 shadow root 提供自定义元素注册表(@beta能力):

export interface ShadowRootOptions extends ShadowRootInit { /** * A registry that provides the custom elements visible * from within this shadow root. * @beta */ registry?: CustomElementRegistry; }

在 1.x API 文档中,PartialFASTElementDefinition对该属性的表述为 "Options controlling the creation of the custom element's shadow DOM"(控制自定义元素 Shadow DOM 创建的选项),与源码注释完全一致。

三种取值的语义与默认行为

源码构造函数对shadowOptions的归一化逻辑是理解该属性的钥匙,见 fast-definitions.ts:

this.shadowOptions = nameOrConfig.shadowOptions === void 0 ? defaultShadowOptions : nameOrConfig.shadowOptions === null ? void 0 : { ...defaultShadowOptions, ...nameOrConfig.shadowOptions };

而模块顶部的默认值定义为:

const defaultShadowOptions: ShadowRootInit = { mode: "open" };

由此可以归纳出三种取值行为:

取值行为结果
未提供(undefined)使用默认值以mode: "open"创建开放的 Shadow Root
{ mode: 'closed' }等配置对象与defaultShadowOptions浅合并以用户指定选项创建 Shadow Root
null归一化为void 0不创建 Shadow DOM,模板渲染到 Light DOM

注意合并方向:{ ...defaultShadowOptions, ...nameOrConfig.shadowOptions }意味着用户配置会覆盖默认值,因此只需写出与默认不同的字段即可,例如shadowOptions: { mode: 'closed' }会自动继承mode之外的其余默认语义(此处默认值仅含mode)。

源码级验证:ElementController 如何消费该配置

shadowOptions从定义流向运行时的桥梁是ElementController。在其构造函数中(element-controller.ts),定义中的配置被写入控制器:

public constructor(element: TElement, definition: FASTElementDefinition) { this._notifier = new PropertyChangeNotifier(element); this.source = element; this.definition = definition; this.shadowOptions = definition.shadowOptions; ... }

随后在shadowOptions的 setter 中(element-controller.ts)执行实际的 Shadow DOM 挂载逻辑:

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); } } } }

这段代码揭示了三个实现细节:

  1. Shadow Root 只允许创建一次:注释 "options on the shadowRoot can only be set once" 表明,一旦_shadowRootOptions被赋值,后续赋值会被忽略——这是浏览器规范中attachShadow不可重复调用的直接映射。
  2. closed模式的内部跟踪:当mode: 'closed'时,浏览器不会暴露element.shadowRoot,因此 FAST 内部用shadowRoots(一个WeakMap<Element, ShadowRoot>,见 element-controller.ts)保存引用,供框架内部(如样式注入、shadowRootFor查询,见该文件 L36 与 L948 附近注释)继续访问。
  3. null配置(Light DOM)不会触发attachShadow:因为value为undefined时整个分支被跳过,模板改由 Light DOM 路径渲染。

在@customElement装饰器中使用

shadowOptions最常见的消费入口是@customElement装饰器。该装饰器定义于 fast-element.ts,其参数类型正是string | PartialFASTElementDefinition:

export function customElement(nameOrDef: string | PartialFASTElementDefinition) { return function (type: Constructable<HTMLElement>) { define(type, nameOrDef); }; }

默认:开放 Shadow DOM

以下写法不提供shadowOptions,FAST 会以默认的{ mode: "open" }创建 Shadow Root,模板渲染进 Shadow DOM:

import { FASTElement, customElement, attr, html } from '@microsoft/fast-element'; const template = html<NameTag>` <div class="header"> <h3>${x => x.greeting.toUpperCase()}</h3> </div> <div class="body"> <slot></slot> </div> `; @customElement({ name: 'name-tag', template }) export class NameTag extends FASTElement { @attr greeting: string = 'Hello'; }

封闭模式:shadowOptions: { mode: 'closed' }

@customElement({ name: 'name-tag', template, shadowOptions: { mode: 'closed' } }) export class NameTag extends FASTElement { @attr greeting: string = 'Hello'; }

需要注意官方文档 working-with-shadow-dom.md 的提醒:

Avoid usingclosedmode since it affects event propagation and makes custom elements less inspectable. (尽量避免使用closed模式,因为它会影响事件传播,并降低自定义元素的可检查性。)

这正对应前述源码行为:closed模式下外部无法通过element.shadowRoot访问内部 DOM,composedPath()中 Shadow DOM 内部目标也不会出现(详见 working-with-shadow-dom.md),事件路径看起来就像自定义元素本身是第一个 target。

Light DOM 渲染:shadowOptions: null

@customElement({ name: 'name-tag', template, shadowOptions: null }) export class NameTag extends FASTElement { @attr greeting: string = 'Hello'; }

官方文档同时给出了重要约束(working-with-shadow-dom.md):

If you choose to render to the Light DOM, you will not be able to compose the content, use slots, or leverage encapsulated styles. Light DOM rendering is not recommended for reusable components. It may have some limited use as the root component of a small app. (如果选择渲染到 Light DOM,将无法组合内容、使用 slot,也无法获得样式封装。Light DOM 渲染不建议用于可复用组件,仅适合作为小型应用的根组件等有限场景。)

与原生attachShadow选项的完整对应

shadowOptions的Partial<ShadowRootOptions>类型意味着它暴露了标准Element.attachShadow()的全部选项。官方文档 working-with-shadow-dom.md 明确指出:除 mode 之外,还可以指定如delegatesFocus: true等新选项,且只需写出与默认值不同的字段。

标准ShadowRootInit支持的核心选项(均由shadowOptions透传给attachShadow):

选项类型作用
mode'open' \| 'closed'控制 Shadow Root 的可见性;FAST 默认'open'
delegatesFocusboolean焦点委托,键盘焦点从 shadow host 委托给可聚焦的 shadow 内部元素
slotAssignment'named' \| 'manual'控制 slot 分配模式(较新浏览器支持)
clonableboolean允许cloneNode()时克隆 Shadow Root(较新浏览器支持)
serializableboolean允许通过 Declarative Shadow DOM 序列化(较新浏览器支持)

这些选项在较新浏览器中才可用,且要求元素在构造时由 FAST 统一调用attachShadow完成挂载。以焦点管理为例,配置方式如下:

@customElement({ name: 'my-dialog', template, shadowOptions: { mode: 'open', delegatesFocus: true } }) export class MyDialog extends FASTElement { // 焦点将自动委托到 Shadow DOM 内第一个可聚焦元素 }

组件库中的真实配置范例

在仓库的组件文档中,shadowOptions被广泛使用。例如 fast-components.fastbutton.md 与 fast-components.fasttoolbar.md 展示了fast-button、fast-toolbar等基础组件的定义片段(以下为文档中呈现的形态):

@customElement({ name: 'fast-button', template, styles, shadowOptions: { ... } })

此外,fast-components.allcomponents.md 汇总了fast-avatar、fast-search、fast-text-field、fast-anchor、fast-text-area、fast-number-field、fast-picker、fast-breadcrumb、fast-combobox等一系列组件的定义,其中均包含shadowOptions字段。在 design-systems/creating-a-component-library.md 与 design-systems/fast-frame.md 的设计系统文档中,也给出了shadowOptions在库级配置中的写法;后者还提示:更多 Shadow 选项的细节可参考原生Element.attachShadow()规范。

若需要快速查阅速记形态,resources/cheat-sheet.md 的速查表中也包含了带shadowOptions的组件定义示例。

与FASTElementDefinition的关系:Partial 到完整定义

需要区分两个相关但不同的属性:

  • PartialFASTElementDefinition.shadowOptions(本文主题):类型为Partial<ShadowRootOptions> | null,是用户书写定义时提供的部分配置,允许省略字段、允许传null。
  • FASTElementDefinition.shadowOptions(见 fast-element.fastelementdefinition.shadowoptions.md 与 fast-definitions.ts):类型为ShadowRootOptions,是归一化后的完整配置——由构造函数完成默认值合并,再交付给ElementController执行attachShadow。

这一"Partial 输入 → 合并默认值 → 完整定义 → 控制器消费"的链路,正是shadowOptions设计的核心:用户永远只需声明差异,框架负责补齐默认语义(默认mode: "open")。

小结

shadowOptions用极简的 API 覆盖了 Shadow DOM 创建的全部决策点:

  • 省略→ 开放 Shadow DOM(框架默认);
  • { mode: 'closed' }→ 封闭 Shadow DOM,牺牲可检查性换取更强封装;
  • null→ Light DOM 渲染,放弃 slot 组合与样式封装,仅适用于根组件等场景;
  • 其余attachShadow选项(delegatesFocus等)→ 按需透传,只写差异项。

其运行时行为可在 element-controller.ts 中验证:Shadow Root 仅创建一次、closed模式由内部WeakMap跟踪、Light DOM 路径不触发attachShadow。理解了这三点,你就能在 FAST 应用中精确掌控自定义元素的渲染域与封装边界。

  • 前端
  • UI组件

【免费下载链接】fast

The adaptive interface system for modern web experiences.

项目地址:https://gitcode.com/gh_mirrors/fa/fast
点击查看免费下载
上一篇:DXVK配置文件验证工具:检查参数有效性
下一篇:终极GameFramework实战指南:如何快速开发完整RPG游戏

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询