如何在自定义元素中安全、规范地获取和处理内部内容(如语法高亮组件)

阿强大大_4491

阿强大大_4491

2026-09-09

349人浏览

原创

如何在自定义元素中安全、规范地获取和处理内部内容(如语法高亮组件)

自定义 Web Component(如 )应优先通过 分发内容并监听 slotchange 事件来响应动态内容,而非直接读取 this.innerHTML;后者易导致 XSS 风险、样式隔离失效、Shadow DOM 渲染冲突及构建工具(如 Vite)解析异常。

自定义 web component(如 ``)应优先通过 `` 分发内容并监听 `slotchange` 事件来响应动态内容,而非直接读取 `this.innerhtml`;后者易导致 xss 风险、样式隔离失效、shadow dom 渲染冲突及构建工具(如 vite)解析异常。

在构建语法高亮类自定义元素(例如 <fs-highlight data-language="html">...</fs-highlight>)时,直接访问 this.innerHTML 并非推荐做法,尽管它在某些简单场景下“看似可用”,但存在多个根本性缺陷:

❌ 为什么 this.innerHTML 是危险且不规范的?

  • 破坏 Shadow DOM 封装性:若组件已启用 Shadow DOM(推荐方式),this.innerHTML 读取的是 light DOM 中的原始 HTML 字符串,但该内容不会自动渲染到 shadowRoot 内,导致内容不可见、样式无法作用、事件无法绑定。
  • XSS 风险高:innerHTML 原样拼接用户输入后直接插入(如 this.shadowRoot.innerHTML = ...),极易触发跨站脚本攻击。即使使用 hljs.highlight() 处理,其返回的 .value 仍是未转义的 HTML 字符串,需配合 TrustedHTML 或 unsafeHTML(仅限 Lit)等安全机制。
  • 构建工具兼容性差:Vite、Rollup 等工具在预构建阶段会解析 HTML 模板中的 <script></script>、<style></style> 标签——若 light DOM 内含未转义的 <script></script> 或嵌套标签(如 <div><code>...
),将导致编译失败或运行时解析异常(如 StackBlitz 中报错)。
  • 无法响应内容变更:innerHTML 是一次性快照,当父级动态修改子节点(如 React 更新、JS 插入新文本)时,组件不会自动感知,必须手动监听 slotchange 或 MutationObserver,而 innerHTML 完全绕过了标准响应机制。
  • ✅ 正确方案:基于 <slot></slot> 的声明式内容分发

    Web Components 规范原生支持内容分发(Content Distribution),核心是 <slot></slot> 元素与 slotchange 事件:

    class FsHighlight extends HTMLElement {
      private shadow: ShadowRoot;
      private codeEl: HTMLElement;
    
      constructor() {
        super();
        this.shadow = this.attachShadow({ mode: 'open' });
        // 创建 slot 容器,显式接收 light DOM 内容
        this.shadow.innerHTML = `
          <style>
            :host { display: block; font-family: 'JetBrains Mono', monospace; }
            pre { margin: 0; padding: 1rem; background: #2d2d2d; color: #f8f8f2; }
            code { white-space: pre; }
          </style><pre class="brush:php;toolbar:false;"><code><slot></slot></code>
    `; this.codeEl = this.shadow.querySelector('code')!; } // 监听 slot 内容变化(包括首次挂载) connectedCallback() { const slot = this.shadow.querySelector('slot')!; slot.addEventListener('slotchange', () => this.highlight()); this.highlight(); // 初始高亮 } private highlight() { const slotAssignments = this.shadow .querySelector('slot')! .assignedNodes({ flatten: true }); // 提取纯文本内容(避免执行 script / style) let rawText = ''; for (const node of slotAssignments) { if (node.nodeType === Node.TEXT_NODE) { rawText += node.textContent; } else if (node.nodeType === Node.ELEMENT_NODE) { rawText += (node as HTMLElement).textContent || ''; } } const language = this.getAttribute('data-language') || 'auto'; const result = hljs.highlightAuto(rawText.trim(), { language: language !== 'auto' ? language : undefined }); // 安全注入:使用 innerHTML + sanitize(或 Lit 的 unsafeHTML) this.codeEl.innerHTML = result.value; } } customElements.define('fs-highlight', FsHighlight);

    ✅ 优势显著:

    • ✅ 语义清晰:<slot></slot> 明确表达“此处接收外部内容”,符合 Web Components 设计哲学;
    • ✅ 响应式更新:slotchange 自动捕获所有 DOM 变更(React/Vue 动态渲染、JS 修改);
    • ✅ 样式隔离:CSS 作用于 shadowRoot,不污染全局,也不被外部 CSS 覆盖;
    • ✅ 构建友好:Vite 不解析 <slot></slot> 内容,完全规避编译错误;
    • ✅ 无障碍支持:<slot></slot> 天然支持屏幕阅读器语义流。

    ⚠️ 补充注意事项

    • 禁止在 constructor 中操作 DOM:this.innerHTML 或 this.shadowRoot 在 constructor 中可能为 null,必须在 connectedCallback 中初始化;
    • 语言检测慎用 auto:highlightAuto 可能误判,建议显式声明 data-language="javascript" 并校验有效性;
    • 样式需内联或通过 <link> 加载:@highlightjs 的 CSS 必须通过 <link rel="stylesheet"> 注入 shadowRoot,或提取为字符串内联(避免外部样式污染);
    • 服务端渲染(SSR)兼容:若需 SSR 支持,应在服务端同步执行高亮逻辑,并将结果作为静态 HTML 注入,客户端只做 hydration。

    综上,<slot></slot> 是 Web Components 中处理传入内容的唯一标准、安全、可维护的方式。放弃 this.innerHTML,拥抱 Slot API,才能构建真正健壮、跨框架、可长期演进的自定义元素。

    PHP速学视频免费教程(入门到精通)
    PHP速学视频免费教程(入门到精通)

    PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

    下载

    相关标签:

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

    相关专题

    更多
    html版权符号
    html版权符号

    html版权符号是“©”,可以在html源文件中直接输入或者从word中复制粘贴过来,php中文网还为大家带来html的相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

    2023.06.14

    5395

    7

    html在线编辑器
    html在线编辑器

    html在线编辑器是用于在线编辑的工具,编辑的内容是基于HTML的文档。它经常被应用于留言板留言、论坛发贴、Blog编写日志或等需要用户输入普通HTML的地方,是Web应用的常用模块之一。php中文网为大家带来了html在线编辑器的相关教程、以及相关文章等内容,供大家免费下载使用。

    2023.06.21

    3112

    4

    html网页制作
    html网页制作

    html网页制作是指使用超文本标记语言来设计和创建网页的过程,html是一种标记语言,它使用标记来描述文档结构和语义,并定义了网页中的各种元素和内容的呈现方式。本专题为大家提供html网页制作的相关的文章、下载、课程内容,供大家免费下载体验。

    2023.07.31

    2770

    5

    html空格
    html空格

    html空格是一种用于在网页中添加间隔和对齐文本的特殊字符,被用于在网页中插入额外的空间,以改变元素之间的排列和对齐方式。本专题为大家提供html空格的相关的文章、下载、课程内容,供大家免费下载体验。

    2023.08.01

    2799

    5

    html是什么
    html是什么

    HTML是一种标准标记语言,用于创建和呈现网页的结构和内容,是互联网发展的基石,为网页开发提供了丰富的功能和灵活性。本专题为大家提供html相关的各种文章、以及下载和课程。

    2023.08.11

    4679

    6

    html字体大小怎么设置
    html字体大小怎么设置

    在网页设计中,字体大小的选择是至关重要的。合理的字体大小不仅可以提升网页的可读性,还能够影响用户对网页整体布局的感知。php中文网将介绍一些常用的方法和技巧,帮助您在HTML中设置合适的字体大小。

    2023.08.11

    2741

    3

    html转txt
    html转txt

    html转txt的方法有使用文本编辑器、使用在线转换工具和使用Python编程。本专题为大家提供html转txt相关的文章、下载、课程内容,供大家免费下载体验。

    2023.08.31

    2529

    3

    html文本框代码怎么写
    html文本框代码怎么写

    html文本框代码:1、单行文本框【<input type="text" style="height:..;width:..;" />】;2、多行文本框【textarea style=";height:;"></textare】。

    2023.09.01

    2288

    6

    HTML嵌入CSS样式的方法
    HTML嵌入CSS样式的方法

    HTML嵌入CSS样式的方法有内联样式、内部样式表和外部样式表。本专题为大家提供CSS样式相关的文章、下载、课程内容,供大家免费下载体验。

    2023.09.20

    2288

    5

    热门下载

    更多
    网站特效
    /
    网站源码
    /
    网站素材
    /
    前端模板

    精品课程

    更多
    热门推荐
    /
    最新课程
    phpStudy极速入门视频教程
    phpStudy极速入门视频教程

    共6课时 | 54.6万人学习

    独孤九贱(4)_PHP视频教程
    独孤九贱(4)_PHP视频教程

    共89课时 | 133.4万人学习