J'ai un très petit sous-ensemble de Markdown ainsi que du HTML personnalisé que j'aimerais analyser dans les composants React. Par exemple, je voudrais transformer cette chaîne suivante:
hello *asdf* *how* _are_ you !doing! today
Dans le tableau suivant:
[ "hello ", <strong>asdf</strong>, " ", <strong>how</strong>, " ", <em>are</em>, " you ", <MyComponent onClick={this.action}>doing</MyComponent>, " today" ]
puis le retourner à partir d'une fonction de rendu React (React rendra le tableau correctement au format HTML formaté)
Fondamentalement, je veux donner aux utilisateurs la possibilité d'utiliser un ensemble très limité de Markdown pour transformer leur texte en composants stylisés (et dans certains cas mes propres composants!)
Il est imprudent de mettre dangereusement SetInnerHTML, et je ne veux pas apporter de dépendance externe, car ils sont tous très lourds, et je n'ai besoin que de fonctionnalités très basiques.
Je fais actuellement quelque chose comme ça, mais c'est très fragile et ne fonctionne pas dans tous les cas. Je me demandais s'il y avait une meilleure façon:
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;
}
Voici ma question précédente qui a conduit à celle-ci.
font _italic *and bold* then only italic_ and normal
? Quel serait le résultat attendu? Ou ne sera-t-il jamais imbriqué?asdf*
sans qu'il disparaisse)Réponses:
Comment ça fonctionne?
Cela fonctionne en lisant un morceau de chaîne par morceau, ce qui n'est peut-être pas la meilleure solution pour les très longues chaînes.
Chaque fois que l'analyseur détecte qu'un morceau critique est en cours de lecture, c'est-à-dire
'*'
ou toute autre balise de démarque, il commence à analyser des morceaux de cet élément jusqu'à ce que l'analyseur trouve sa balise de fermeture.Il fonctionne sur des chaînes multi-lignes, voir le code par exemple.
Avertissements
Vous n'avez pas précisé, ou j'aurais pu mal comprendre vos besoins, s'il est nécessaire d'analyser les balises à la fois en gras et en italique , ma solution actuelle pourrait ne pas fonctionner dans ce cas.
Si vous avez cependant besoin de travailler avec les conditions ci-dessus, commentez ici et je modifierai le code.
Première mise à jour: modifie la façon dont les balises de démarque sont traitées
Les balises ne sont plus codées en dur, mais plutôt une carte que vous pouvez facilement étendre pour répondre à vos besoins.
Correction des bugs que vous avez mentionnés dans les commentaires, merci d'avoir signalé ce problème = p
Deuxième mise à jour: balises de démarque multi-longueur
Le moyen le plus simple d'y parvenir: remplacer les caractères multi-longueurs par un unicode rarement utilisé
Bien que la méthode
parseMarkdown
ne prenne pas encore en charge les balises multi-longueur, nous pouvons facilement remplacer ces balises multi-longueur par un simplestring.replace
lors de l'envoi de notrerawMarkdown
accessoire.Pour voir un exemple de cela dans la pratique, regardez le
ReactDOM.render
, situé à la fin du code.Même si votre application prend en charge plusieurs langues, il existe toujours des caractères unicode non valides que JavaScript détecte, par exemple:
"\uFFFF"
n'est pas un unicode valide, si je me souviens bien, mais JS sera toujours en mesure de le comparer ("\uFFFF" === "\uFFFF" = true
)Cela peut sembler piraté au début, mais, selon votre cas d'utilisation, je ne vois aucun problème majeur en utilisant cette route.
Une autre façon d'y parvenir
Eh bien, nous pourrions facilement suivre les derniers morceaux
N
(oùN
correspond à la longueur de la balise multi-longueur la plus longue).Il y aurait quelques ajustements à apporter au comportement de la méthode de boucle à l'intérieur
parseMarkdown
, c'est-à-dire vérifier si le morceau actuel fait partie d'une balise de plusieurs longueurs, s'il l'utilise comme balise; sinon, dans des cas comme``k
, nous aurions besoin de le marquer commenotMultiLength
ou quelque chose de similaire et de pousser ce morceau comme contenu.Code
Lien vers le code (TypeScript) https://codepen.io/ludanin/pen/GRgNWPv
Lien vers le code (vanilla / babel) https://codepen.io/ludanin/pen/eYmBvXw
la source
This must be *bold*
parThis must be *bo_ld*
. Cela entraîne une mauvaise formation du code HTML résultantIl semble que vous recherchiez une petite solution très basique. Pas des "super-monstres" comme
react-markdown-it
:)Je voudrais vous recommander https://github.com/developit/snarkdown qui a l'air assez léger et agréable! Juste 1 Ko et extrêmement simple, vous pouvez l'utiliser et l'étendre si vous avez besoin d'autres fonctionnalités de syntaxe.
Liste des balises prises en charge https://github.com/developit/snarkdown/blob/master/src/index.js#L1
Mise à jour
Je viens de remarquer les composants React, je l'ai manqué au début. Donc, c'est génial pour vous, je crois que nous prenons la bibliothèque comme exemple et implémentons vos composants requis personnalisés pour le faire sans définir le HTML dangereusement. La bibliothèque est assez petite et claire. Aie du plaisir avec ça! :)
la source
Le résultat:
Résultat du test d'expression régulière
Explication:
Vous pouvez définir vos balises dans cette section:
[*|!|_]
une fois que l'une d'elles est mise en correspondance, elle sera capturée en tant que groupe et nommée "tag_begin".Et
(?<content>\w+)
capture ensuite le contenu enveloppé par la balise.La balise de fin doit être la même que la balise précédente, donc ici utilise
\k<tag_begin>
, et si elle a réussi le test, capturez-la en groupe et donnez-lui un nom "tag_end", c'est ce qui(?<tag_end>\k<tag_begin>))
dit.Dans le JS, vous avez configuré une table comme celle-ci:
Utilisez ce tableau pour remplacer les balises correspondantes.
Sting.replace a une surcharge String.replace (regexp, fonction) qui peut prendre les groupes capturés comme paramètres, nous utilisons ces éléments capturés pour rechercher la table et générer la chaîne de remplacement.
[Mise à jour]
J'ai mis à jour le code, j'ai gardé le premier au cas où quelqu'un d'autre n'aurait pas besoin de composants réactifs, et vous pouvez voir qu'il y a peu de différence entre eux.
la source
console.log
sortie, vous verrez que le tableau est plein de chaînes, pas de véritables composants React: jsfiddle.net/xftswh41vous pouvez le faire comme ceci:
la source
A working solution purely using Javascript and ReactJs without dangerouslySetInnerHTML.
Approche
Recherche caractère par caractère des éléments de démarque. Dès que l'on en rencontre, recherchez la balise de fin pour la même, puis convertissez-la en html.
Balises prises en charge dans l'extrait de code
Entrée et sortie de l'extrait:
JsFiddle: https://jsfiddle.net/sunil12738/wg7emcz1/58/
Code:
Explication détaillée (avec exemple):
Supposons que si la chaîne est
How are *you* doing?
Conserver un mappage des symboles aux balises["How are "]
et démarre la boucle intérieure jusqu'à ce que vous trouviez la prochaine *.Now next between * and * needs to be bold
, nous les convertissons en élément html par texte et poussons directement dans le tableau où Tag = b de la carte. Si vous le faites<Tag>text</Tag>
, réagissez en interne convertit en texte et poussez dans le tableau. Maintenant, le tableau est ["comment allez - vous ", vous ]. Rupture de la boucle intérieureHow are <b>you</b> doing?
Note: <b>you</b> is html and not text
Remarque : l'imbrication est également possible. Nous devons appeler la logique ci-dessus en récursivité
Pour ajouter la prise en charge de nouvelles balises
map
objet avec la clé comme caractère et la valeur comme balise correspondantePrend-il en charge l'imbrication? Non
Prend-il en charge tous les cas d'utilisation mentionnés par OP? Oui
J'espère que cela aide.
la source
asdf
le rendu sera<pre>asdf</pre>
sur fond sombre, non? Faites le moi savoir et je verrai. Même vous pouvez essayer maintenant. Une approche simple est la suivante: dans la solution ci-dessus, remplacez le `` `dans le texte par un caractère spécial tel que ^ ou ~ et mappez-le à la balise pre. Ensuite, cela fonctionnera bien. Une autre approche nécessite plus de travail<pre>asdf</pre>
. Merci!pre
prise en charge des balises. Faites-moi savoir si cela fonctionne