Извините за эссе, не смог найти короткий способ объяснить это :(
Контекст
В настоящее время я создаю веб-часть для Office 365, которая очень похожа на клиентское приложение, которое пользователи могут добавлять на свою страницу и настраивать через пользовательский интерфейс.
WebPart отображает некоторый контент, и я хотел бы, чтобы пользователи могли предоставить внешний скрипт, который мог бы выполняться ПОСЛЕ процесса рендеринга. Пока это легко, я мог бы просто сделать что-то подобное в коде WebPart:
// Render the WebPart
this.render();
// Load external script
this.loadExternalScript(this.props.externalScriptUrl);
Проблема в том, что я хотел бы, чтобы внешний скрипт действовал как обратный вызов, который моя веб-часть могла бы вызвать, чтобы предоставить ему некоторый контекст.
Решение 1
Первое решение, которое я нашел, состояло в том, чтобы предоставить пользователям руководство по созданию внешнего сценария с использованием определенного пространства имен на основе имени файла и точной функции, чтобы моя веб-часть могла:
Это работает хорошо, и это выглядит так:
MyExternalScript.js
MyNamespace.ExternalScripts.MyExternalScript = {
onPostRender: function(wpContext) {
console.info(wpContext);
}
}
Веб-часть
// Render the WebPart
this.render();
// Load external script
this.loadExternalScript(this.props.externalScriptUrl);
// Calls the script callback if available
var scriptNamespace = MyNamespace.ExternalScripts.MyExternalScript;
var scriptCallback = scriptNamespace ? scriptNamespace.onPostRender : null;
if (scriptCallback) {
scriptCallback(this.wpContext);
}
Это прекрасно работает, но построение пространства имен на основе имени файла — довольно схематичная вещь, чтобы просить пользователей сделать ее, и я хотел бы найти что-то более прямолинейное, чем это хакерское решение.
Решение 2
Другое решение, о котором я думал, состояло в том, чтобы сделать следующее:
Это выглядело бы следующим образом:
MyExternalScript.js
onPostRender: function(wpContext) {
console.info(wpContext);
}
Веб-часть
// Render the WebPart
this.render();
// Load external script content
$scriptContent = this.readExternalScript(this.props.externalScriptUrl);
// Append unique namespace
$scriptContent = "MyNamespace.ExternalScripts.MyExternalScript = {" + $scriptContent + "}";
// Eval everything within that namespace
eval($scriptContent);
// Calls the script callback if available
var scriptNamespace = MyNamespace.ExternalScripts.MyExternalScript;
var scriptCallback = scriptNamespace ? scriptNamespace.onPostRender : null;
if (scriptCallback) {
scriptCallback(this.wpContext);
}
Я провел быстрое тестирование, и похоже, что оно работает, тот факт, что веб-часть динамически генерирует пространство имен, намного лучше, чем просить пользователя соблюдать сложное пространство имен, однако я не уверен, что есть лучшее решение, чем использование eval( ).
Все, что мне нужно в конце дня, — это найти способ заставить мою веб-часть «осведомляться» о обратном вызове, который ей нужно вызвать. Я также должен убедиться, что пространство имен является уникальным для WebPart и уникально для сценария, поскольку на одной странице может быть 4 веб-части, загружающих разные сценарии, поэтому я должен любой ценой избегать конфликтов пространств имен.
У кого-нибудь есть идея получше?
Спасибо!



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я не совсем понимаю контекст здесь, но как насчет передачи функции пост-рендеринга в качестве обратного вызова?
var runExternalScript = Function('onPostRender', `
// your external script
console.info('rendering...');
console.info('rendering finished');
if (onPostRender) onPostRender();
`)
function postCallback(){ console.info('finished!') }
runExternalScript(postCallback)
Итак, как объясняется в моем комментарии, основная цель состоит в том, чтобы иметь возможность выполнять внешний скрипт, который имеет доступ к «волшебной» переменной «wpContext», предоставляемой веб-частью.
Время, когда внешний скрипт вызывается/оценивается, на самом деле не важно, так как именно WebPart решает, когда он хочет его вызвать.
Следуя вашему примеру, я думаю, что это будет выглядеть примерно так:
Веб-часть
// Render the WebPart
this.render();
// Load the external script
var runExternalScript = Function('wpContext', `
// External script content
console.info('External script is able to use the WebPart context!');
console.info(wpContext);
`);
// Run the external script while providing the current WebPart's context
runExternalScript(this.wpcontext)
Это намного элегантнее, чем мой eval(), потому что у меня нет никакого пространства имен для создания, и внешнему сценарию даже не нужно соответствовать определенной сигнатуре функции, чтобы веб-часть могла ее получить, он может просто использовать Переменная «wpContext» прямо в ее содержимом.
Так что, даже если это выглядит как лучшее решение, я упускаю другое решение, которое не требует использования eval/Function, или это в значительной степени путь?
Спасибо!
На самом деле мне нравится эта идея с функцией, в моем случае все наоборот: у пользователя нет доступа к коду WebPart, он может только предоставить URL-адрес для своего внешнего скрипта, и это не внешний скрипт, который должен иметь возможность вызовите onPostRender, определенный в веб-части, на самом деле все наоборот, веб-часть должна вызывать onPostRender, определенный во внешнем скрипте. Но ваш пример все еще актуален, я отвечу, как я мог бы его использовать, как только я буду на компьютере (сейчас мобильный телефон) спасибо