React:呈现语法高亮显示的代码块

React : Rendering a syntax highlighted code block

本文关键字:显示 代码 高亮 语法 React      更新时间:2023-09-26

我正在使用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);
};

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

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"的简单解析器。其思想是使用regex解析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]);
    }
}
});