มันทำงานอย่างไร?
มันทำงานได้โดยการอ่าน string chunk โดย chunk ซึ่งอาจไม่ใช่วิธีที่ดีที่สุดสำหรับสตริงที่ยาวมาก ๆ
เมื่อใดก็ตามที่ parser ตรวจพบ chunk วิกฤติกำลังถูกอ่านนั่นคือ'*'
หรือแท็ก markdown อื่น ๆ มันจะเริ่มการแยกส่วนขององค์ประกอบนี้จนกว่า parser จะพบแท็กปิดของมัน
มันทำงานบนสตริงหลายบรรทัดดูรหัสตัวอย่าง
คำเตือน
คุณยังไม่ได้ระบุหรือฉันอาจเข้าใจผิดที่คุณต้องการหากมีความจำเป็นในการแยกวิเคราะห์แท็กที่มีทั้งตัวหนาและตัวเอียงโซลูชันปัจจุบันของฉันอาจไม่ทำงานในกรณีนี้
อย่างไรก็ตามหากคุณต้องการทำงานกับเงื่อนไขข้างต้นเพียงแค่แสดงความคิดเห็นที่นี่และฉันจะปรับแต่งรหัส
อัปเดตครั้งแรก: ปรับแต่งวิธีปฏิบัติแท็ก markdown
แท็กจะไม่มีการเข้ารหัสอีกต่อไป แต่เป็นแผนที่ที่คุณสามารถขยายให้พอดีกับความต้องการของคุณได้อย่างง่ายดาย
แก้ไขข้อบกพร่องที่คุณพูดถึงในความคิดเห็นขอบคุณที่ชี้ปัญหานี้ = p
การอัพเดตครั้งที่สอง: แท็ก markdown ที่มีความยาวหลายระดับ
วิธีที่ง่ายที่สุดในการบรรลุเป้าหมายนี้: แทนที่ตัวอักษรแบบหลายความยาวด้วย unicode ที่ไม่ค่อยได้ใช้
แม้ว่าวิธีการparseMarkdown
นี้จะไม่รองรับแท็กที่มีหลายความยาว แต่เราสามารถแทนที่แท็กที่มีความยาวหลาย ๆ อันได้อย่างง่ายดายด้วยการstring.replace
ส่งrawMarkdown
เสา
หากต้องการดูตัวอย่างของสิ่งนี้ในทางปฏิบัติให้ดูที่ReactDOM.render
ตั้งอยู่ท้ายรหัส
แม้ว่าโปรแกรมประยุกต์ของคุณไม่สนับสนุนหลายภาษามีอักขระ Unicode ที่ไม่ถูกต้องแล้วว่า JavaScript ยังคงตรวจพบอดีต .: "\uFFFF"
ไม่ได้เป็น Unicode ที่ถูกต้องถ้าผมจำอย่างถูกต้อง แต่ JS จะยังคงสามารถที่จะเปรียบเทียบ ( "\uFFFF" === "\uFFFF" = true
)
อาจดูเหมือนแฮ็ค -y ในตอนแรก แต่ขึ้นอยู่กับการใช้งานของคุณฉันไม่เห็นปัญหาที่สำคัญใด ๆ โดยใช้เส้นทางนี้
อีกวิธีหนึ่งในการบรรลุเป้าหมายนี้
เราสามารถติดตามชิ้นสุดท้าย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
เชื่อมโยงไปยังรหัส (วานิลลา / บาเบล) https://codepen.io/ludanin/pen/eYmBvXw
font _italic *and bold* then only italic_ and normal
? ผลลัพธ์ที่คาดหวังคืออะไร หรือมันจะไม่ซ้อนกัน?