前言
在markdown 语法中我们可以实现标题、表格、图片等功能的实现,但同样有一些定制化的表现形式难以在markdown中实现,最近就遇到一个需求要在markdown渲染器中展示图表,搜阅了很多文档并没有找到现成的实现方法,最终在react-mardown官方文档中受到启发并实现了这个需求。
react-markdown
react-markdown 是React生态中的一个三方库,主要功能是渲染markdown语法的文本为markdown的预览模式。 使用方法也比较简单:
import React, { FC } from 'react';
import Markdown from 'react-markdown';
import styled from 'styled-components';
interface MDPropsType {}
/** */
export const MD: FC<MDPropsType> = (props) => {
const markdown = `
# 前言
在markdown 语法中我们可以实现标题、表格、图片等功能的实现,但同样有一些定制化的表现形式难以在markdown中实现,最近就遇到一个需求要在markdown渲染器中展示图表,搜阅了很多文档并没有找到现成的实现方法,最终在[react-mardown官方文档](https://github.com/remarkjs/react-markdown)中受到启发并实现了这个需求。
## react-markdown
react-markdown 是React生态中的一个三方库,主要功能是渲染markdown语法的文本为markdown的预览模式。
使用方法也比较简单:
\`\`\`js
console.log("Hello, Word!")
\`\`\`
`;
return (
<MDContain>
<Markdown children={markdown}></Markdown>
</MDContain>
);
};
//style_components
const MDContain = styled.div`
width: 100%;
height: 100%;
`;
这里的显示结果是没有样式的,可以去引用三方的样式或者说是自己去写样式
在此处我采用的是引用github markdown的样式
这里可以看到代码块是没有样式和高亮的
react-mardown官方文档中有提到解决方案
当然,其他插件也可以使用:
按照官方文档的指引去完善代码
- npm i remark-gfm react-syntax-highlighter --save-dev
- 完善代码
import React, { FC } from 'react';
import Markdown from 'react-markdown';
import styled from 'styled-components';
import remarkGfm from 'remark-gfm';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
interface MDPropsType {}
/** */
export const MD: FC<MDPropsType> = (props) => {
const markdown = `
# 前言
在markdown 语法中我们可以实现标题、表格、图片等功能的实现,但同样有一些定制化的表现形式难以在markdown中实现,最近就遇到一个需求要在markdown渲染器中展示图表,搜阅了很多文档并没有找到现成的实现方法,最终在[react-mardown官方文档](https://github.com/remarkjs/react-markdown)中受到启发并实现了这个需求。
## react-markdown
react-markdown 是React生态中的一个三方库,主要功能是渲染markdown语法的文本为markdown的预览模式。
使用方法也比较简单:
\`\`\`js
console.log("Hello, Word!")
\`\`\`
`;
return (
<MDContain>
<Markdown
children={markdown}
remarkPlugins={[remarkGfm]}
components={{
code(props) {
const { children, className, node, ...rest } = props;
const match = /language-(\w+)/.exec(className || '');
return match ? (
<SyntaxHighlighter
{...rest}
PreTag='div'
children={String(children).replace(/\n$/, '')}
language={match[1]}
style={atomDark}
/>
) : (
<code {...rest} className={className}>
{children}
</code>
);
}
}}></Markdown>
</MDContain>
);
};
//style_components
const MDContain = styled.div`
width: 100%;
height: 100%;
`;
结果是这个样子的
观察代码高亮的插件实现不难发现其中的原理
在components对象的的code方法上可以通过className去捕获对应的语,这里我们用```echarts来定义语言
而children就是代码块里面的内容,这里我们用json来保存图标数据
return 会返回如何渲染
那么我们可以写一个Echarts组件接收 option 内容去渲染图表
import React, { FC, useEffect, useRef } from 'react';
import Markdown from 'react-markdown';
import styled from 'styled-components';
import remarkGfm from 'remark-gfm';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
import * as echarts from 'echarts';
interface MDPropsType {}
/** */
export const MD: FC<MDPropsType> = (props) => {
const markdown = `
# 前言
在markdown 语法中我们可以实现标题、表格、图片等功能的实现,但同样有一些定制化的表现形式难以在markdown中实现,最近就遇到一个需求要在markdown渲染器中展示图表,搜阅了很多文档并没有找到现成的实现方法,最终在[react-mardown官方文档](https://github.com/remarkjs/react-markdown)中受到启发并实现了这个需求。
## react-markdown
react-markdown 是React生态中的一个三方库,主要功能是渲染markdown语法的文本为markdown的预览模式。
使用方法也比较简单:
\`\`\`js
console.log("Hello, Word!")
\`\`\`
\`\`\`echarts
{"xAxis":{"type":"category","data":["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]},"yAxis":{"type":"value"},"series":[{"data":[120,200,150,80,70,110,130],"type":"bar"}]}
\`\`\`
`;
return (
<MDContain>
<Markdown
children={markdown}
remarkPlugins={[remarkGfm]}
components={{
code(props) {
const { children, className, node, ...rest } = props;
const match = /language-(\w+)/.exec(className || '');
if (!match) {
return (
<code {...rest} className={className}>
{children}
</code>
);
}
if (match[1] === 'echarts') {
return <ChartsRende option={JSON.parse(children)}></ChartsRende>;
}
return (
<SyntaxHighlighter
{...rest}
PreTag='div'
children={String(children).replace(/\n$/, '')}
language={match[1]}
style={atomDark}
/>
);
}
}}></Markdown>
</MDContain>
);
};
//style_components
const MDContain = styled.div`
width: 100%;
height: 100%;
`;
const ChartsRende: React.FC<{ option: echarts.EChartsOption }> = ({ option }) => {
const chartRef = useRef<echarts.ECharts>();
const domRef = useRef<HTMLElement>();
useEffect(() => {
chartRef.current = echarts.init(domRef.current);
chartRef.current.setOption(option);
}, [option]);
return <div ref={domRef} style={{ height: 300 }} className='chart_contain'></div>;
};
这样就得出了在markdown中显示的Echarts
在最后针对Option中有函数和三方库的注册以及时间戳函数的适配做一点补充
前言
这个其实是属于如何开发一个类似Echarts官网的在线编辑器的范畴了,计划等搬砖有空了提一个专题进行讲解。
有什么?
我们现在有的只是一串字符串。
字符串不仅仅是简单的JSON,是一串可执行的JavaScript代码,name去执行这串代码有两种方案。
- eval函数执行并返回结果(这个eval可能会有很多副作用,这里不做赘述)
- new Function() 创造函数并执行代码返回配置(这里利用了Function的一种特殊构造,虽然也存在一些副作用,但整体优于eval)
实现方式
首先我们先尝试一下:
const getOption = new Function(
`try{
const option = {test:'test'}
;return option
} catch(e){
console.log(e)
}`
)
console.log(getOption()) //{test: 'test'}
那么搞一个一个配置代码:
我们将Makdown的字符串修改为这样:
const markdown = `
# 前言
在markdown 语法中我们可以实现标题、表格、图片等功能的实现,但同样有一些定制化的表现形式难以在markdown中实现,最近就遇到一个需求要在markdown渲染器中展示图表,搜阅了很多文档并没有找到现成的实现方法,最终在[react-mardown官方文档](https://github.com/remarkjs/react-markdown)中受到启发并实现了这个需求。
## react-markdown
react-markdown 是React生态中的一个三方库,主要功能是渲染markdown语法的文本为markdown的预览模式。
使用方法也比较简单:
\`\`\`js
console.log("Hello, Word!")
\`\`\`
\`\`\`echarts
let option = {"xAxis":{"type":"category","data":["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]},"yAxis":{"type":"value"},"series":[{"data":[120,200,150,80,70,110,130],"type":"bar"}]}
\`\`\`
`;
由于这个option是一个可执行的js代码并不是我们上文拿到的JSON,那么我们可以将这串js 代码贫瘠成一个函数并返回Option:
function getOption () {
let option = {"xAxis":{"type":"category","data":["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]},"yAxis":{"type":"value"},"series":[{"data":[120,200,150,80,70,110,130],"type":"bar"}]}
//注意这里应该将option返回
;return option;
}
这样可以屁出一个可执行的函数字符串,但是不能直接执行,就要借助new Function(就是可以直接用函数体字符串进行构造)
const getOption = new Function(`
let option = {"xAxis":{"type":"category","data":["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]},"yAxis":{"type":"value"},"series":[{"data":[120,200,150,80,70,110,130],"type":"bar"}]}
//注意这里应该将option返回
;return option;`)
//测试
console.log(getOption())
这样可以得到一个option配置,但是还有不足,在Echarts中还会有很多外部库需要使用比如Ajax,Axios,echarts-gl,echarts等用户的公共方法,特别是Echarts官方经常使用myChart,那么就需要给函数的作用域追加:(这里举例一些)
import * as echarts from 'echarts';
import 'echarts-gl';
const getOption = new Function("myChart", 'echarts', `${option};return option;`)
try {
myChart(getOption(myChart,echarts))
}catch(e){
console.log(e)
}
最后代码是这个样子:
import React, { FC, useEffect, useRef } from 'react';
import Markdown from 'react-markdown';
import styled from 'styled-components';
import remarkGfm from 'remark-gfm';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
import * as echarts from 'echarts';
import 'echarts-gl';
import 'src/assets/css/github-markdown-css.css';
interface MDPropsType {}
/** */
export const MD: FC<MDPropsType> = (props) => {
const markdown = `
# 前言
在markdown 语法中我们可以实现标题、表格、图片等功能的实现,但同样有一些定制化的表现形式难以在markdown中实现,最近就遇到一个需求要在markdown渲染器中展示图表,搜阅了很多文档并没有找到现成的实现方法,最终在[react-mardown官方文档](https://github.com/remarkjs/react-markdown)中受到启发并实现了这个需求。
## react-markdown
react-markdown 是React生态中的一个三方库,主要功能是渲染markdown语法的文本为markdown的预览模式。
使用方法也比较简单:
\`\`\`js
console.log("Hello, Word!")
\`\`\`
\`\`\`echarts
let option = {"xAxis":{"type":"category","data":["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]},"yAxis":{"type":"value"},"series":[{"data":[120,200,150,80,70,110,130],"type":"bar"}]}
\`\`\`
`;
return (
<MDContain>
<Markdown
children={markdown}
remarkPlugins={[remarkGfm]}
components={{
code(props) {
const { children, className, node, ...rest } = props;
const match = /language-(\w+)/.exec(className || '');
if (!match) {
return (
<code {...rest} className={className}>
{children}
</code>
);
}
if (match[1] === 'echarts') {
return (
<code {...rest} className={className}>
<ChartsRende option={children}></ChartsRende>
</code>
);
}
return (
<SyntaxHighlighter
{...rest}
PreTag='div'
children={String(children).replace(/\n$/, '')}
language={match[1]}
style={atomDark}
/>
);
}
}}></Markdown>
</MDContain>
);
};
//style_components
const MDContain = styled.div`
width: 100%;
height: 100%;
`;
const ChartsRende: React.FC<{ option: echarts.EChartsOption }> = ({ option }) => {
const chartRef = useRef<echarts.ECharts>();
const domRef = useRef<HTMLElement>();
useEffect(() => {
chartRef.current = echarts.init(domRef.current);
const getOption = new Function('myChart', 'echarts', `${option};return option`);
try {
chartRef.current.setOption(getOption(chartRef.current, echarts));
} catch (e) {
console.log(e);
}
}, [option]);
return <div ref={domRef} style={{ height: 300 }} className='chart_contain'></div>;
};
结果是这个样子啦:
最后感觉不是什么都要交(我才不惯着你们)
有一个时间戳函数的bug
我就只放代码截图自己搞,授人以渔,不授人予鱼