Javascript: Eval внешний скрипт в определенном пространстве имен - PullRequest
0 голосов
/ 27 марта 2019

Извините за эссе, не могу найти краткий способ объяснить это: (

Context

В настоящее время я создаю WebPart для Office 365, который во многом напоминает клиентское приложение, которое пользователи могут добавлять на свою страницу и настраивать с помощью пользовательского интерфейса.

WebPart отображает некоторый контент, и я хотел бы, чтобы пользователи могли предоставлять внешний скрипт, который мог бы запускаться ПОСЛЕ процесса рендеринга. Пока это легко, я мог бы просто сделать что-то вроде этого в коде WebPart:

// Render the WebPart
this.render();

// Load external script
this.loadExternalScript(this.props.externalScriptUrl);

Проблема в том, что я хотел бы, чтобы внешний сценарий действовал как обратный вызов, который мой WebPart мог бы вызывать для предоставления некоторого контекста.

Решение 1

Первым решением, которое я нашел, было предоставление пользователям рекомендаций о том, как создать их внешний скрипт с использованием определенного пространства имен на основе имени файла и точной функции, чтобы мой WebPart мог:

  • Рендер
  • Загрузить внешний скрипт (который заполнил бы это конкретное пространство имен)
  • Получить пространство имен внешнего скрипта (на основе имени файла)
  • Вызвать обратный вызов внешнего скрипта, используя пространство имен, и предоставить контекст

Это хорошо работает и выглядит так:

MyExternalScript.js

MyNamespace.ExternalScripts.MyExternalScript = {

    onPostRender: function(wpContext) {
        console.log(wpContext);
    }
}

WebPart

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

Еще одним решением, о котором я подумал, было следующее:

  • Удалить классное пространство имен из внешнего скрипта и оставить подпись определенной функции
  • Загрузка содержимого скрипта в виде строки
  • Пусть WebPart динамически создает уникальное имя пространства имен для этого конкретного сценария
  • Предварительно добавить содержимое сценария к пространству имен
  • eval () все это
  • Обратный звонок

Это будет выглядеть следующим образом:

MyExternalScript.js

onPostRender: function(wpContext) {
    console.log(wpContext);
}

WebPart

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

Я провел небольшое быстрое тестирование, и похоже, что оно работает, тот факт, что WebPart динамически генерирует пространство имен, намного лучше, чем запрос пользователя о соответствии сложному пространству имен, однако я не уверен, что есть лучшее решение, чем используя eval ().

Все, что мне нужно в конце дня, - это найти способ, чтобы мой WebPart «знал» о обратном вызове, который ему нужен для вызова. Я также должен убедиться, что пространство имен уникально для WebPart и скриптов, поскольку на одной странице может быть 4 WebParts, загружающих разные скрипты, поэтому я должен избегать конфликтов пространства имен любой ценой.

У кого-нибудь есть идея получше?

Спасибо!

Ответы [ 2 ]

0 голосов
/ 27 марта 2019

Итак, как объяснено в моем комментарии, основная цель состоит в том, чтобы иметь возможность выполнить внешний скрипт, который имеет доступ к «магической» переменной «wpContext», предоставляемой WebPart.

Время, в котороевнешний сценарий вызывается / оценивается не очень важен, так как WebPart должен решить, когда он хочет вызвать его.

Следуя вашему примеру, я думаю, это будет выглядеть примерно так:

WebPart

// Render the WebPart
this.render();

// Load the external script
var runExternalScript = Function('wpContext', `
  // External script content
  console.log('External script is able to use the WebPart context!');
  console.log(wpContext);
`);

// Run the external script while providing the current WebPart's context
runExternalScript(this.wpcontext)

Это гораздо более элегантно, чем мой eval (), потому что у меня нет пространства имен для генерации, а внешний сценарий даже не должен соответствоватьсигнатура конкретной функции, чтобы WebPart мог получить ее, он может просто использовать переменную "wpContext" прямо в своем содержимом.

Так что даже если это выглядит как лучшее решение, я упускаю другое решение, которое быне требует использования eval / Function или это в значительной степени путь?

Спасибо!

0 голосов
/ 27 марта 2019

Я не совсем понимаю контекст здесь, но как насчет передачи функции пост-рендеринга в качестве обратного вызова?

var runExternalScript = Function('onPostRender', `
// your external script
  console.log('rendering...');
  console.log('rendering finished');
  if (onPostRender) onPostRender();
`)

function postCallback(){ console.log('finished!') }

runExternalScript(postCallback)
...