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

如何在Web Component中引入CDN脚本?问题排查与解决

集成Summernote的Web Component无法加载外部脚本及初始化问题

我尝试编写了一个集成Summernote的Web Component代码,意图在其中引入组件所需的JS文件,但始终无法正常运行——脚本似乎未加载到DOM中,或加载后无法调用。我既尝试了使用Shadow DOM,也试过不使用,结果一致。以下是我的代码:

class MyEditor extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        this.isOpen = false;
        this.shadowRoot.innerHTML = `
            <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
            <link href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.css" rel="stylesheet">
            <script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.js"></script>
            
        <style>
            
        </style>
        
        <div id="summernote">
            <span style="color:red;background-color: yellow;">example</span>
        </div>
    `;

        const summernote = this.shadowRoot.querySelectorAll('#summernote');
        summernote.summernote({
            placeholder: 'Hello stand alone ui',
            tabsize: 2,
            height: 600,
            toolbar: [
                ['style', ['style']],
                ['font', ['bold', 'underline', 'clear']],
                ['color', ['color']],
                ['para', ['ul', 'ol', 'paragraph']],
                ['table', ['table']],
                ['insert', ['link', 'picture', 'video']],
                ['view', ['fullscreen', 'codeview', 'help']]
            ],
            disableResizeEditor: true
        });
}

customElements.define('my-editor', MyEditor);

所有引入的文件均未加载,导致功能无法使用,请问该如何解决此问题?

另外,相同的CDN配置在静态HTML页面中可正常运行,代码如下:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>without bootstrap</title>

    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.js"></script>
</head>
<body>

    <div id="summernote">
        <span style="color:red;background-color: yellow;">1234</span>
    </div>

<script>
    $('#summernote').summernote({
        placeholder: 'Hello stand alone ui',
        tabsize: 2,
        height: 600,
        toolbar: [
            ['style', ['style']],
            ['font', ['bold', 'underline', 'clear']],
            ['color', ['color']],
            ['para', ['ul', 'ol', 'paragraph']],
            ['table', ['table']],
            ['insert', ['link', 'picture', 'video']],
            ['view', ['fullscreen', 'codeview', 'help']],
        ],
        disableResizeEditor: true
    });

</script>
</body>
</html>

解决方案

核心问题分析

  1. Shadow DOM的innerHTML不解析脚本:浏览器出于安全机制,不会执行通过innerHTML插入到Shadow DOM中的<script>标签,这是脚本完全未加载的根本原因。
  2. 初始化时机错误:即使脚本能加载,在constructor中立刻调用summernote()时,脚本还未完成加载,必然抛出未定义错误。
  3. 元素选择错误:querySelectorAll返回的是NodeList集合,不是单个DOM元素,无法直接调用jQuery的方法。

修复后的完整代码

class MyEditor extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        this.isOpen = false;
    }

    connectedCallback() {
        // 加载Summernote样式到Shadow DOM
        const styleLink = document.createElement('link');
        styleLink.href = 'https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.css';
        styleLink.rel = 'stylesheet';
        this.shadowRoot.appendChild(styleLink);

        // 创建编辑器容器
        const editorDiv = document.createElement('div');
        editorDiv.id = 'summernote';
        editorDiv.innerHTML = '<span style="color:red;background-color: yellow;">example</span>';
        this.shadowRoot.appendChild(editorDiv);

        // 按顺序加载jQuery和Summernote脚本,用Promise保证依赖顺序
        this.loadExternalScript('https://code.jquery.com/jquery-3.4.1.slim.min.js')
            .then(() => this.loadExternalScript('https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.js'))
            .then(() => this.initSummernote())
            .catch(error => console.error('资源加载失败:', error));
    }

    // 封装脚本加载方法,返回Promise便于异步控制
    loadExternalScript(src) {
        return new Promise((resolve, reject) => {
            const script = document.createElement('script');
            script.src = src;
            script.onload = () => resolve();
            script.onerror = () => reject(new Error(`加载脚本失败: ${src}`));
            // 脚本加载到全局文档,因为Summernote依赖全局的jQuery对象
            document.head.appendChild(script);
        });
    }

    // 初始化Summernote编辑器
    initSummernote() {
        // 手动获取Shadow DOM内的元素,再传入jQuery
        const editorElement = this.shadowRoot.getElementById('summernote');
        $(editorElement).summernote({
            placeholder: 'Hello stand alone ui',
            tabsize: 2,
            height: 600,
            toolbar: [
                ['style', ['style']],
                ['font', ['bold', 'underline', 'clear']],
                ['color', ['color']],
                ['para', ['ul', 'ol', 'paragraph']],
                ['table', ['table']],
                ['insert', ['link', 'picture', 'video']],
                ['view', ['fullscreen', 'codeview', 'help']]
            ],
            disableResizeEditor: true
        });
    }
}

customElements.define('my-editor', MyEditor);

关键修复细节

  • 使用connectedCallback初始化:这个回调在元素插入到DOM后触发,是Web Component中做资源加载和初始化的正确时机,避免constructor中DOM未就绪的问题。
  • 手动创建脚本元素:通过document.createElement('script')创建脚本并添加到全局document.head,确保脚本能被浏览器执行,同时用Promise严格保证jQuery先于Summernote加载。
  • 正确访问Shadow DOM元素:jQuery默认只能查询全局DOM,需要手动获取Shadow DOM内的元素后再传给jQuery实例化编辑器。
  • 样式隔离:CSS链接直接插入Shadow DOM,样式只会作用于组件内部,不会污染全局样式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 17:05:22