วิธีการแยกย่อยเซตย่อยของ Markdown ลงในส่วนประกอบ React


9

ฉันมีชุดย่อยของ 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 render (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;
}

นี่คือคำถามก่อนหน้าของฉันซึ่งนำไปสู่คำถามนี้


1
เกิดอะไรขึ้นถ้าการป้อนข้อมูลมีรายการซ้อนกันเช่นfont _italic *and bold* then only italic_ and normal? ผลลัพธ์ที่คาดหวังคืออะไร หรือมันจะไม่ซ้อนกัน?
trincot

1
ไม่จำเป็นต้องกังวลเกี่ยวกับการทำรัง เป็นเพียง markdown พื้นฐานสำหรับผู้ใช้ สิ่งที่ง่ายที่สุดในการนำไปใช้นั้นใช้ได้กับฉัน ในตัวอย่างของคุณมันคงเป็นเรื่องที่ดีมากถ้าการพับลิกภายในไม่ทำงาน แต่ถ้ามันง่ายกว่าที่จะทำรังมากกว่าที่จะไม่มีมันก็ไม่เป็นไรเหมือนกัน
Ryan Peschel

1
อาจเป็นวิธีที่ง่ายที่สุดในการใช้โซลูชันแบบปิด
mb21

1
ฉันไม่ได้ใช้ markdown มันเป็นเพียงส่วนย่อยที่คล้ายกัน / เล็ก ๆ ของมัน (ซึ่งรองรับส่วนประกอบที่กำหนดเองสองคู่พร้อมกับตัวหนา, ตัวเอียง, โค้ด, ขีดเส้นใต้) ตัวอย่างที่ฉันโพสต์ค่อนข้างใช้งานได้ แต่ดูเหมือนจะไม่เหมาะมากและล้มเหลวในบางกรณีเล็กน้อย (เช่นคุณไม่สามารถพิมพ์เครื่องหมายดอกจันเดียวเช่นนี้: asdf*โดยไม่หายไป)
Ryan Peschel

1
ดี ... การแยกวิเคราะห์มาร์คดาวน์หรือบางอย่างเช่น markdown ไม่ใช่เรื่องง่าย ... regexes อย่าตัดมัน ... สำหรับคำถามที่คล้ายกันเกี่ยวกับ html ให้ดูstackoverflow.com/questions/1732348/ …
mb21

คำตอบ:


1

มันทำงานอย่างไร?

มันทำงานได้โดยการอ่าน 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


ฉันรู้สึกว่าทางออกนี้อยู่ในเส้นทางที่ถูกต้อง แต่ดูเหมือนว่าจะมีปัญหากับการใส่อักขระอื่น ๆ ลงในอักขระอื่น ตัวอย่างเช่นลองเปลี่ยนด้วยThis must be *bold* This must be *bo_ld*มันทำให้ HTML ที่เป็นผลลัพธ์ผิดรูปแบบ
Ryan Peschel

การขาดการทดสอบที่เหมาะสมทำให้เกิดข้อผิดพลาดนี้ขึ้นมา ฉันกำลังแก้ไขแล้วและจะโพสต์ผลลัพธ์ที่นี่ดูเหมือนปัญหาง่าย ๆ ในการแก้ไข
Lukas Danin

ใช่ขอบคุณ. ฉันชอบวิธีนี้จริง ๆ ดูเหมือนแข็งแกร่งและสะอาดมาก ฉันคิดว่ามันสามารถปรับปรุงใหม่ได้เล็กน้อยเพื่อความสง่างามมากยิ่งขึ้น ฉันอาจลองล้อเล่นกับมันสักหน่อย
Ryan Peschel

ทำได้โดยวิธีฉันได้ tweaked รหัสเพื่อสนับสนุนวิธียืดหยุ่นมากขึ้นของการกำหนดแท็ก markdown และค่า JSX ที่เกี่ยวข้อง
Lukas Danin

เฮ้ขอบคุณสิ่งนี้ดูดีมาก สิ่งสุดท้ายที่ฉันคิดว่ามันจะสมบูรณ์แบบ ในโพสต์ต้นฉบับของฉันฉันมีฟังก์ชั่นสำหรับโค้ดขนาดเล็กเกินไป (ที่เกี่ยวข้องกับ backticks สามเท่า) เป็นไปได้ไหมที่จะได้รับการสนับสนุนเช่นนี้? เพื่อให้แท็กเลือกที่จะเป็นอักขระหลายตัวได้หรือไม่ การตอบกลับเพิ่มการสนับสนุนอีกครั้งโดยแทนที่อินสแตนซ์ของ `` `ด้วยอักขระที่ไม่ค่อยได้ใช้ นั่นจะเป็นวิธีที่ง่ายในการทำ แต่ไม่แน่ใจว่ามันเหมาะอย่างยิ่งหรือไม่
Ryan Peschel

4

ดูเหมือนว่าคุณกำลังมองหาโซลูชันขั้นพื้นฐานขนาดเล็กมาก ไม่ใช่ "super-monsters" อย่างreact-markdown-it:)

ฉันอยากจะแนะนำคุณhttps://github.com/developit/snarkdownซึ่งดูสวยและมีน้ำหนักเบา! เพียง 1kb และง่ายมากคุณสามารถใช้มันและขยายถ้าคุณต้องการคุณสมบัติไวยากรณ์อื่น ๆ

รายการแท็กที่รองรับhttps://github.com/developit/snarkdown/blob/master/src/index.js#L1

ปรับปรุง

เพิ่งสังเกตเห็นเกี่ยวกับองค์ประกอบการตอบสนองพลาดมันในตอนแรก ดังนั้นจึงเป็นเรื่องที่ยอดเยี่ยมสำหรับคุณฉันเชื่อว่าการใช้ห้องสมุดเป็นตัวอย่างและนำส่วนประกอบที่กำหนดเองของคุณมาใช้เพื่อให้ทำงานได้โดยไม่ต้องตั้งค่า HTML อย่างเป็นอันตราย ห้องสมุดมีขนาดเล็กและชัดเจน ขอให้สนุกกับมัน! :)


3
var table = {
  "*":{
    "begin":"<strong>",
    "end":"</strong>"
    },
  "_":{
    "begin":"<em>",
    "end":"</em>"
    },
  "!":{
    "begin":"<MyComponent onClick={this.action}>",
    "end":"</MyComponent>"
    },

  };

var myMarkdown = "hello *asdf* *how* _are_ you !doing! today";
var tagFinder = /(?<item>(?<tag_begin>[*|!|_])(?<content>\w+)(?<tag_end>\k<tag_begin>))/gm;

//Use case 1: direct string replacement
var replaced = myMarkdown.replace(tagFinder, replacer);
function replacer(match, whole, tag_begin, content, tag_end, offset, string) {
  return table[tag_begin]["begin"] + content + table[tag_begin]["end"];
}
alert(replaced);

//Use case 2: React components
var pieces = [];
var lastMatchedPosition = 0;
myMarkdown.replace(tagFinder, breaker);
function breaker(match, whole, tag_begin, content, tag_end, offset, string) {
  var piece;
  if (lastMatchedPosition < offset)
  {
    piece = string.substring(lastMatchedPosition, offset);
    pieces.push("\"" + piece + "\"");
  }
  piece = table[tag_begin]["begin"] + content + table[tag_begin]["end"];
  pieces.push(piece);
  lastMatchedPosition = offset + match.length;

}
alert(pieces);

ผลลัพธ์: ผลการแข่งขัน

ผลการทดสอบ Regexp

คำอธิบาย:

/(?<item>(?<tag_begin>[*|!|_])(?<content>\w+)(?<tag_end>\k<tag_begin>))/
  • คุณสามารถกำหนดแท็กของคุณในส่วนนี้: [*|!|_]เมื่อจับคู่หนึ่งในนั้นแท็กนั้นจะถูกจับเป็นกลุ่มและตั้งชื่อเป็น "tag_begin"

  • จากนั้น(?<content>\w+)รวบรวมเนื้อหาที่ห่อด้วยแท็ก

  • แท็กสิ้นสุดจะต้องเหมือนกับแท็กที่จับคู่ก่อนหน้านี้ดังนั้นที่นี่จะใช้\k<tag_begin>และถ้าผ่านการทดสอบแล้วให้จับเป็นกลุ่มและตั้งชื่อ "tag_end" นั่นคือสิ่งที่(?<tag_end>\k<tag_begin>))พูด

ใน JS คุณได้ตั้งค่าตารางเช่นนี้:

var table = {
  "*":{
    "begin":"<strong>",
    "end":"</strong>"
    },
  "_":{
    "begin":"<em>",
    "end":"</em>"
    },
  "!":{
    "begin":"<MyComponent onClick={this.action}>",
    "end":"</MyComponent>"
    },

  };

ใช้ตารางนี้เพื่อแทนที่แท็กที่ตรงกัน

Sting.replaceมี Overload String.replace (ฟังก์ชัน regexp)ซึ่งสามารถจับกลุ่มเป็นพารามิเตอร์เราใช้รายการที่จับเหล่านี้เพื่อค้นหาตารางและสร้างสตริงแทนที่

[อัปเดต]
ฉันได้อัปเดตโค้ดแล้วฉันเก็บรหัสตัวแรกไว้ในกรณีที่คนอื่นไม่ต้องการส่วนประกอบที่ตอบสนองและคุณสามารถเห็นความแตกต่างเล็กน้อยระหว่างพวกเขา ส่วนประกอบทำปฏิกิริยา


น่าเสียดายที่ฉันไม่แน่ใจว่ามันใช้งานได้หรือไม่ เพราะฉันต้องการองค์ประกอบการตอบสนองที่แท้จริงและองค์ประกอบของตัวเอง ถ้าคุณดูในโพสต์ต้นฉบับของฉันคุณจะเห็นว่าฉันกำลังเพิ่มองค์ประกอบที่แท้จริงลงในอาร์เรย์ไม่ใช่สตริงของพวกเขา และการใช้ dangerlySetInnerHTML นั้นมีอันตรายเนื่องจากผู้ใช้สามารถป้อนสตริงที่เป็นอันตรายได้
Ryan Peschel

โชคดีที่มันง่ายมากที่จะแปลงการแทนที่สตริงเป็นส่วนประกอบของ React ฉันได้อัปเดตโค้ดแล้ว
Simon

หืม? ฉันต้องคิดถึงบางสิ่งบางอย่างเพราะพวกเขายังคงเป็นจุดสิ้นสุดของฉัน ฉันยังทำไวโอลินกับรหัสของคุณ หากคุณอ่านconsole.logผลลัพธ์คุณจะเห็นว่าอาร์เรย์เต็มไปด้วยสตริงไม่ใช่ส่วนประกอบของ React จริง: jsfiddle.net/xftswh41
Ryan Peschel

จริงๆแล้วฉันไม่รู้ React ดังนั้นฉันไม่สามารถทำทุกอย่างได้อย่างสมบูรณ์แบบตามความต้องการของคุณ แต่ฉันคิดว่าข้อมูลเกี่ยวกับวิธีการแก้ไขคำถามของคุณก็เพียงพอแล้วคุณต้องนำพวกเขาไปยังเครื่อง React ของคุณและสามารถทำได้
Simon

สาเหตุที่มีเธรดนี้อยู่เนื่องจากดูเหมือนว่าจะยากในการแยกวิเคราะห์ลงในคอมโพเนนต์ของ React (ดังนั้นชื่อเธรดที่ระบุความต้องการที่แน่นอน) การแยกวิเคราะห์สตริงเป็นเรื่องไม่สำคัญและคุณสามารถใช้ฟังก์ชันแทนที่สตริงได้ สตริงไม่ได้เป็นทางออกที่ดีเพราะพวกเขาช้าและไวต่อ XSS เนื่องจากต้องโทรตั้งอย่างเป็นอันตราย
Ryan Peschel

0

คุณสามารถทำได้เช่นนี้:

//inside your compoenet

   mapData(myMarkdown){
    return myMarkdown.split(' ').map((w)=>{

        if(w.startsWith('*') && w.endsWith('*') && w.length>=3){
           w=w.substr(1,w.length-2);
           w=<strong>{w}</strong>;
         }else{
             if(w.startsWith('_') && w.endsWith('_') && w.length>=3){
                w=w.substr(1,w.length-2);
                w=<em>{w}</em>;
              }else{
                if(w.startsWith('!') && w.endsWith('!') && w.length>=3){
                w=w.substr(1,w.length-2);
                w=<YourComponent onClick={this.action}>{w}</YourComponent>;
                }
            }
         }
       return w;
    })

}


 render(){
   let content=this.mapData('hello *asdf* *how* _are_ you !doing! today');
    return {content};
  }

0

A working solution purely using Javascript and ReactJs without dangerouslySetInnerHTML.

เข้าใกล้

ตัวละครโดยการค้นหาตัวละครสำหรับองค์ประกอบ markdown ทันทีที่พบเจอให้ค้นหาแท็กสิ้นสุดสำหรับแท็กเดียวกันจากนั้นแปลงเป็น html

แท็กที่สนับสนุนในตัวอย่างข้อมูล

  • กล้า
  • หนังสือแบบตัวเอียง
  • em
  • ก่อน

อินพุตและเอาต์พุตจากตัวอย่างข้อมูล:

JsFiddle: https://jsfiddle.net/sunil12738/wg7emcz1/58/

รหัส:

const preTag = "đ"
const map = {
      "*": "b",
      "!": "i",
      "_": "em",
      [preTag]: "pre"
    }

class App extends React.Component {
    constructor(){
      super()
      this.getData = this.getData.bind(this)
    }

    state = {
      data: []
    }
    getData() {
      let str = document.getElementById("ta1").value
      //If any tag contains more than one char, replace it with some char which is less frequently used and use it
      str = str.replace(/```/gi, preTag)
      const tempArr = []
      const tagsArr = Object.keys(map)
      let strIndexOf = 0;
      for (let i = 0; i < str.length; ++i) {
        strIndexOf = tagsArr.indexOf(str[i])
        if (strIndexOf >= 0 && str[i-1] !== "\\") {
          tempArr.push(str.substring(0, i).split("\\").join("").split(preTag).join(""))
          str = str.substr(i + 1);
          i = 0;
          for (let j = 0; j < str.length; ++j) {
            strIndexOf = tagsArr.indexOf(str[j])
            if (strIndexOf >= 0 && str[j-1] !== "\\") {
              const Tag = map[str[j]];
              tempArr.push(<Tag>{str.substring(0, j).split("\\").join("")}</Tag>)
              str = str.substr(j + 1);
              i = 0;
              break
             }
          }
        }
      }
      tempArr.push(str.split("\\").join(""))
      this.setState({
        data: tempArr,
      })
    }
    render() {
      return (
        <div>
          <textarea rows = "10"
            cols = "40"
           id = "ta1"
          /><br/>
          <button onClick={this.getData}>Render it</button><br/> 
          {this.state.data.map(x => x)} 
        </div>
      )
    }
  }

ReactDOM.render(
  <App/>,
  document.getElementById('root')
);
<body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.production.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.production.min.js"></script>
  <div id="root"></div>
</body>

คำอธิบายโดยละเอียด (พร้อมตัวอย่าง):

สมมติว่าสตริงเป็นHow are *you* doing? Keep สำหรับการแมปสัญลักษณ์

map = {
 "*": "b"
}
  • วนซ้ำจนกว่าคุณจะพบ * ข้อความก่อนหน้านั้นเป็นสตริงปกติ
  • กดที่อาร์เรย์ภายใน อาร์เรย์กลายเป็น["How are "]และเริ่มวงภายในจนกว่าคุณจะพบต่อไป *
  • Now next between * and * needs to be boldเราแปลงพวกมันเป็นองค์ประกอบ html โดยข้อความและดันเข้าไปใน array โดยตรงที่ Tag = b จากแผนที่ หากคุณ<Tag>text</Tag>ตอบกลับจะแปลงข้อความเป็นข้อความภายในและกดเข้าไปในอาร์เรย์ ตอนนี้อาร์เรย์คือ ["เป็นอย่างไรบ้าง" คุณ ] หยุดพักจากวงใน
  • ตอนนี้เราเริ่มวนรอบนอกจากที่นั่นและไม่พบแท็กดังนั้นกดที่เหลือในอาร์เรย์ Array กลายเป็น: ["เป็นอย่างไร", คุณ , "กำลังทำ"]
  • แสดงผลบน UI How are <b>you</b> doing?
    Note: <b>you</b> is html and not text

หมายเหตุ : การทำรังสามารถทำได้เช่นกัน เราจำเป็นต้องเรียกใช้ตรรกะข้างต้นในการเรียกซ้ำ

เพื่อเพิ่มการสนับสนุนแท็กใหม่

  • หากพวกเขาเป็นหนึ่งตัวละครเช่น * หรือ! เพิ่มพวกเขาในmapวัตถุที่มีคีย์เป็นตัวละครและความคุ้มค่าเป็นแท็กที่สอดคล้องกัน
  • หากพวกเขามีอักขระมากกว่าหนึ่งตัวเช่น `` `ให้สร้างแผนที่หนึ่งถึงหนึ่งโดยใช้อักขระที่ใช้งานไม่บ่อยและใส่ (เหตุผล: ปัจจุบันวิธีการตามตัวละครโดยการค้นหาตัวละครและมากกว่าหนึ่งตัวอักษรจะแตกอย่างไรก็ตาม ที่สามารถได้รับการดูแลโดยการปรับปรุงตรรกะ)

มันรองรับการทำรังหรือไม่? ไม่มี
ไม่ได้สนับสนุนกรณีการใช้งานทั้งหมดที่กล่าวถึงโดย OP? ใช่

หวังว่ามันจะช่วย


สวัสดีมองไปทางนี้แล้ว เป็นไปได้ที่จะใช้กับการสนับสนุน backtick สามเช่นกัน? ดังนั้น `` `asdf` '' จะทำงานได้ดีสำหรับบล็อคโค้ด?
Ryan Peschel

มันจะ แต่การแก้ไขบางอย่างอาจมีความจำเป็น ขณะนี้มีการจับคู่อักขระเดียวเท่านั้นสำหรับ * หรือ! ที่ต้องมีการปรับเปลี่ยนเล็กน้อย โดยทั่วไปการบล็อกรหัสหมายความว่าasdfจะแสดงผล<pre>asdf</pre>ด้วยพื้นหลังสีดำใช่ไหม แจ้งให้เราทราบและฉันจะเห็น แม้คุณสามารถลองตอนนี้ วิธีการง่ายๆคือ: ในโซลูชันด้านบนให้แทนที่ `` `ในข้อความด้วยอักขระพิเศษเช่น ^ หรือ ~ และแมปเป็นแท็กล่วงหน้า จากนั้นมันก็จะทำงานได้ดี วิธีอื่น ๆ ต้องการงานเพิ่มอีก
Sunil Chaudhary

ใช่ว่าแทนที่ `` `asdf``` <pre>asdf</pre>กับ ขอบคุณ!
Ryan Peschel

@RyanPeschel สวัสดี! ได้เพิ่มการpreรองรับแท็กเช่นกัน แจ้งให้เราทราบว่าใช้งานได้หรือไม่
Sunil Chaudhary

โซลูชันที่น่าสนใจ (ใช้อักขระที่หายาก) ปัญหาหนึ่งที่ฉันยังเห็นคือการขาดการสนับสนุนสำหรับการหลบหนี (เช่น \ * asdf * ไม่เป็นตัวหนา) ซึ่งฉันรวมการสนับสนุนในรหัสในโพสต์ต้นฉบับของฉัน โพสต์) จะเพิ่มยากไหม
Ryan Peschel
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.