Javascript: внешний скрипт Eval в определенном пространстве имен

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

Контекст

В настоящее время я создаю веб-часть для 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

Другое решение, о котором я думал, состояло в том, чтобы сделать следующее:

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

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

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 веб-части, загружающих разные сценарии, поэтому я должен любой ценой избегать конфликтов пространств имен.

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

Спасибо!

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
526
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

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

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

runExternalScript(postCallback)

На самом деле мне нравится эта идея с функцией, в моем случае все наоборот: у пользователя нет доступа к коду WebPart, он может только предоставить URL-адрес для своего внешнего скрипта, и это не внешний скрипт, который должен иметь возможность вызовите onPostRender, определенный в веб-части, на самом деле все наоборот, веб-часть должна вызывать onPostRender, определенный во внешнем скрипте. Но ваш пример все еще актуален, я отвечу, как я мог бы его использовать, как только я буду на компьютере (сейчас мобильный телефон) спасибо

Simon-Pierre Plante 27.03.2019 12:30

Итак, как объясняется в моем комментарии, основная цель состоит в том, чтобы иметь возможность выполнять внешний скрипт, который имеет доступ к «волшебной» переменной «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, или это в значительной степени путь?

Спасибо!

Другие вопросы по теме