Как последовательно вставлять скрипты в контекст страницы с помощью тегов ‹script›

У меня возникают проблемы с загрузкой всех сценариев, которые я вставляю в контекст страницы с тегами <script> из сценария content_script, потому что они должны выполняться в правильном порядке загрузки, так как одни зависят от других. В реальном файле HTML, я думаю, есть очередь для загрузки файлов, но с вставкой тегов <script> кажется, что если один скрипт немного задерживается, следующий начинает загружаться, а затем немедленно выполняется, несмотря на то, что ему пришлось ждать своей зависимости библиотека, которая все еще загружается.

Ниже приведен вывод сети с ошибкой, вызванной тем, что x-tag-core.min.js загружается до primeui-all.min.js и eventPage. js, который использует jquery-ui.min.js, загружается перед ним:

Вывод сети Chrome

// 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);

person another    schedule 11.12.2016    source источник
comment
Также было бы полезно, если бы вы предоставили нам список полных URL-адресов, которые вы использовали для загрузки различных библиотек, которые вы используете, и/или инструкции о том, как их получить. Это сэкономит нам время на их поиск и предотвратит потенциальное использование другой версии этих библиотек. Например: для jQuery у вас есть версия, но для jQueryUI существует множество различных версий и комбинаций параметров.   -  person Makyen♦    schedule 11.12.2016
comment
К вашему сведению: <script> elements, если явно не помечено как async , выполняются в том порядке, в котором они вставлены в DOM. На самом деле они должны извлекаться и выполняться немедленно, до того, как браузер продолжит анализ страницы. Таким образом, они должны выполняться последовательно.   -  person Makyen♦    schedule 11.12.2016
comment
Учитывая, что вы вставляете их как элементы <script> на страницу, вы можете дублировать проблему в среде расширения, отличной от Chrome. Попробуйте фрагмент. Для этого потребуются сетевые URL-адреса для сценариев библиотеки, но это значительно упростит тестирование и поиск решения.   -  person Makyen♦    schedule 11.12.2016
comment
Я вернусь с этим фрагментом, если это возможно.   -  person another    schedule 11.12.2016
comment
К вашему сведению: я думаю, что у меня есть фрагмент, который дублирует проблему. Мне все еще нужно немного поиграть с ним, чтобы определить, действительно ли это так.   -  person Makyen♦    schedule 12.12.2016
comment
ХОРОШО. Я смог продублировать проблему во фрагменте и опубликовал ответ.   -  person Makyen♦    schedule 12.12.2016


Ответы (1)


Дублирование вашей проблемы в фрагменте

Следующий фрагмент дублирует вашу проблему, последовательно вставляя <script> элементов. Я использую сетевые ресурсы, потому что нет способа хранить такие скрипты в Stack Overflow. Учитывая, что вы предоставили только информацию о версии для jQuery, мне пришлось угадывать соответствующие версии для других библиотек, которые вы используете.

Чтобы оставаться близким к коду, который вы используете в своем расширении Chrome, chrome.extension.getURL() подделан. В этих фрагментах эта функция возвращает URL-адрес функциональной сети для библиотек, которые вы используете. Очевидно, что в вашем расширении Chrome вы захотите продолжать использовать файлы библиотеки, которые вы включили в свое расширение.

Кроме того, код для eventPage.js подделывается, так как имеет некоторый код, который сообщает, существует ли jQuery, является ли $(document).puidialog функцией и/или определено ли 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 для каждого скрипта. .

Следующий код загружает каждый последующий сценарий в обработчик событий load предыдущего сценария.

Функция createScriptElement создает отдельный элемент <script>. Эта функция может быть несколько упрощена в вашем коде, поскольку не нужно подделывать сценарий eventPage.js.

Функция createScriptSequence создает последовательность из <script> элементов, каждый из которых вставляет следующий скрипт в свой onload слушатель. Это использует script.addEventListerner('load',...), чтобы быть невосприимчивым к загружаемому сценарию, изменяющему свойство/атрибут script.onload.

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>

person Makyen♦    schedule 11.12.2016
comment
Спасибо за старания ! Я рассмотрю это сегодня вечером. Я работаю с Java и SQL atm! - person another; 12.12.2016
comment
Спасибо за это и за указание моего вопроса здесь, так как это почти работает. Я могу запустить скрипт на главной странице, но код прослушивателя OnLoad, по-видимому, выполняется в контексте расширения, а не в контексте главной страницы. Я обновил свой вопрос (поскольку эти ответы на сообщения настолько ограничены), если вы хотите снова попытаться помочь. - person Scott Gartner; 04.02.2017