☰
FASTElementDefinition.shadowOptions 详解:用 FASTElement 精确控制 Shadow DOM 的创建
2026/9/29 5:34:35 网站建设 项目流程
  • 前端
  • UI组件

【免费下载链接】fast

The adaptive interface system for modern web experiences.

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

本篇技术指南围绕@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
nullundefined不创建 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

核心要点回顾:

  1. FASTElementDefinition.shadowOptions是定义元数据的一部分,类型为readonly shadowOptions?: ShadowRootInit,控制 Shadow DOM 的创建方式。
  2. 默认值为{ mode: "open" };传null则渲染到 Light DOM;传对象时与默认值浅合并。
  3. 实际创建由ElementController在构造函数阶段调用attachShadow完成,且只执行一次;closed模式下的 Shadow Root 引用由内部WeakMap维护。
  4. 完整实战示例与使用边界,可继续参阅 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.

项目地址:https://gitcode.com/gh_mirrors/fa/fast
点击查看免费下载
上一篇:5个现代Web开发痛点与Express.js的架构级解决方案
下一篇:pgFormatter安全部署指南:CGI模式的安全配置和防护

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

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

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

立即咨询