You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在原生JS编写的Web Component中使用HTML模板

性能问题说明

你担心的重复解析问题确实存在:如果每次组件实例化都直接给innerHTML赋值模板字符串,浏览器每次都会重新解析字符串生成DOM,实例数量多的时候会有可感知的性能损耗。但这个问题很容易通过原生JS的静态缓存能力解决,不需要依赖废弃的HTML Import。

原生JS推荐方案

方案1:静态缓存Template实例(最通用,性能和原生template一致)

这个方案的核心是把模板解析逻辑只执行一次,所有组件实例复用预解析好的模板内容,性能和.html文件里的原生template完全持平:

class MyComponent extends HTMLElement {
  // 私有静态模板,全类共享,只会初始化一次
  static #template = document.createElement('template');

  // 静态块初始化模板内容,只会执行一次
  static {
    this.#template.innerHTML = `
      <style>
        /* 你的所有样式 */
        :host { display: block; }
        b { color: red; }
      </style>
      <b>I'm in shadow dom!</b>
      <slot></slot>
    `;
  }

  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    // 直接克隆预解析好的模板内容,不需要每次解析字符串
    this.shadowRoot.appendChild(MyComponent.#template.content.cloneNode(true));
  }
}

customElements.define('my-component', MyComponent);

该方案优势:

  • 所有内容都封装在.js文件里,不需要额外的.html资源
  • 私有静态属性不会污染全局作用域,不会和其他组件的模板冲突

方案2:结合Constructable Stylesheets优化样式性能(现代浏览器推荐)

如果你的组件样式体量较大,可以用浏览器原生支持的可构造样式表进一步优化,样式只会被解析一次,所有组件实例共享同一份样式表内存:

class MyComponent extends HTMLElement {
  static #template = document.createElement('template');
  static #sheet = new CSSStyleSheet();

  static {
    // 初始化样式,仅解析一次
    this.#sheet.replaceSync(`
      :host { display: block; }
      b { color: red; }
      /* 大量样式 */
    `);
    // 模板只写HTML结构,不用内嵌style标签
    this.#template.innerHTML = `
      <b>I'm in shadow dom!</b>
      <slot></slot>
    `;
  }

  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    // 复用共享样式表
    this.shadowRoot.adoptedStyleSheets = [MyComponent.#sheet];
    this.shadowRoot.appendChild(MyComponent.#template.content.cloneNode(true));
  }
}
customElements.define('my-component', MyComponent);

兼容性降级方案

如果需要支持不支持静态块、私有静态属性的旧环境,可以把模板初始化逻辑放到第一次实例化时执行:

class MyComponent extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    
    // 第一次实例化时初始化模板,后续实例直接复用
    if (!MyComponent.template) {
      MyComponent.template = document.createElement('template');
      MyComponent.template.innerHTML = `
        <style>/* 样式 */</style>
        <b>I'm in shadow dom!</b>
        <slot></slot>
      `;
    }

    this.shadowRoot.appendChild(MyComponent.template.content.cloneNode(true));
  }
}
customElements.define('my-component', MyComponent);

注意事项

  • 模板字符串里只放固定的静态内容,不要拼接用户输入的动态内容,避免XSS风险。动态内容请在实例化后通过textContent、setAttribute等API单独赋值
  • 可构造样式表目前在所有现代浏览器(Chrome 73+, Firefox 101+, Safari 16.4+)都已支持,无需兼容极旧浏览器的场景优先使用

内容的提问来源于stack exchange,提问作者run_the_race

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 21:36:03