Web组件托管在异域名时如何正确加载关联HTML、CSS资源?
解决方案
核心是利用ES模块原生提供的import.meta.url属性,该属性会返回当前JS模块自身的绝对部署路径,我们只需要基于这个路径拼接CSS和HTML的相对路径即可,不需要硬编码CDN地址。
修改后的WebComponent.js代码如下:
export default class WebComponent extends HTMLElement { constructor() { super(); this.attachShadow({ mode: "open" }); const style = new CSSStyleSheet(); const template = document.createElement("template"); // 基于模块自身路径解析CSS资源地址 fetch(new URL("./WebComponent.css", import.meta.url)).then((res) => res.text().then((css) => { style.replaceSync(css); this.shadowRoot.adoptedStyleSheets = [style]; }) ); // 基于模块自身路径解析HTML模板地址 fetch(new URL("./WebComponent.html", import.meta.url)).then((res) => res.text().then((html) => { template.innerHTML = html; this.shadowRoot.appendChild(template.content.cloneNode(true)); }) ); } }
方案优势
- 完全符合无构建工具依赖、纯ES模块导出的需求
- 三个资源文件依然独立存储,保留语法高亮、关注点分离的开发体验
- 自动适配任意部署路径,不管是本地开发还是CDN部署,只要三个文件放在同一目录下即可正常运行
- 无额外第三方依赖,兼容性覆盖所有支持原生Web Component和ES模块的浏览器(Chrome 64+、Firefox 67+、Safari 14+、Edge 79+)
可选优化
你可以用Promise.all并行加载两个资源,提升组件初始化速度,示例如下:
export default class WebComponent extends HTMLElement { constructor() { super(); this.attachShadow({ mode: "open" }); const style = new CSSStyleSheet(); const template = document.createElement("template"); // 并行加载CSS和HTML资源 Promise.all([ fetch(new URL("./WebComponent.css", import.meta.url)).then(res => res.text()), fetch(new URL("./WebComponent.html", import.meta.url)).then(res => res.text()) ]).then(([css, html]) => { style.replaceSync(css); this.shadowRoot.adoptedStyleSheets = [style]; template.innerHTML = html; this.shadowRoot.appendChild(template.content.cloneNode(true)); }) } }
内容的提问来源于stack exchange,提问作者Julius Niedermeier
相关产品推荐
相关产品推荐

