React:呈现语法突出显示的代码块

React : Rendering a syntax highlighted code block

我正在使用 React 和 JSX 开发一个文章系统。我的文章有时在其内容中包含代码示例。我已经实施 highlight.js 来为这些块添加样式。我的主要问题是我的文章使用 HTML 标签,因此我使用 React 的 dangerouslySetInnerHTML 方法。这工作正常,但当然我的代码块中的任何 HTML 代码都会被解释为 HTML。我想知道你们中是否有人对我应该如何实施这一点有任何见解。我是否应该从我的内容中删除所有 HTML 并在安全地将其呈现为文本之前解析它(使用 markdown)?

感谢阅读本文。

我使用带有高亮显示的 markdown,然后使用 innerHtml。这是来自 https://github.com/calitek/ProjectNotes.

'use strict';

var fs = require('fs');
var Remarkable = require('remarkable');
var hljs       = require('highlight.js');
let lodash = require('lodash');

var md = new Remarkable({
  highlight: function (str, lang) {
    if (lang && hljs.getLanguage(lang)) {
      try {
        return hljs.highlight(lang, str).value;
      } catch (err) {}
    }

    try {
      return hljs.highlightAuto(str).value;
    } catch (err) {}

    return '';
  }
});

var rootDataPath = './data';

var getFile = function(clientData, doneCallBack) {
  let getNote = function(fileData) {
    var filePath = rootDataPath + '/filenotes.json';
    var notesReadCallBack = function(err, data){
      if (err) doneCallBack({fileData: fileData, noteData: {note: 'error'}});
      else {
        let noteData;
        var jsonData = JSON.parse(data.toString());
        let noteRecord = lodash.findWhere(jsonData, {nodeid: clientData.nodeid});
        if (noteRecord) {
          let noteIndex = lodash.indexOf(jsonData, noteRecord);
          noteData = jsonData[noteIndex];
        } else {
          noteData = {nodeid: clientData.nodeid, note: ""};
        }
        let returnObject = {
          noteData: noteData,
          fileData: fileData
        }
        return doneCallBack(returnObject);
      }
    };
    fs.readFile(filePath, notesReadCallBack);
  }
  var fileReadCallBack = function(err, data){
    if (err) doneCallBack({note: 'error'});
    else {
      let inData = data.toString();
      let inFile = clientData.filePath;
      if (inFile.endsWith('.js') || inFile.endsWith('.json') || inFile.endsWith('.css')) {
        inData = '``` javascript\n' + inData + '```';
      }

      let outData = md.render(inData);
      getNote(outData);
    }
  };
  fs.readFile(clientData.filePath, fileReadCallBack);
};

我在服务端做markdown渲染。然后将其发送到组件。

import React from 'react';

let FileCtrlSty = {
  height: '60%',
  marginBottom: '20px',
  width: '100%'
};

export default class FileCtrl extends React.Component {
  render() {
    let htmlDivSty = {height: '100%', width: '100%'}
    if (this.props.fileData.startsWith('<pre>')) htmlDivSty.overflow = 'hidden';
    else htmlDivSty.overflow = 'auto';
    let fileHtml = {__html: this.props.fileData};
    return (
      <div id='FileCtrlSty' style={FileCtrlSty}>
        <div dangerouslySetInnerHTML={fileHtml} style={htmlDivSty}></div>
      </div>
    );
  }
}

我的解决方案是在我的文章视图中实现一个简单的 "markdown" 类解析器。这个想法是使用正则表达式和 return JSX 元素从这些结果中解析降价。我不太擅长正则表达式(这可能会有所改进)但是这里是:

module.exports = React.createClass({
//this regex matchs \n\n, all wrapping ** **, wrapping ''' '''
mainRegex: /(\n\n|\*\*(?:[\s\S]*?)\*\*|'''(?:[\s\S]*?)''')/g,
titleRegex : /(?:\*\*([\s\S]*?)\*\*)/,
codeRegex : /(?:'''([\s\S]*?)''')/,

getInitialState: function() {
    return {
        content: []
    };
},

setContent: function() {
    if (this.props.htmlContent && !this.state.content.length) this.setState( {content: this.formatText(this.props.htmlContent) });
},

formatText: function(_text) {

    var _this = this;
    var _content = [];

    _text = _text.split(this.mainRegex);

    _text.forEach(function(_frag) {

        if (!/\n\n/g.test(_frag) ) {

            if (_this.titleRegex.test(_frag))  _content.push( {type: "h2", value: _frag.match(_this.titleRegex)[1] } );
            else if (_frag!=="") _content.push({type: "p", value: _frag});

        } else if (_this.codeRegex.test(_frag))  {

            _content.push( {type: "code", value: _frag.match(_this.codeRegex)[1] } );
        }

    });

    return _content;
},
render: function() {

    return <article>
        {this.state.content.map(this.renderText)}
    </article>;

},

renderText:function(_tag, i) {

    switch (_tag.type) {

        case "p":
            return <p key={i}>{_tag.value}</p>;
            break;

        case "h2":
            return <h2 key={i}>{_tag.value}</h2>;
            break;

        case "code":
            return <pre key={i}><code clasName="js">{_tag.value}</code></pre>;
            break;

    }

},

componentDidUpdate: function() {

    this.setContent();
    this.highlightCode();

},

highlightCode: function() {

    var _codeBlocks = document.getElementsByTagName('code');
    for (var i = 0, j = _codeBlocks.length; i<j; ++i) {
        hljs.highlightBlock(_codeBlocks[i]);
    }

}
});