如何使用 <script> 标签按顺序将脚本插入页面上下文

How to sequentially insert scripts into the page context using <script> tags

我无法加载我插入到页面上下文中的所有脚本,这些脚本来自 content_script 脚本,带有 <script> 标记,因为它们需要以正确的加载顺序执行,因为有些人依赖于其他人。在一个实际的 HTML 文件中,我猜有一个队列来加载文件,但是插入 <script> 标签似乎如果一个脚本延迟了一点时间,下一个开始加载然后立即执行尽管它必须等待仍在加载的依赖库。

下面是由于x-tag-core.min.jsprimeui-all.min.js之前加载导致错误的网络输出eventPage.js 使用 jquery-ui.min.js 在它之前加载:

// manifest.js
"content_scripts": [
{
  "matches": [
    "<all_urls>"
  ],
  "js": [
    "js/jquery-3.1.1.min.js",
    "js/main.js"
  ]
}
]
// main.js
var s = document.createElement('script');
s.src = chrome.extension.getURL('js/jquery-3.1.1.min.js');
$(document.head).append(s);

s = document.createElement('script');
s.src = chrome.extension.getURL('js/jquery-ui.min.js');
$(document.head).append(s);

s = document.createElement('script');
s.src = chrome.extension.getURL('js/primeui-all.min.js');
$(document.head).append(s);

s = document.createElement('script');
s.src = chrome.extension.getURL('js/x-tag-core.min.js');
$(document.head).append(s);

s = document.createElement('script');
s.src = chrome.extension.getURL('js/primeelements.min.js');
$(document.head).append(s);

s = document.createElement('script');
s.src = chrome.extension.getURL('js/eventPage.js');
$(document.head).append(s);

在片段中复制您的问题

以下代码片段通过顺序插入 <script> 个元素来重复您的问题。我正在使用网络资源,因为在 Stack Overflow 上没有存储此类脚本的方法。鉴于您只提供了 jQuery 的版本信息,我不得不猜测您正在使用的其他库的适当版本。

为了接近您在 Chrome 扩展中使用的代码,chrome.extension.getURL() 是伪造的。在这些片段中,该函数 returns 是您正在使用的库的功能网络 URL。显然,在您的 Chrome 扩展中,您将希望继续使用包含在您的扩展中的库文件。

此外,eventPage.js 的代码是通过一些代码伪造的,如果 jQuery 存在,如果 $(document).puidialog 是一个函数,and/or 如果 xtag 被定义。您看到的错误是 $([something]).puidialog 不是函数并且 xtag 未定义。此 fakeEventPageJS 代码准确显示脚本是否已正确加载。

在这个重复的问题中,各种库也会产生错误,因为后续库找不到先前的库。

var s = document.createElement('script');
s.src = chrome.extension.getURL('js/jquery-3.1.1.min.js');
document.head.appendChild(s);

s = document.createElement('script');
s.src = chrome.extension.getURL('js/jquery-ui.min.js');
document.head.appendChild(s);

s = document.createElement('script');
s.src = chrome.extension.getURL('js/primeui-all.min.js');
document.head.appendChild(s);

s = document.createElement('script');
s.src = chrome.extension.getURL('js/x-tag-core.min.js');
document.head.appendChild(s);

s = document.createElement('script');
s.src = chrome.extension.getURL('js/primeelements.min.js');
document.head.appendChild(s);

s = document.createElement('script');
//s.src = chrome.extension.getURL('js/eventPage.js');
//Fake js/eventPage.js with an actual script.
s.textContent = fakeEventPageJS;
document.head.appendChild(s);
<!-- The JavaScript code included in this HTML section is used to fake the chrome API
     and part of faking the existence of a js/eventPage.js file by inserting code. -->
    
<!-- Using HTML <script> tags to test that loading it via these works prior to testing
       using JavaScript inserts. If you want to verify that the code works when the
       scripts are included in the original HTML, you can uncomment the <script>
       tags here. -->

<!-- jquery-3.1.1.min.js -->
<!-- <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script><!--  -->
<!-- jquery-ui.min.js -->
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
<!-- <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script><!--  -->

<!-- primeui-all.min.js -->
<link rel="stylesheet" href="https://cdn.rawgit.com/primefaces/primeui-distribution/master/primeui-all.min.css">
<!-- <script src="https://cdn.rawgit.com/primefaces/primeui-distribution/master/primeui-all.min.js"></script><!--  -->

<!-- x-tag-core.min.js -->
<!-- <script src="https://cdn.rawgit.com/x-tag/core/master/dist/x-tag-core.min.js"></script>

<!-- primeelements.min.js -->
<!-- <script src="https://cdn.rawgit.com/primefaces/primeui-distribution/master/primeelements.min.js"></script> <!-- -->

<script>
  //Fake chrome.extension.getURL
  var netScriptLoations = {
    'js/jquery-3.1.1.min.js':'https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js',
    'js/jquery-ui.min.js':'https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js',
    'js/primeui-all.min.js':'https://cdn.rawgit.com/primefaces/primeui-distribution/master/primeui-all.min.js',
    'js/x-tag-core.min.js':'https://cdn.rawgit.com/x-tag/core/master/dist/x-tag-core.min.js',
    'js/primeelements.min.js':'https://cdn.rawgit.com/primefaces/primeui-distribution/master/primeelements.min.js'
}
if(typeof chrome !== 'object'){
  var chrome = {};
}
if(typeof chrome.extension !== 'object'){
  chrome.extension = {};
}
if(typeof chrome.extension.getURL !== 'function'){
  chrome.extension.getURL = function(script){
    //console.log(netScriptLoations[script]);
    return netScriptLoations[script];
  };
}

var fakeEventPageJS = 
      'var testResult = "Scripts did NOT load correctly. "' 
    + '               + "$(document).puidialog is NOT a function.";'
    + 'var passedChecks=0;'
    + 'if(typeof $ === "function"){'
    + '    var puidialogNot = " NOT";'
    + '    if(typeof $(document).puidialog === "function") {'
    + '        puidialogNot = "";'
    + '    }'
    + '    console.log("$(document).puidialog is" + puidialogNot + " a function");'
    + '} else {'
    + '    console.log("No jQuery");'
    + '}'
    + 'var xtagNot = " NOT";'
    + 'if(typeof xtag !== "undefined") {'
    + '    xtagNot = "";'
    + '}'
    + 'console.log("xtag is" + xtagNot + " defined.");'
    + 'if(puidialogNot + xtagNot === "") {'
    + '    testResult = "Scripts loaded CORRECTLY. "' 
    + '}'
    + 'console.log(testResult);';
</script>

将每个 <script> 插入先前脚本的 onload 事件处理程序中

很明显插入的<script>个元素是异步执行的。为了强制它们同步执行,我们需要在前一个脚本执行完毕后插入下一个脚本。这可以通过为每个脚本使用 load event 来完成。

以下代码将每个后续脚本加载到先前脚本的 load 事件处理程序中。

函数 createScriptElement 创建一个单独的 <script> 元素。由于不需要伪造 eventPage.js 脚本,因此可以在您的代码中稍微简化此函数。

函数 createScriptSequence 创建了一系列 <script> 元素,每个元素都将下一个脚本插入其 onload 侦听器中。这使用 script.addEventListerner('load',...) 以避免正在加载的脚本更改 script.onload property/attribute.

var scriptsToInsert = [
    'js/jquery-3.1.1.min.js',
    'js/jquery-ui.min.js',
    'js/primeui-all.min.js',
    'js/x-tag-core.min.js',
    'js/primeelements.min.js',
    'js/eventPage.js'
]

function createScriptElement(script){
    let scriptEl = document.createElement('script');
    let scriptElSource = chrome.extension.getURL(script);
    if(scriptElSource){
        scriptEl.src = scriptElSource;
    } else {
        //Only need this `else` because we are faking having js/eventPage.js by using
        //  some code to indicate if $(document).puidialog is a function.
        scriptEl.textContent = fakeEventPageJS;
    }
    return scriptEl;
}

function createScriptSequence(scriptArray){
    var scriptEls = [];
    //Create all the script elements
    scriptArray.forEach((script,index)=>{
        //console.log(script);
        scriptEls.push(createScriptElement(script));
        if(index>0){
            //Add an onload listener for each script (except the last) which loads
            //  the next one in the sequence.
            scriptEls[index - 1].addEventListener('load',function oneTime(){
                //Probably don't need to remove this, but better to clean things up.
                scriptEls[index - 1].removeEventListener('load',oneTime,false);
                document.head.appendChild(scriptEls[index]);
            },false);
        }
    });
    //Return the first script in the sequence
    return scriptEls[0];
}

document.head.appendChild(createScriptSequence(scriptsToInsert));
<!-- The JavaScript code included in this HTML section is used to fake the chrome API
     and part of faking the existence of a js/eventPage.js file by inserting code. -->

<!-- jquery-ui.min.js -->
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
<!-- primeui-all.min.js -->
<link rel="stylesheet" href="https://cdn.rawgit.com/primefaces/primeui-distribution/master/primeui-all.min.css">

<script>
  //Fake chrome.extension.getURL
  var netScriptLoations = {
    'js/jquery-3.1.1.min.js':'https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js',
    'js/jquery-ui.min.js':'https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js',
    'js/primeui-all.min.js':'https://cdn.rawgit.com/primefaces/primeui-distribution/master/primeui-all.min.js',
    'js/x-tag-core.min.js':'https://cdn.rawgit.com/x-tag/core/master/dist/x-tag-core.min.js',
    'js/primeelements.min.js':'https://cdn.rawgit.com/primefaces/primeui-distribution/master/primeelements.min.js'
}
if(typeof chrome !== 'object'){
  var chrome = {};
}
if(typeof chrome.extension !== 'object'){
  chrome.extension = {};
}
if(typeof chrome.extension.getURL !== 'function'){
  chrome.extension.getURL = function(script){
    //console.log(netScriptLoations[script]);
    return netScriptLoations[script];
  };
}

var fakeEventPageJS = 
      'var testResult = "Scripts did NOT load correctly. "' 
    + '               + "$(document).puidialog is NOT a function.";'
    + 'var passedChecks=0;'
    + 'if(typeof $ === "function"){'
    + '    var puidialogNot = " NOT";'
    + '    if(typeof $(document).puidialog === "function") {'
    + '        puidialogNot = "";'
    + '    }'
    + '    console.log("$(document).puidialog is" + puidialogNot + " a function");'
    + '} else {'
    + '    console.log("No jQuery");'
    + '}'
    + 'var xtagNot = " NOT";'
    + 'if(typeof xtag !== "undefined") {'
    + '    xtagNot = "";'
    + '}'
    + 'console.log("xtag is" + xtagNot + " defined.");'
    + 'if(puidialogNot + xtagNot === "") {'
    + '    testResult = "Scripts loaded CORRECTLY. "' 
    + '}'
    + 'console.log(testResult);';
</script>