如何在React Markdown中渲染自定义短代码为组件?
嗨,我之前刚好碰到过一模一样的需求,其实React Markdown虽然官方文档没直接提自定义短代码的处理,但可以通过两种方式实现——一种是用自定义remark插件解析AST,另一种是预处理文本替换短代码。下面给你详细说说这两种方案:
方案一:自定义remark插件解析短代码(推荐,更安全)
这种方式是通过操作Markdown的抽象语法树(AST)来识别短代码,不会有XSS风险,适合用户提交的不可控内容场景。
步骤1:写一个自定义remark插件
这个插件会遍历AST里的文本节点,匹配[[ table product="mask" ]]这样的短代码,把它转换成自定义的AST节点:
import { visit } from 'unist-util-visit'; // 自定义remark插件,解析table类型的短代码 export function remarkTableShortcodes() { return (tree) => { // 遍历所有文本节点 visit(tree, 'text', (node, index, parent) => { // 匹配[[ table ... ]]格式的短代码 const shortcodeRegex = /\[\[ table (.*?) \]\]/g; let match; const newChildren = []; let lastTextIndex = 0; // 逐个处理匹配到的短代码 while ((match = shortcodeRegex.exec(node.value)) !== null) { // 保留短代码前面的普通文本 if (match.index > lastTextIndex) { newChildren.push({ type: 'text', value: node.value.slice(lastTextIndex, match.index), }); } // 解析短代码里的属性(比如product="mask") const attrsString = match[1]; const attributes = {}; const attrRegex = /(\w+)="([^"]+)"/g; let attrMatch; while ((attrMatch = attrRegex.exec(attrsString)) !== null) { attributes[attrMatch[1]] = attrMatch[2]; } // 添加自定义AST节点,类型设为customTable newChildren.push({ type: 'customTable', data: { hName: 'custom-table', // 对应后续映射的组件标识 hProperties: attributes, }, ...attributes, }); lastTextIndex = match.index + match[0].length; } // 处理剩余的文本内容 if (lastTextIndex < node.value.length) { newChildren.push({ type: 'text', value: node.value.slice(lastTextIndex), }); } // 如果有匹配到短代码,替换原文本节点为新的子节点数组 if (newChildren.length > 0) { parent.children.splice(index, 1, ...newChildren); } }); }; }
步骤2:在React Markdown中使用插件并映射组件
把插件加到remarkPlugins里,再通过components属性把自定义的AST节点映射到你的<Table />组件:
import ReactMarkdown from 'react-markdown'; import { remarkTableShortcodes } from './remarkTableShortcodes'; import Table from './Table'; // 你的自定义Table组件 function MarkdownContent({ markdown }) { return ( <ReactMarkdown remarkPlugins={[remarkTableShortcodes]} components={{ // 把custom-table节点映射到自定义Table组件 'custom-table': (props) => <Table {...props} />, }} > {markdown} </ReactMarkdown> ); }
方案二:预处理Markdown文本(简单快速,适合可控内容)
如果你的Markdown内容是自己生成的、完全可控的,直接预处理文本把短代码替换成React组件标签会更省事,但要注意开启相关插件支持JSX解析:
步骤1:写预处理函数
把[[ table product="mask" ]]替换成React组件的JSX语法:
function replaceTableShortcodes(markdown) { // 正则匹配并替换短代码为<Table />组件 return markdown.replace( /\[\[ table (.*?) \]\]/g, (_, attrs) => `<Table ${attrs} />` ); }
步骤2:配置React Markdown支持JSX解析
需要用到rehype-raw插件来解析原生HTML/JSX,同时通过components映射标签到你的组件:
import ReactMarkdown from 'react-markdown'; import rehypeRaw from 'rehype-raw'; import Table from './Table'; function MarkdownContent({ markdown }) { const processedMarkdown = replaceTableShortcodes(markdown); return ( <ReactMarkdown rehypePlugins={[rehypeRaw]} components={{ // 把<Table />标签映射到自定义组件 Table: Table, }} > {processedMarkdown} </ReactMarkdown> ); }
小提示
- 方案一更安全,适合用户提交的内容,因为是基于AST解析,不会执行恶意代码;
- 方案二实现起来更快,但如果内容不可控,开启
rehype-raw可能会有XSS风险,要谨慎使用。
内容的提问来源于stack exchange,提问作者phanatuan
相关产品推荐
相关产品推荐

