React:渲染语法高亮显示的代码块

时间:2016-01-03 15:35:44

标签: javascript reactjs syntax-highlighting

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

感谢您阅读本文。

2 个答案:

答案 0 :(得分:1)

我使用markdown和highlight,然后使用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);
};

我正在服务器上进行降价渲染。然后将其发送到组件。

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

答案 1 :(得分:0)

我的解决方案是在文章视图中实现一个简单的“markdown”,就像解析器一样。我们的想法是使用正则表达式解析markdown并从这些结果中返回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]);
    }

}
});