起因
我在对接流式读取后有一个复制按钮,点击复制的是纯文本,整个都黏在一起了,非常土,因此秉持着不懂就问的精神,我问了豆包大神,一开始它给我的是marked方案,效果不彳亍,一些行首尾空格都给我干掉了。后面让它统一使用unified链式处理就彳亍了
代码
/**
* 用 unified 解析 Markdown,返回 HTML 和保留格式的纯文本
* @param markdown 原始 Markdown 文本
* @returns { html: string, text: string } 解析后的 HTML 和纯文本
*/
async function parseMarkdownWithUnified(markdown: string) {
if (!markdown) return { html: '', text: '' }
try {
// 1. 移除 rehype-format(避免自动添加多余缩进/换行)
const processed = await unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkRehype)
// 去掉 rehype-format,避免 HTML 自动格式化导致多余空白
.use(rehypeStringify)
.process(markdown)
const html = String(processed)
const tempElement = document.createElement('div')
tempElement.innerHTML = html
// 2. 关键:清理纯文本的多余空白(保留必要换行,合并连续空行)
let text = tempElement.textContent || ''
// 正则规则:
// - 合并连续的多个空行(保留1个换行)
// - 移除行首/行尾的多余空格(保留缩进的必要空格)
text = text.replace(/\n{3,}/g, '\n\n') // 连续3个以上换行 → 保留2个(段落间距)
return { html, text }
} catch (err) {
console.error('unified 解析 Markdown 失败:', err)
// 降级时也清理空白
let fallbackText = markdown.replace(/\n{3,}/g, '\n\n').trim()
return { html: markdown, text: fallbackText }
}
}
async function handleCopy() {
try {
const rawMarkdown = props.msg.content || ''
if (!rawMarkdown) {
showSuccessToast('暂无内容可复制')
return
}
const { html, text } = await parseMarkdownWithUnified(rawMarkdown)
if (navigator.clipboard && window.ClipboardItem) {
await navigator.clipboard.write([
new ClipboardItem({
'text/plain': new Blob([text], { type: 'text/plain' }),
'text/html': new Blob([html], { type: 'text/html' })
})
])
showSuccessToast('已复制到剪贴板')
} else {
const tempTextarea = document.createElement('textarea')
tempTextarea.style.position = 'absolute'
tempTextarea.style.left = '-9999px'
tempTextarea.style.top = '-9999px'
tempTextarea.value = text
document.body.appendChild(tempTextarea)
tempTextarea.select()
tempTextarea.setSelectionRange(0, tempTextarea.value.length)
const success = document.execCommand('copy')
if (success) {
showSuccessToast('已复制到剪贴板')
} else {
throw new Error('execCommand 复制失败')
}
document.body.removeChild(tempTextarea)
}
} catch (err) {
console.error('复制失败:', err)
showSuccessToast('复制失败,请手动复制')
}
}
效果
结论
我的评价给到“夯”,我是写不出这样的代码,都不敢想啊