У меня есть очень небольшое подмножество Markdown вместе с некоторыми пользовательскими html, которые я хотел бы проанализировать в компонентах React. Например, я хотел бы включить эту строку:
hello *asdf* *how* _are_ you !doing! today
В следующий массив:
[ "hello ", <strong>asdf</strong>, " ", <strong>how</strong>, " ", <em>are</em>, " you ", <MyComponent onClick={this.action}>doing</MyComponent>, " today" ]
и затем верните его из функции рендеринга React (React правильно отобразит массив как отформатированный HTML)
По сути, я хочу дать пользователям возможность использовать очень ограниченный набор Markdown, чтобы превратить их текст в стилизованные компоненты (и в некоторых случаях мои собственные компоненты!)
Неразумно опасаться SetInnerHTML, и я не хочу вводить внешние зависимости, потому что все они очень тяжелые, и мне нужны только самые базовые функциональные возможности.
В настоящее время я делаю что-то вроде этого, но это очень хрупко, и не работает для всех случаев. Мне было интересно, если есть лучший способ:
function matchStrong(result, i) {
let match = result[i].match(/(^|[^\\])\*(.*)\*/);
if (match) { result[i] = <strong key={"ms" + i}>{match[2]}</strong>; }
return match;
}
function matchItalics(result, i) {
let match = result[i].match(/(^|[^\\])_(.*)_/); // Ignores \_asdf_ but not _asdf_
if (match) { result[i] = <em key={"mi" + i}>{match[2]}</em>; }
return match;
}
function matchCode(result, i) {
let match = result[i].match(/(^|[^\\])```\n?([\s\S]+)\n?```/);
if (match) { result[i] = <code key={"mc" + i}>{match[2]}</code>; }
return match;
}
// Very brittle and inefficient
export function convertMarkdownToComponents(message) {
let result = message.match(/(\\?([!*_`+-]{1,3})([\s\S]+?)\2)|\s|([^\\!*_`+-]+)/g);
if (result == null) { return message; }
for (let i = 0; i < result.length; i++) {
if (matchCode(result, i)) { continue; }
if (matchStrong(result, i)) { continue; }
if (matchItalics(result, i)) { continue; }
}
return result;
}
Вот мой предыдущий вопрос, который привел к этому.
font _italic *and bold* then only italic_ and normal
? Каков будет ожидаемый результат? Или это никогда не будет вложенным?asdf*
без ее исчезновения)Ответы:
Как это работает?
Он работает путем чтения фрагмента строки за фрагментом, что может быть не лучшим решением для действительно длинных строк.
Всякий раз, когда анализатор обнаруживает, что читается критический фрагмент, т.
'*'
Е. Или любой другой тег уценки, он начинает анализировать фрагменты этого элемента, пока анализатор не найдет свой закрывающий тег.Он работает на многострочных строках, см. Код, например.
Предостережения
Вы не указали, или я мог бы неправильно понять ваши потребности, если есть необходимость проанализировать теги, выделенные жирным шрифтом и курсивом , мое текущее решение может не сработать в этом случае.
Однако, если вам нужно работать с вышеуказанными условиями, просто прокомментируйте здесь, и я настрою код.
Первое обновление: настройка тегов уценки
Теги больше не жестко закодированы, они представляют собой карту, которую вы можете легко расширить в соответствии со своими потребностями.
Исправлены ошибки, о которых вы упоминали в комментариях, спасибо за указание на эти проблемы = p
Второе обновление: многократные теги уценки
Самый простой способ добиться этого: замена многомерных символов на редко используемый Юникод
Хотя метод
parseMarkdown
еще не поддерживает теги с несколькими длинами, мы можем легко заменить эти теги с несколькими длинами на простыеstring.replace
при отправке нашегоrawMarkdown
реквизита.Чтобы увидеть пример этого на практике, посмотрите на
ReactDOM.render
, расположенный в конце кода.Даже если ваше приложение делает поддержку нескольких языков, есть недопустимые символы Юникода , что JavaScript все еще обнаруживает, напр .:
"\uFFFF"
не является допустимым юникода, если я правильно помню, но JS все равно будет иметь возможность сравнить его ("\uFFFF" === "\uFFFF" = true
)Сначала это может показаться хакерским, но, в зависимости от вашего варианта использования, я не вижу каких-либо серьезных проблем при использовании этого маршрута.
Еще один способ достижения этого
Ну, мы могли бы легко отследить последние
N
(гдеN
соответствует длине самого длинного тега с длинной длиной) фрагменты.Было бы необходимо внести некоторые изменения в
parseMarkdown
поведение метода внутри цикла , т. Е. Проверить, является ли текущий фрагмент частью тега с длинной длиной, если он используется в качестве тега; в противном случае, в таких случаях``k
, нам нужно пометить его какnotMultiLength
или что-то похожее и вставить этот кусок как контент.Код
Ссылка на код (TypeScript) https://codepen.io/ludanin/pen/GRgNWPv
Ссылка на код (vanilla / babel) https://codepen.io/ludanin/pen/eYmBvXw
источник
This must be *bold*
наThis must be *bo_ld*
. Это приводит к тому, что полученный HTML будет искаженПохоже, вы ищете небольшое очень простое решение. Не "супер-монстры", как
react-markdown-it
:)Я бы хотел порекомендовать вам https://github.com/developit/snarkdown, который выглядит довольно легким и красивым! Просто 1 КБ и очень просто, вы можете использовать его и расширить его, если вам нужны другие синтаксические функции.
Список поддерживаемых тегов https://github.com/developit/snarkdown/blob/master/src/index.js#L1
Обновить
Просто заметил про реагирующие компоненты, пропустил это в начале. Так что это здорово для вас, я полагаю, взять библиотеку в качестве примера и реализовать ваши собственные необходимые компоненты, чтобы сделать это без опасной настройки HTML. Библиотека довольно маленькая и понятная. Веселитесь с этим! :)
источник
Результат:
Результат теста на регулярное выражение
Объяснение:
Вы можете определить свои теги в этом разделе: как
[*|!|_]
только один из них будет сопоставлен, он будет записан как группа и назван как «tag_begin».И затем
(?<content>\w+)
захватывает контент, завернутый в тег.Конечный тег должен быть таким же, как и ранее подобранный, поэтому здесь используется
\k<tag_begin>
, и если он прошел тест, то захватывает его как группу и присваивает ему имя «tag_end», вот что(?<tag_end>\k<tag_begin>))
говорит.В JS вы создали таблицу следующим образом:
Используйте эту таблицу, чтобы заменить соответствующие теги.
Sting.replace имеет перегрузку String.replace (regexp, функция), которая может принимать захваченные группы в качестве параметров, мы используем эти захваченные элементы для просмотра таблицы и генерирования замещающей строки.
[Обновление]
Я обновил код, я сохранил первый на тот случай, если кому-то еще не нужны реагирующие компоненты, и вы можете видеть, что между ними мало различий.
источник
console.log
вывод, то увидите, что массив заполнен строками, а не фактическими компонентами React: jsfiddle.net/xftswh41Вы можете сделать это так:
источник
A working solution purely using Javascript and ReactJs without dangerouslySetInnerHTML.
Подходить
Посимвольный поиск элементов уценки. Как только он встречается, найдите конечный тег для того же самого и затем конвертируйте его в HTML.
Поддерживаемые теги во фрагменте
Ввод и вывод из фрагмента:
JsFiddle: https://jsfiddle.net/sunil12738/wg7emcz1/58/
Код:
Подробное объяснение (с примером):
Предположим, если строка - это «
How are *you* doing?
Сохранить отображение символов на теги».["How are "]
и запускать внутренний цикл, пока не найдете следующий *.Now next between * and * needs to be bold
, мы конвертируем их в html-элемент по тексту и напрямую помещаем в массив где Tag = b с карты. Если вы это сделаете<Tag>text</Tag>
, внутренняя реакция преобразуется в текст и толкает в массив. Теперь массив [«как дела», вы ]. Вырваться из внутреннего циклаHow are <b>you</b> doing?
Note: <b>you</b> is html and not text
Примечание : вложение также возможно. Нам нужно вызвать вышеуказанную логику в рекурсии
Добавить поддержку новых тегов
map
объект с ключом в качестве символа и значением в качестве соответствующего тегаЭто поддерживает вложение? Нет
Поддерживает ли он все случаи использования, упомянутые OP? да
Надеюсь, поможет.
источник
asdf
что будут отображаться<pre>asdf</pre>
с темным фоном, верно? Дайте мне знать это, и я увижу. Даже вы можете попробовать сейчас. Простой подход заключается в следующем: в приведенном выше решении замените `` `в тексте специальным символом, например ^ или ~, и сопоставьте его с предварительным тегом. Тогда все будет работать нормально. Другой подход требует дополнительной работы<pre>asdf</pre>
. Спасибо!pre
поддержку тегов. Дайте мне знать, если это работает