如何在React Markdown文件中添加「复制代码片段」功能?
为React渲染的Markdown文档添加代码复制功能
步骤1:安装依赖
先装好用的工具库,覆盖Markdown渲染、代码高亮和复制功能:
npm install react-markdown react-syntax-highlighter clipboard
步骤2:创建带复制按钮的代码块组件
新建CodeBlockWithCopy.jsx组件,把代码块和复制功能整合到一起:
import { useState } from 'react'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; import ClipboardJS from 'clipboard'; const CodeBlockWithCopy = ({ language, value }) => { const [copyStatus, setCopyStatus] = useState('复制'); const handleCopy = () => { const codeId = `code-${Date.now()}`; const clipboard = new ClipboardJS(`#copy-${codeId}`, { text: () => value.trim() }); clipboard.on('success', () => { setCopyStatus('已复制'); setTimeout(() => setCopyStatus('复制'), 2000); clipboard.destroy(); }); clipboard.on('error', () => { setCopyStatus('复制失败'); setTimeout(() => setCopyStatus('复制'), 2000); clipboard.destroy(); }); }; const codeId = `code-${Date.now()}`; return ( <div style={{ position: 'relative', margin: '1rem 0' }}> <button id={`copy-${codeId}`} onClick={handleCopy} style={{ position: 'absolute', top: '8px', right: '8px', padding: '4px 8px', fontSize: '12px', cursor: 'pointer', backgroundColor: '#333', color: '#fff', border: 'none', borderRadius: '4px' }} > {copyStatus} </button> <SyntaxHighlighter language={language} style={vscDarkPlus} customStyle={{ borderRadius: '4px' }} id={codeId} > {value.trim()} </SyntaxHighlighter> </div> ); }; export default CodeBlockWithCopy;
步骤3:渲染单个Markdown页面
以page1.md为例,写一个页面组件,用自定义的代码块替换默认渲染:
import ReactMarkdown from 'react-markdown'; import CodeBlockWithCopy from './CodeBlockWithCopy'; import page1Content from './page1.md?raw'; // Vite环境加?raw,Webpack需配置raw-loader const Page1 = () => { return ( <div style={{ maxWidth: '800px', margin: '0 auto', padding: '2rem' }}> <ReactMarkdown components={{ code({ node, inline, className, children, ...props }) { const match = /language-(\w+)/.exec(className || ''); // 只给非行内的代码块加复制按钮 return !inline && match ? ( <CodeBlockWithCopy language={match[1]} value={String(children).replace(/\n$/, '')} {...props} /> ) : ( <code className={className} {...props}> {children} </code> ); } }} > {page1Content} </ReactMarkdown> </div> ); }; export default Page1;
步骤4:适配多个Markdown文件
其他MD页面直接复用上面的逻辑,替换导入的MD文件就行。比如Page2:
import ReactMarkdown from 'react-markdown'; import CodeBlockWithCopy from './CodeBlockWithCopy'; import page2Content from './page2.md?raw'; const Page2 = () => { return ( <div style={{ maxWidth: '800px', margin: '0 auto', padding: '2rem' }}> <ReactMarkdown components={{ code({ node, inline, className, children, ...props }) { const match = /language-(\w+)/.exec(className || ''); return !inline && match ? ( <CodeBlockWithCopy language={match[1]} value={String(children).replace(/\n$/, '')} {...props} /> ) : ( <code className={className} {...props}> {children} </code> ); } }} > {page2Content} </ReactMarkdown> </div> ); }; export default Page2;
额外说明
- 用Webpack的话,得装
raw-loader并配置配置文件,才能把MD文件导入成纯文本。 - 按钮样式、代码高亮主题都可以自己改,比如换
react-syntax-highlighter里的其他主题。
内容的提问来源于stack exchange,提问作者Mahendran V M
相关产品推荐
相关产品推荐

