解答:可以采用to-markdown,只需要下面的一句代码就可以了:
//其中tableHtml是我们的html格式的table,而gfm表明符合github标准的markdown
const markdownTable = toMarkdown(tableHtml, { gfm: true });
其中gfm(github flavor markdown)表明这是GitHub 风格的 Markdown 正式规范
const commonmark = require("commonmark");
const reader = new commonmark.Parser();
const parsed = reader.parse("");
//parse方法传入你需要解析的字符串
const parsedJSON = {};
const walker = parsed.walker();
while ((event = walker.next())) {
node = event.node;
if (event.entering && node.type === "code_block") {
//通过node的_info属性
if (node._info === "css") {
parsedJSON.css = node.literal;
} else if (node._info === "html") {
parsedJSON.html = node.literal;
} else if (node._info === "javascript" || node._info === "js") {
parsedJSON.js = node.literal;
}
}
}
具体使用你可以参考commonmark,commonmark其实让markdown在规范道路上更近了一步,而GFM
又是基于commonmark的,当评估GFM规范 的时候,你可以清楚的知道哪些是GFM特定规范的补充内容,因为它们都高亮显示了。并且你也会看到原有规范的所有部分都保持原样,因此,GFM 规范能够与任何其他的实现保持兼容
。我们是在现存的CommonMark规范中来完成这一项工作的,同时还特意关注以确保我们的扩展是原有规范的一个严格且可选的超集。如果有疑问自行google。
解决:只需要在调用load方法时候传入一个参数就可以了。
const $ = cheerio.load(marked(contentParse), {
decodeEntities: false
//参数
});
很多情况下,我们有一个字符串,但是我们需要将里面的"<script/>"标签内容原样展示,这时候就需要对我们的标签进行转义,此时你可以参考我的转义字符串中的script标签。此时我们可以将转义后的字符串转化为markdown格式:
var scriptRegex = /(<script\b[^>]*>)([\s\S]*?)(<\/script>)/gm;
var linkRegex = /(<link\b[^>]*>)([\s\S]*?)(<\/link>)/gm;
/**
* 转义script标签防止被解
* @param {[type]} str [description]
* @return {[type]} [description]
*/
function escapeChars(str) {
str = str.replace(/&/g, '&');
str = str.replace(/</g, '<');
str = str.replace(/>/g, '>');
str = str.replace(/'/g, '´');
str = str.replace(/"/g, '"');
str = str.replace(/\|/g, '¦');
return str;
}
var toMarkdown = require("to-markdown");
var descriptionContent = encodeScript($(whenUse).nextUntil("h2").html());
//html片段并转义script标签
var readme = "<p>" + descriptionFirst.html() + "</p><h2>" + whenUse.html() + "</h2>" + descriptionContent;
toMarkdown(readme, { gfm: true });
//to-markdown将我们的html转化为markdown,此时我们转义后的script又回到了script标签了
此时react-markdown就可以原样解析出来了。当然,我们的link标签也是需要编码的:
function encodeLink(str){
return str.replace(linkRegex,function(matched,$1,$2,$3){
const replacedStr = escapeChars($1)+$2+escapeChars($3)
return replacedStr;
});
}
报错信息如下:
Uncaught TypeError: Cannot read property 'value' of null at Function.H.fromTextArea (codemirror.min.js:1) at MarkdownEdit._this.mountedMdEditor (MarkdownEdit.js:96) at MarkdownEdit.componentDidMount (MarkdownEdit.js:148) at ReactCompositeComponent.js:264 at measureLifeCyclePerf (ReactCompositeComponent.js:75) at ReactCompositeComponent.js:263 at CallbackQueue.notifyAll (CallbackQueue.js:76) at ReactReconcileTransaction.close (ReactReconcileTransaction.js:80) at ReactReconcileTransaction.closeAll (Transaction.js:209) at ReactReconcileTransaction.perform (Transaction.js:156)
一开始我猜测是我没有给textarea设置value或者defaultValue:
<textarea
name="markdownRaw"
id="markdownRaw"
style={{ width: "100%", display: "none" }}
/>
最后发现不是这么回事,而是我将这个textarea放到Modal里面,导致Modal没有展示的时候无法获取到这个textarea,所以修改为如下内容即可:
<Modal
title="你在编辑在线文档"
width={"90%"}
height={"calc(100vh - 60px)"}
visible={this.state.isEditMd}
onOk={this.handleOk}
onCancel={this.handleCancel}
>
<div
id="iframewrapper"
style={{ height: "100%", width: "100%" }}
/>
</Modal>
<textarea
name="markdownRaw"
id="markdownRaw"
style={{ width: "100%", display: "none" }}
/>
<\/Col>
这里使用codeMirror遇到的问题都是与Modal有关,所以一般我们都是完成一定的操作后Modal显示了,然后做特定的操作,如下:
editMarkdown = source => {
this.setState(
{
isEditMd: true
},
() => {
this.submitTryit();
}
);
};
很多情况,我们都会遇到高亮jsx,js,css,html代码等需求,此时我们可以考虑使用prism和highlight,此处我们采用prism来完成。
第一步:下面是我们使用的html代码:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>React Prism | tomchentw</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/themes/prism.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/prism.min.js"></script>
</head>
<body>
<div id="react-content"></div>
</body>
<\/html>
这一步我们引入了prism.js和prism.css两部分的代码。
第二步:我们使用react-prism组件来显示代码
import PrismCode from "react-prism";
import React from "react";
import ReactDOM from "react-dom";
export default class Test extends React.Component{
render () {
return (
<div className="container">
<PrismCode component="pre" className="language-jsx">
{
` import { Breadcrumb } from 'antd';
ReactDOM.render(
<Breadcrumb>
<Breadcrumb.Item>Home</Breadcrumb.Item>
<Breadcrumb.Item><a href="">Application Center</a></Breadcrumb.Item>
<Breadcrumb.Item><a href="
">Application List</a></Breadcrumb.Item>
<Breadcrumb.Item>An Application</Breadcrumb.Item>
</Breadcrumb>
, mountNode);
`}
</PrismCode>
</div>
);
}
}
ReactDOM.render(<Test/>,document.getElementById('react-content'));
到了这一步,可能你的jsx还是无法正常高亮的,一个很可能的原因在于你打包成的最终的prism.js没有对于jsx的处理的插件。此时打开这个页面,然后将languages部分将React jsx
勾选上,然后点击下载。将最新的prism.js和prism.css引入到页面中就可以了。
最后:我们分析下react-prism这个组件的代码:
import React, { PureComponent } from "react";
import { PropTypes } from "prop-types";
export default class PrismCode extends PureComponent {
static propTypes = {
async: PropTypes.bool,
className: PropTypes.string,
children: PropTypes.any,
component: PropTypes.node,
};
//(1)默认指定组件的props
static defaultProps = {
component: `code`,
}
componentDidMount() {
this._hightlight();
}
//(2)在componentDidMount和componentDidUpdate中我们都要重新高亮显示
componentDidUpdate() {
this._hightlight();
}
//(5)此时提供的highlightElement方法就是高亮某一个element中的代码
//Prism.highlightElement(element, async, callback)
_hightlight() {
Prism.highlightElement(this._domNode, this.props.async);
}
//(3)当组件加载完毕,我们通过ref来将该组件生成的DOM节点获取到
_handleRefMount = (domNode) => {
this._domNode = domNode
}
render() {
const { className, component: Wrapper, children } = this.props;
//(4)将我们的this.props.component赋值到Wrapper变量上,后续可以直接使用Wrapper变量
return (
<Wrapper
ref={this._handleRefMount}
className={className}
>
{children}
<\/Wrapper>
);
}
}
其中有以下几个知识点:
(1)如果是ES6或者函数组件可以使用defaultProps
static defaultProps = {
component: `code`,
}
上面是使用static在组件内部指定的,后面还可以在组件外直接指定:
Greeting.defaultProps = {
name: 'Mary'
};
(2)componentDidMount和componentDidUpdate中都需要重新调用高亮显示方法
(3)其他内容已经在上面分析过了,API请参考这里。
import hljs from "highlight.js/lib/highlight";
import javascript from "highlight.js/lib/languages/javascript";
import css from "highlight.js/lib/languages/css";
highlightCode = () => {
const domNode = ReactDOM.findDOMNode(this);
//这里不再是domNode.querySelectorAll('pre code')
const nodes = domNode.querySelectorAll("code");
// 注册语言
hljs.registerLanguage("javascript", javascript);
hljs.registerLanguage("css", css);
for (let i = 0; i < nodes.length; i++) {
// console.log("nodes[i]==", nodes[i]);
const tagName = nodes[i].tagName;
// code标签
let innerHTML = nodes[i].innerHTML;
// console.log("获取焦点的inerinnerHTML为==", innerHTML);
innerHTML = innerHTML.replace(/(silk|npm|propTypes)/g, function($1) {
return `<span style="color:red">${$1}</span>`;
});
// console.log("获取焦点的inerinnerHTML的replaced为==", innerHTML);
nodes[i].innerHTML = innerHTML;
hljs.highlightBlock(nodes[i]);
}
// console.log("高亮后的node为====>", nodes);
const aTags = domNode.querySelectorAll("a");
//以这种方式来解决浏览器的后退问题,即a标签以新页面打开
for (let j = 0; j < aTags.length; j++) {
if (!aTags[j].getAttribute("target")) {
aTags[j].setAttribute("target", "_blank");
}
}
};
上面这种方式就是通过操作DOM来高亮特定的关键字。关键点在于字符串操作。
const code =
typeof stringOrObjectSchema == "object"
? serialize(stringOrObjectSchema, { space: 2 })
: stringOrObjectSchema;
this.CodeMirror.codeMirror.setValue(code);
// 设置值
this.CodeMirror.codeMirror.getValue();
// 获取值
<CodeMirror
options={optionsMirror}
mode="text/typescript-jsx"
defaultValue={this.state.code}
ref={CodeMirror => {
this.CodeMirror = CodeMirror;
}}
onChange={this.onSchemaChange}
\/>
SilverDemo.js:228 Uncaught TypeError: CodeMirror.fromTextArea is not a function at SilverDemo.componentDidMount (SilverDemo.js:228) at commitLifeCycles (react-dom.development.js:16227) at commitAllLifeCycles (react-dom.development.js:17592) at HTMLUnknownElement.callCallback (react-dom.development.js:149) at Object.invokeGuardedCallbackDev (react-dom.development.js:199) at invokeGuardedCallback (react-dom.development.js:256) at commitRoot (react-dom.development.js:17788) at completeRoot (react-dom.development.js:19240) at performWorkOnRoot (react-dom.development.js:19169) at performWork (react-dom.development.js:19077)
最后发现我一开始引入的5.2.0,但是后面又引入了6.0.1版本,直接删除后者就好了。
参考资料: