HTML 原生不支持 createContext / useContext,customElement 需手动通过属性传递、自定义事件、全局对象或 DOM 查找模拟上下文,attributeChangedCallback 是关键入口但需配合 observedAttributes 且易踩大小写、字符串解析、无限循环等坑。

HTML 原生不支持 React 那样的 createContext / useContext 机制,所谓“上下文传递”在 HTML 组件化中不是标准 API,而是开发者通过约定、属性注入、自定义事件或全局状态管理模拟出来的行为——直接用 customElements.define() 注册的组件之间,没有内置的跨层级数据透传能力。
customElement 内部如何模拟上下文?
本质是手动把“上下文”作为属性或事件参数向下传递,或借助全局对象/事件总线。常见做法包括:
- 父组件通过
setAttribute或直接赋值(如el.theme = 'dark')把配置传给子元素,子组件在attributeChangedCallback中解析 - 用
dispatchEvent发送自定义事件(如new CustomEvent('context-change', { detail: { locale: 'zh-CN' } })),祖先组件监听并转发给后代 - 依赖全局状态对象(如
window.__APP_CONTEXT__),子组件读取时加if (window.__APP_CONTEXT__)守卫,避免运行时报错 - 用
document.querySelector向上查找最近的“上下文提供者”(如this.closest('app-shell')),再取其公开属性
attributeChangedCallback 是上下文同步的关键入口
它不是“订阅”,而是被动响应属性变更,必须配合 static get observedAttributes() 才生效。容易踩的坑有:
- 属性名大小写敏感:
observedAttributes返回['theme'],但 HTML 写成<x-button theme="dark">才触发,THEME或data-theme不会触发 - 属性值始终是字符串,
JSON.parse(this.getAttribute('config'))必须包try/catch,失败时设默认值(如this._config = {}) - 不要在
attributeChangedCallback中调用this.setAttribute(),否则可能引发无限循环 - 布尔属性(如
disabled)不能靠值判断:this.disabled = this.hasAttribute('disabled')才正确
为什么不能直接复用 React 的 Context 模式?
因为 customElements 是 DOM 级别 API,不介入 JS 框架的渲染生命周期。React 的 Context 依赖组件树的虚拟节点关系和 Fiber 调度,而原生 customElement 的父子关系只体现在 DOM 树上,且没有统一的“Provider”挂载点。
立即学习“前端免费学习笔记(深入)”;
强行套用会导致:
-
MyContext.Provider在非 React 环境下只是普通 HTML 元素,子组件无法识别其“提供者”身份 - 没有
useContext这样的 Hook,子组件无法自动订阅变化,只能轮询或监听 MutationObserver - 若混用 React 和 customElement,上下文需桥接两套系统(如用
ref获取 DOM 节点再读取 context 属性),维护成本高
真正能落地的“上下文”,是明确知道谁提供、谁消费、怎么降级的显式契约——比如所有组件都约定从 this.ownerDocument.documentElement.dataset.theme 读主题,比幻想一个自动透传的 Context 更可靠。



















