- 前端
- UI组件
【免费下载链接】fast
The adaptive interface system for modern web experiences.
Controller.template是 FAST 元素控制器(Controller/ElementController)上用于读取与设置组件渲染模板的核心属性,负责把html模板编译器生成的ElementViewTemplate绑定到自定义元素的生命周期上。本文以 1.x API 文档中该属性的官方签名为骨架,结合当前仓库packages/fast-element的源码与 Playwright 测试,完整解析其三级解析优先级、connect前后的读写语义差异、以及设置模板后的即时重渲染机制,帮助你在自定义元素开发中准确控制模板的加载时机与覆盖行为。
属性定位:Controller 在组件渲染体系中的角色
在@microsoft/fast-element中,Controller类负责控制一个FASTElement的生命周期与渲染。官方 1.x 类文档(fast-element.controller.md)给出的签名如下:
export declare class Controller extends PropertyChangeNotifier该类暴露的核心属性包括:
| 属性 | 类型 | 说明 |
|---|---|---|
definition | FASTElementDefinition | 指导控制器完成渲染与平台集成定义 |
element | HTMLElement | 被该控制器控制的元素 |
isConnected | boolean | 元素是否已连接进文档 |
styles | ElementStyles \| null | 读取/设置组件的主样式 |
template | ElementViewTemplate \| null | 读取/设置用于渲染组件的模板 |
view | ElementView \| null | 与自定义元素关联的视图实例 |
其中template是渲染的源头:模板经过 template.ts 中ElementViewTemplate接口的create(hostBindingTarget)方法生成视图,视图再被挂载到 shadow root(或 light DOM)中;而view属性(见 fast-element.controller.view.md)即为渲染产生的最终结果,若为null则表示元素自己管理渲染。
官方签名与语义
关联文档(fast-element.controller.template.md)对该属性给出的完整定义如下:
功能描述:Gets/sets the template used to render the component.(读取/设置用于渲染组件的模板。)
签名:
get template(): ElementViewTemplate | null; set template(value: ElementViewTemplate | null);Remarks(官方注意事项):
This value can only be accurately read after connect but can be set at any time. (该值只能在 connect 之后被准确读取,但可以在任意时刻被设置。)
这两句话概括了template的全部行为边界:读取需要等待连接完成,设置则不受生命周期限制。下面结合源码逐一解释背后的原因。
Getter 的三级模板解析优先级
Controller.template的读取并非直接返回某个固定字段,而是一个惰性、带优先级的解析过程。当前仓库中ElementController的实现(element-controller.ts)清晰地展示了这一逻辑:
public get template(): ElementViewTemplate<TElement> | null { // 1. Template overrides take top precedence. if (this._template === null) { const definition = this.definition; if ((this.source as any).resolveTemplate) { // 2. Allow for element instance overrides next. this._template = (this.source as any).resolveTemplate(); } else if (definition.template) { // 3. Default to the static definition. this._template = (definition.template as ElementViewTemplate<TElement> | undefined) ?? null; } } return this._template; }解析优先级从高到低为:
- 已显式设置的模板:一旦
_template私有字段非null(例如通过 setter 或此前解析写入),直接返回,不再重复解析。 - 元素实例级
resolveTemplate()覆盖:若被控制的元素实例上存在resolveTemplate方法,则调用它获得模板——这为按实例定制模板(如按属性、环境动态选择模板)提供了入口。 - 静态定义兜底:回退到
FASTElementDefinition.template(即通过@customElement装饰器或FASTElement.define配置在元素定义上的模板)。
测试用例中可以看到这一优先级被实际验证(element-controller.pw.spec.ts):
class ControllerTest extends FASTElement { static definition = { name }; resolveTemplate() { return html` ${templateA} `; } }Setter 的行为:可随时设置,且连接后即时生效
与 getter 的惰性解析不同,setter 是主动赋值:直接写入私有字段_template,并且在元素已完成初始化(已 connect 过)的情况下立即触发重渲染。源码实现(element-controller.ts):
public set template(value: ElementViewTemplate<TElement> | null) { if (this._template === value) { return; } this._template = value; if (!this.needsInitialization) { this.renderTemplate(value); } }这里有两个关键设计:
- 值相等短路:若新值与原模板引用相同,直接返回,避免无意义的重复渲染。
needsInitialization门控:若元素尚未完成首次初始化,setter 只记录模板,渲染动作推迟到connect()时统一执行;若元素已经初始化过,则立刻调用renderTemplate重渲染。这正是官方 Remarks 中"可以在任意时刻被设置"的底层保证——无论 connect 前后赋值,模板最终都会被正确应用到组件上。
为什么"只能在 connect 后准确读取"
官方注释明确指出读取的准确性与连接时机强相关,原因可以从connect()生命周期流程中找到(element-controller.ts):
public connect(): void { if (this.stage !== Stages.disconnected) { return; } this.stage = Stages.connecting; // ... 捕获绑定属性、同步晚定义属性、绑定可观察值、连接 behaviors if (this.needsInitialization) { this.renderTemplate(this.template); this.addStyles(this.mainStyles); this.needsInitialization = false; } else if (this.view !== null) { this.view.bind(this.source); } this.stage = Stages.connected; Observable.notify(this, isConnectedPropertyName); }要点如下:
- 首次渲染发生在 connect 期间:
renderTemplate(this.template)首次被调用时,getter 才开始真正执行三级解析(此时_template为null,需要从resolveTemplate()或定义中解析)。因此"connect 之后"的读取才反映最终生效的模板;在此之前,_template尚未被解析填充,读取可能得到null或未解析的中间状态。 resolveTemplate可异步/延迟返回:从fast-definitions.ts中可见,模板解析支持FASTElementTemplateResolver与 pending 解析机制(fast-definitions.ts),元素实例的解析结果在 connect 时才被确认。- 实例覆盖的动态性:
resolveTemplate()是实例方法,其返回值可能依赖实例状态(如某个属性值),只有元素实例化并进入连接流程后,该状态才可用。
renderTemplate 底层:水合与客户端渲染
当 setter 在连接后触发、或connect()完成首次渲染时,实际执行的是renderTemplate方法(element-controller.ts)。其核心流程为:
- 确定渲染宿主:优先使用元素的 shadow root,否则回退到元素自身(light DOM 模式)。
- 清理旧视图:若存在既有
view,调用dispose()释放;若是首次初始化但存在既有 shadow root(如 SSR 预渲染内容),先清空宿主子节点。 - 水合尝试:若存在预渲染内容且安装了水合钩子(
ElementController.hydrationHook,由enableHydration()安装),先尝试水合,成功则跳过客户端渲染。 - 客户端渲染兜底:水合未执行或失败时,走
renderClientSide——克隆编译后的模板片段、绑定、追加到宿主,并标记sourceLifetime为coupled(element-controller.ts)。 - 模板为
null时:仅解析isPrerendered/isHydrated状态,不产生渲染。
这解释了 setter 赋值null的含义:在已初始化元素上将模板置为null会走renderTemplate(null)路径,移除既有视图;而在未初始化元素上则推迟到 connect 时按"无模板"处理(对应测试 element-controller.pw.spec.ts 验证的"无模板时不渲染任何内容到 shadow/light DOM"行为)。
实际使用场景与完整示例
基于上述机制,Controller.template的典型使用方式有两种:
场景一:通过元素定义静态声明(最常见)
import { FASTElement, customElement, html } from '@microsoft/fast-element'; @customElement('name-tag') export class NameTag extends FASTElement { // 定义级模板,由控制器在 connect 时自动解析并渲染 }在@customElement装饰器中以template选项配置模板(详见 1.x 指南 defining-elements.md):
FASTElement.define({ name: 'name-tag', template: html<NameTag>`<span>Hello, ${x => x.greeting}</span>`, });场景二:运行时通过控制器覆盖模板
先获取控制器再赋值,实现模板的热切换:
import { ElementController } from '@microsoft/fast-element'; const element = document.querySelector('name-tag'); const controller = ElementController.forCustomElement(element); // 元素已连接:立即触发重渲染;未连接:在首次 connect 时生效 controller.template = html<NameTag>`<span>New template</span>`;测试 element-controller.pw.spec.ts 验证了这一覆盖行为:定义中声明模板 A,在 connect 前通过controller.template设置为模板 B,最终渲染结果为 B,证明"可随时设置"确实生效于连接前后的两种情形(shadow DOM 与 light DOM 模式均覆盖)。
关联参考
- 属性总览:fast-element.controller.md
- 关联属性:
view(fast-element.controller.view.md)、styles(fast-element.controller.styles.md) - 模板类型定义:template.ts
- 控制器核心实现:element-controller.ts
- 行为验证测试:element-controller.pw.spec.ts
- 前端
- UI组件
【免费下载链接】fast
The adaptive interface system for modern web experiences.
相关推荐
fast-element `Accessor.getValue()` 深度解析:属性读取与依赖收集机制
fast element Accessor.getValue 深度解析:属性读取与依赖收集机制 在 @microsoft/fast element 的响应式系统
前端UI组件FASTElementDefinition.attributes 属性详解:@microsoft/fast-element 自定义元素属性元数据的内幕
FASTElementDefinition.attributes 属性详解:@microsoft/fast element 自定义元素属性元数据的内幕 导读 F
前端UI组件FAST 颜色系统详解:@microsoft/fast-colors 中 QuantizedColor.color 属性与图像调色板量化
FAST 颜色系统详解:@microsoft/fast colors 中 QuantizedColor.color 属性与图像调色板量化 本文围绕 @micro
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考