Оценить вызов функции в виде строки, которая была объявлена с помощью 'require'

1

Мне нужно использовать функцию, к которой у меня есть доступ только в кавычках, поэтому это строка.

Допустим, функция называется "м". Функция представляет собой пакет NPM. Я импортирую его в свой проект с помощью 'require'.

Предположим, что строка дана мне другой функцией. Поэтому у меня нет возможности просто удалить кавычки.

const m = require('mithril')
let vnode = "m('p', 'text')" // I need this string as a function

Я сделал свой поиск по сети и придумал два ответа. Использование eval() или Function().

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

И Function() действует странно. Когда я делаю Function("m('p', 'text')"), он возвращает это (в виде строки):

function anonymous( ) { m("p", "text" ) }

... и когда я называю это Function("m('p', 'text')")(), она возвращает ошибку, говоря:

ReferenceError: m is not defined

Я попытался снова require mithril модуль в вызове Function(), но на этот раз он выдает ошибку о том, что require не определен. Я предполагаю, что это потому, что Function происходит из другой среды, а не из Nodejs.

Я просто ожидаю поведение функции eval, без страха быть взломанным.

Есть идеи?

РЕДАКТИРОВАТЬ (пояснить далее):

Я заметил, что спрашиваю Y в проблеме X/Y. Вот Х моей проблемы.

Я развертываю одностраничное приложение на страницах Github с помощью Mithril JS. (мой репо)

Там мне нужно визуализировать математические выражения, в которые входит KaTeX.

KaTeX поставляется с простым API, renderToString() возвращает HTML-элемент, который визуализируется в хорошо визуализированное математическое выражение.

С другой стороны, Mithril JS предоставляет VNodes, специальную функцию для описания HTML-тегов. Пример:

<div class="cont">
    <p>
        Text
    </p>
</div>

... при преобразовании в vnodes...

m('div', {class:'cont'}, [
    m('p', 'Text')
])

Теперь, чтобы Mithril правильно просматривал HTML-вывод KaTeX, тег html должен быть переведен во Vnodes. Я нашел конвертер здесь

Я добавил код конвертера в свой проект и импортировал его как функцию. Проблема, с которой я здесь сталкиваюсь, заключается в том, что конвертер прекрасно конвертирует HTML-структуры в VNodes, но возвращает их в виде строки в кавычках...

Решения, которые я могу придумать:

  • Добавление еще одного пакета в мой проект, который позволяет Mithril работать с синтаксисом JSX, где JS может быть написан с добавлением HTML-тегов. Это будет моим последним средством, но мне лично не нравится синтаксис JSX, поскольку это всего лишь компромисс.
  • Использование функции eval(), которая делает то, что я хочу, но проблемы с безопасностью печально известны.

Есть идеи другие?

Теги:

3 ответа

1

Я прочитал ваш пост редактирования и не могу не задаться вопросом, почему вы должны конвертировать html в vnode, вместо того, чтобы передавать html напрямую в визуализированный vnode?

После монтирования мифрилового компонента он вернет DOM-узел в oncreate жизненного цикла oncreate. Таким образом, вы можете написать функцию mathRenderer следующим образом:

const renderMath = mathHTML => ({
  oncreate: ({ dom }) => {
    dom.innerHTML = mathHTML;
  },
  view: vnode => m("div", "math expression")
});

Здесь демо с katex.

  • 0
    Спасибо! Это очень полезный ответ. Я продолжаю изучать мифрил, но довольно часто теряюсь в понятиях структуры SPA. Такие как методы жизненного цикла и их связь с dom, виртуальный dom и способ обновления dom при изменении маршрута и т. Д. Спасибо за указание на это. Тем не менее, я не мог видеть демо, работающее в codeandbox, не могли бы вы проверить это? (Я вижу пустую страницу в разделе рендеринга)
  • 0
    @hrkucuk Нет проблем! CodeSandbox работает на моем конце, но, чтобы убедиться, что я развернул здесь код mithril-katex-example.netlify.com из этого репозитория github.com/d4rekanguok/so-mithril-katex-example . Дайте мне знать, если это все еще не работает!
Показать ещё 4 комментария
1

Вероятно, было бы более элегантно преобразовать строку eval -like в массив параметров, а затем вызвать m с этими параметрами. Например, если параметры всегда являются строками, разделенными символами ' s:

// const m = require('mithril')
// for demonstration purposes:
const m = (...args) => console.log('called with ' + args.join(','));

const vnode = "m('p', 'text')"
let match;
const pattern = /'([^']+)'/g;
const args = [];
while (match = pattern.exec(vnode)) {
  args.push(match[1]);
}
m.apply(undefined, args);

Если имя функции также может меняться, то вместо отдельной переменной используйте объект с ключом m:

// const m = require('mithril')
// for demonstration purposes:
const fns = {
  m: (...args) => console.log('called with ' + args.join(','))
};

const vnode = "m('p', 'text')";
const fnName = vnode.match(/^[^(]+/)[0];
let match;
const pattern = /'([^']+)'/g;
const args = [];
while (match = pattern.exec(vnode)) {
  args.push(match[1]);
}
fns[fnName].apply(undefined, args);
  • 0
    Ваш ответ полезен для определенных случаев, но типичная строка, с которой я собираюсь иметь дело, будет иметь два аргумента, один из которых будет именем тега с классом, то есть span.katex , а другой - список со многими другими функциями m() включая много списков в качестве второго аргумента и так далее. Внутри этих вложенных списков функций m() некоторые из них имеют вышеупомянутые списки в качестве третьего аргумента, а вторым аргументом является JSON, описывающий атрибуты и значения тегов HTML. Вы можете перейти к следующим фламам, чтобы увидеть типичный vnode, с которым мне нужно иметь дело. Есть другие идеи?
  • 0
    Это довольно сложно. Разбор всех вложенных вызовов будет очень утомительным (не невозможным, но проблемой X / Y), есть ли шанс исправить ввод, чтобы послать более управляемый формат, такой как JSON, а не строку, чтобы попытаться eval ? (Ваша ссылка, кажется, не работает, лучше всего разместить информацию в самом вопросе)
Показать ещё 1 комментарий
0

Поскольку моя проблема может иметь много решений на разных этапах, вот как решить ReferenceError: m is not defined проблема.

Как это описано в MDN, существует проблема закрытия с функцией Function. Чтобы обойти это, можно указать параметр для Function и передать переменную m (которая определена только в локальной области видимости) в качестве аргумента.

const m = require('mithril');
const f = new Function("m", "return m('p', {class:'red'}, 'text')");
f(m);

Большое спасибо Сами Хульт, который ответил в оригинальном месте: qaru.site/questions/16847592/...

Ещё вопросы

Сообщество Overcoder
Наверх
Меню