Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/378.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何将降价的一小部分解析为React组件?_Javascript_Arrays_Regex_Reactjs_Markdown - Fatal编程技术网

Javascript 如何将降价的一小部分解析为React组件?

Javascript 如何将降价的一小部分解析为React组件?,javascript,arrays,regex,reactjs,markdown,Javascript,Arrays,Regex,Reactjs,Markdown,我有一个非常小的Markdown子集以及一些自定义html,我想将它们解析为React组件。例如,我想转换以下字符串: 你好*asdf**你好!干!今天 进入以下数组: [“你好”、asdf,“,你好吗?”,“你”今天在做什么? 然后从React呈现函数返回它(React将以格式化的HTML正确呈现数组) 基本上,我想让用户选择使用一组非常有限的标记,将他们的文本转换为样式化组件(在某些情况下,我自己的组件!) 使用危险的HTML是不明智的,我不想引入外部依赖,因为它们都很重,我只需要非常基本的

我有一个非常小的Markdown子集以及一些自定义html,我想将它们解析为React组件。例如,我想转换以下字符串:

你好*asdf**你好!干!今天

进入以下数组:

[“你好”、asdf,“,你好吗?”,“你”今天在做什么?

然后从React呈现函数返回它(React将以格式化的HTML正确呈现数组)

基本上,我想让用户选择使用一组非常有限的标记,将他们的文本转换为样式化组件(在某些情况下,我自己的组件!)

使用危险的HTML是不明智的,我不想引入外部依赖,因为它们都很重,我只需要非常基本的功能

我目前正在做类似的事情,但它非常脆弱,不适用于所有情况。我想知道是否有更好的方法:

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;
}
函数匹配强(结果,i){
让match=result[i]。match(/(^|[^\\])\*(.*)\*/);
如果(匹配){result[i]={match[2]};}
复赛;
}
函数匹配斜体(结果,i){
让match=result[i]。match(/(^ |[^\\])_(.*)/);//忽略\\u asdf,但不忽略\\u asdf_
如果(匹配){result[i]={match[2]};}
复赛;
}
函数匹配代码(结果,i){
让match=result[i]。match(/(^ |[^\\])``\n?([\s\s]+)\n?`/);
如果(匹配){result[i]={match[2]}
;} 复赛; } //非常脆弱和低效 导出函数转换器MarkdownToComponents(消息){ 让result=message.match(/(\\?([!*\u`+-]{1,3})([\s\s]+?)\2)\s |([^\\!*\u`+-]+)/g); 如果(result==null){返回消息;} for(设i=0;i
这导致了这个问题。

看起来您正在寻找一个非常基本的小解决方案。不是像
那样的“超级怪兽”反应(降低它的价格
:)

我想推荐你,它看起来很轻很漂亮!只要1kb,而且非常简单,如果您需要任何其他语法特性,您可以使用它并扩展它

支持的标记列表

更新 刚注意到react组件,一开始就错过了。因此,我相信,以库为例,实现定制所需的组件,从而在不危险地设置HTML的情况下完成任务,这对您来说是非常好的。图书馆很小,很干净。玩得开心点!:)

使用此表替换匹配的标记

replace有一个重载,可以将捕获的组作为参数,我们使用这些捕获的项来查找表并生成替换字符串

[更新]
我已经更新了代码,我保留了第一个代码,以防其他人不需要react组件,您可以看到它们之间没有什么区别。
您可以这样做:

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

  };
//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};
  }
//在组件内部
地图数据(myMarkdown){
返回myMarkdown.split(“”).map((w)=>{
如果(w.startsWith('*')和&w.endsWith('*')和&w.length>=3){
w=w.substr(1,w.length-2);
w={w};
}否则{
如果(w.startsWith('u')和w.endsWith('u')&和w.length>=3){
w=w.substr(1,w.length-2);
w={w};
}否则{
如果(w.startsWith(“!”)&w.endsWith(“!”)&w.length>=3){
w=w.substr(1,w.length-2);
w={w};
}
}
}
返回w;
})
}
render(){
让content=this.mapData('hello*asdf**how*\u you!doing!today');
返回{content};
}

一个纯使用Javascript和ReactJs的工作解决方案,没有危险的HTML。

方法 逐字符搜索标记元素。一旦遇到一个标记,就搜索该标记的结束标记,然后将其转换为html

代码段中支持的标记
  • 大胆的
  • 斜体字
  • em
代码段的输入和输出:

JsFiddle:

代码:

const preTag=“đ”
常数映射={
“*”:“b”,
“!”:“我”,
":"em",,
[preTag]:“pre”
}
类应用程序扩展了React.Component{
构造函数(){
超级()
this.getData=this.getData.bind(this)
}
状态={
数据:[]
}
getData(){
让str=document.getElementById(“ta1”).value
//如果任何标记包含多个字符,请将其替换为使用频率较低的字符并使用它
str=str.replace(/``/gi,preTag)
常数tempArr=[]
const tagsArr=Object.keys(映射)
设strIndexOf=0;
for(设i=0;i=0&&str[i-1]!=“\\”){
tempArr.push(str.substring(0,i).split(“\\”).join(“”)。split(preTag.join(“”)
str=str.substr(i+1);
i=0;
对于(设j=0;j=0&&str[j-1]!=“\\”){
const Tag=map[str[j]];
tempArr.push({str.substring(0,j).split(“\\”).join(“”})
str=str.substr(j+1);
i=0;
打破
}
}
}
}
tempArr.push(str.split(\\“”)。join(“”)
这是我的国家({
数据:tempArr,
})
}
render(){
返回(

渲染它
{this.state.data.map(x=>x)} ) } } ReactDOM.render( , document.getElementById('root')) );

它是如何工作的? 它起作用了
var table = {
  "*":{
    "begin":"<strong>",
    "end":"</strong>"
    },
  "_":{
    "begin":"<em>",
    "end":"</em>"
    },
  "!":{
    "begin":"<MyComponent onClick={this.action}>",
    "end":"</MyComponent>"
    },

  };
//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};
  }
// 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'));