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

Angular 18 ShadowRoot中Mermaid内容无法渲染的解决方法

解决Angular 18 ShadowDom下Mermaid图表空白问题

问题根源

Shadow DOM的隔离机制导致Mermaid默认的全局DOM查找(如document.querySelectorAll)无法定位到ShadowRoot内的Mermaid元素,即使关闭了startOnLoad也无法自动触发渲染。

解决方案步骤

1. 调整Mermaid初始化逻辑

在组件的ngAfterViewInit中初始化Mermaid,关闭自动加载,然后手动遍历ShadowRoot内的Mermaid元素触发渲染。

2. 获取ShadowRoot并定位Mermaid元素

通过Angular的ElementRef获取组件的ShadowRoot,在其中查找所有Mermaid代码块容器,逐个调用Mermaid的render方法生成图表。

完整代码示例

组件TS文件

import { Component, ViewEncapsulation, ElementRef, AfterViewInit } from '@angular/core';
import mermaid from 'mermaid';
import { MarkedOptions, MarkedRenderer } from 'ngx-marked';

@Component({
  selector: 'app-shadow-mermaid',
  templateUrl: './shadow-mermaid.component.html',
  styleUrls: ['./shadow-mermaid.component.css'],
  encapsulation: ViewEncapsulation.ShadowDom,
  providers: [
    {
      provide: MarkedOptions,
      useValue: {
        renderer: new MarkedRenderer(),
        extensions: [
          // 自定义Mermaid代码块渲染规则,确保生成带类名的容器
          {
            name: 'mermaid',
            level: 'block',
            start(src) { return src.match(/```mermaid/)?.index; },
            tokenizer(src) {
              const match = /^```mermaid\n([\s\S]*?)\n```/.exec(src);
              if (match) {
                return {
                  type: 'mermaid',
                  raw: match[0],
                  text: match[1].trim(),
                };
              }
              return null;
            },
            renderer(token) {
              return `<div class="mermaid"><pre><code>${token.text}</code></pre></div>`;
            }
          }
        ]
      }
    }
  ]
})
export class ShadowMermaidComponent implements AfterViewInit {
  markdownContent = `
# ShadowDom下的Mermaid测试
\`\`\`mermaid
flowchart LR
A[Angular ShadowDom] --> B[Mermaid渲染]
B --> C[手动触发]
C --> D[成功显示图表]
\`\`\`
`;

  constructor(private elRef: ElementRef) {}

  async ngAfterViewInit() {
    // 初始化Mermaid,关闭自动加载,设置安全级别(根据需求调整)
    await mermaid.initialize({ 
      startOnLoad: false, 
      securityLevel: 'loose',
      theme: 'default'
    });

    const shadowRoot = this.elRef.nativeElement.shadowRoot;
    if (!shadowRoot) return;

    // 查找ShadowRoot内所有带mermaid类的容器
    const mermaidContainers = shadowRoot.querySelectorAll('.mermaid');

    for (const container of Array.from(mermaidContainers)) {
      try {
        // 提取代码内容
        const codeElement = container.querySelector('code');
        if (!codeElement?.textContent) continue;

        const code = codeElement.textContent.trim();
        // 生成唯一ID避免冲突
        const renderId = `mermaid-${Date.now()}-${Math.random().toString(36).slice(2)}`;
        
        // 调用Mermaid渲染方法
        const { svg } = await mermaid.render(renderId, code);
        // 替换容器内容为生成的SVG
        container.innerHTML = svg;
      } catch (err) {
        console.error('Mermaid渲染失败:', err);
        container.innerHTML = `<div style="color: #dc2626; padding: 8px; border: 1px solid #fecdd3; border-radius: 4px;">
          Mermaid渲染错误: ${(err as Error).message}
        </div>`;
      }
    }
  }
}

组件HTML模板

<markdown [content]="markdownContent"></markdown>

关键注意点

  • ShadowRoot获取:必须通过组件的nativeElement.shadowRoot访问,不能用全局document。
  • 代码块识别:确保Markdown组件生成的Mermaid代码块带有.mermaid类,方便后续查找(如果使用的marked-code-preview默认已处理,可跳过自定义扩展)。
  • 异步处理:Mermaid的render是异步方法,需用async/await确保渲染完成后再替换DOM内容。
  • 唯一ID:每个Mermaid元素需要唯一ID,避免渲染冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 02:07:25