어떻게 작동합니까?
그것은 청크별로 문자열 청크를 읽음으로써 작동합니다. 이것은 실제로 긴 문자열에 대한 최상의 솔루션이 아닐 수도 있습니다.
파서는 임계 청크 (즉 '*'
, 다른 마크 다운 태그)를 읽고 있음을 감지 할 때마다 파서가 닫는 태그를 찾을 때까지이 요소의 청크 파싱을 시작합니다.
여러 줄 문자열에서 작동합니다. 예를 들어 코드를 참조하십시오.
경고
굵은 체와 기울임 꼴로 된 태그를 구문 분석해야하는 경우 , 현재 솔루션이 작동하지 않을 수 있습니다.
그러나 위의 조건으로 작업하려면 여기에 주석을 달고 코드를 조정하십시오.
첫 번째 업데이트 : 마크 다운 태그 처리 방법을 조정합니다.
태그는 더 이상 하드 코딩되지 않으며, 필요에 따라 쉽게 확장 할 수있는 맵입니다.
이 문제를 지적 해 주셔서 감사합니다. = p에서 언급 한 버그를 수정했습니다.
두 번째 업데이트 : 다중 길이 마크 다운 태그
가장 쉬운 방법 : 다중 길이 문자를 거의 사용하지 않는 유니 코드로 대체
이 방법 parseMarkdown
은 아직 다중 길이 태그를 지원하지 않지만 소품을 string.replace
보낼 때 이러한 다중 길이 태그를 간단한 것으로 쉽게 바꿀 수 있습니다 rawMarkdown
.
실제로 이에 대한 예제를 보려면 ReactDOM.render
코드 끝에있는를보십시오.
응용 프로그램 이 여러 언어를 지원 하더라도 JavaScript가 여전히 감지하는 잘못된 유니 코드 문자가 있습니다. 예 : "\uFFFF"
올바르게 기억하면 유효한 유니 코드가 아니지만 JS는 여전히 비교 할 수 있습니다 ( "\uFFFF" === "\uFFFF" = true
)
처음에는 해킹처럼 보일 수 있지만 사용 사례에 따라이 경로를 사용하여 큰 문제는 보이지 않습니다.
이것을 달성하는 또 다른 방법
글쎄, 우리는 마지막 N
( N
가장 긴 다중 길이 태그의 길이에 해당하는) 청크를 쉽게 추적 할 수 있습니다 .
메소드 내부 루프가 parseMarkdown
동작 하는 방식 , 즉 현재 청크가 다중 길이 태그의 일부인지, 태그로 사용되는지 확인하는 방법 이 약간 수정 될
것입니다. 그렇지 않은 경우,와 같은 경우 또는 이와 유사한 ``k
것으로 표시하고 notMultiLength
해당 청크를 콘텐츠로 푸시해야합니다.
암호
// Instead of creating hardcoded variables, we can make the code more extendable
// by storing all the possible tags we'll work with in a Map. Thus, creating
// more tags will not require additional logic in our code.
const tags = new Map(Object.entries({
"*": "strong", // bold
"!": "button", // action
"_": "em", // emphasis
"\uFFFF": "pre", // Just use a very unlikely to happen unicode character,
// We'll replace our multi-length symbols with that one.
}));
// Might be useful if we need to discover the symbol of a tag
const tagSymbols = new Map();
tags.forEach((v, k) => { tagSymbols.set(v, k ); })
const rawMarkdown = `
This must be *bold*,
This also must be *bo_ld*,
this _entire block must be
emphasized even if it's comprised of multiple lines_,
This is an !action! it should be a button,
\`\`\`
beep, boop, this is code
\`\`\`
This is an asterisk\\*
`;
class App extends React.Component {
parseMarkdown(source) {
let currentTag = "";
let currentContent = "";
const parsedMarkdown = [];
// We create this variable to track possible escape characters, eg. "\"
let before = "";
const pushContent = (
content,
tagValue,
props,
) => {
let children = undefined;
// There's the need to parse for empty lines
if (content.indexOf("\n\n") >= 0) {
let before = "";
const contentJSX = [];
let chunk = "";
for (let i = 0; i < content.length; i++) {
if (i !== 0) before = content[i - 1];
chunk += content[i];
if (before === "\n" && content[i] === "\n") {
contentJSX.push(chunk);
contentJSX.push(<br />);
chunk = "";
}
if (chunk !== "" && i === content.length - 1) {
contentJSX.push(chunk);
}
}
children = contentJSX;
} else {
children = [content];
}
parsedMarkdown.push(React.createElement(tagValue, props, children))
};
for (let i = 0; i < source.length; i++) {
const chunk = source[i];
if (i !== 0) {
before = source[i - 1];
}
// Does our current chunk needs to be treated as a escaped char?
const escaped = before === "\\";
// Detect if we need to start/finish parsing our tags
// We are not parsing anything, however, that could change at current
// chunk
if (currentTag === "" && escaped === false) {
// If our tags array has the chunk, this means a markdown tag has
// just been found. We'll change our current state to reflect this.
if (tags.has(chunk)) {
currentTag = tags.get(chunk);
// We have simple content to push
if (currentContent !== "") {
pushContent(currentContent, "span");
}
currentContent = "";
}
} else if (currentTag !== "" && escaped === false) {
// We'll look if we can finish parsing our tag
if (tags.has(chunk)) {
const symbolValue = tags.get(chunk);
// Just because the current chunk is a symbol it doesn't mean we
// can already finish our currentTag.
//
// We'll need to see if the symbol's value corresponds to the
// value of our currentTag. In case it does, we'll finish parsing it.
if (symbolValue === currentTag) {
pushContent(
currentContent,
currentTag,
undefined, // you could pass props here
);
currentTag = "";
currentContent = "";
}
}
}
// Increment our currentContent
//
// Ideally, we don't want our rendered markdown to contain any '\'
// or undesired '*' or '_' or '!'.
//
// Users can still escape '*', '_', '!' by prefixing them with '\'
if (tags.has(chunk) === false || escaped) {
if (chunk !== "\\" || escaped) {
currentContent += chunk;
}
}
// In case an erroneous, i.e. unfinished tag, is present and the we've
// reached the end of our source (rawMarkdown), we want to make sure
// all our currentContent is pushed as a simple string
if (currentContent !== "" && i === source.length - 1) {
pushContent(
currentContent,
"span",
undefined,
);
}
}
return parsedMarkdown;
}
render() {
return (
<div className="App">
<div>{this.parseMarkdown(this.props.rawMarkdown)}</div>
</div>
);
}
}
ReactDOM.render(<App rawMarkdown={rawMarkdown.replace(/```/g, "\uFFFF")} />, document.getElementById('app'));
코드 링크 (TypeScript) https://codepen.io/ludanin/pen/GRgNWPv
코드 링크 (vanilla / babel) https://codepen.io/ludanin/pen/eYmBvXw
font _italic *and bold* then only italic_ and normal
? 와 같이 중첩 된 항목이 있으면 어떻게 됩니까? 예상되는 결과는 무엇입니까? 아니면 절대 중첩되지 않습니까?