如何在React文本渲染组件中匹配成对星号内容实现Markdown加粗效果
问题核心原因
你当前的处理逻辑是先按空格拆分每个词再做规则匹配,跨多词的加粗内容(比如这是一整句加粗的文本)会被拆成**这是、一整句、加粗的、文本**多个独立片段,自然无法匹配到成对的星号。同时你当前用的加粗正则是贪婪匹配,即使是单个词内有多个加粗标记也会出现匹配异常。
修改方案
调整处理顺序:先按加粗规则把整行文本分割成「普通文本片段」和「加粗文本片段」交替的结构,对每个片段再执行原有逻辑处理提及、话题标签、URL,就能同时支持单/多词加粗,也不会影响原有功能。
修改后的代码如下:
import { Theme, createStyles, WithStyles, withStyles, Link as MUI_Link, } from "../components/material"; import { split_on_new_line } from "../utils"; import Link from "next/link"; import { RouterProps } from "next/router"; import { Send_to_root_T } from "../pages/_app"; import { get_a_user_info_by_username$$ } from "../utils/api"; const styles = function(theme: Theme) { return createStyles({ wrapper: { marginTop: theme.spacing.unit, marginLeft: theme.spacing.unit, marginRight: theme.spacing.unit, display: "block", }, line: { display: "inline-block", width: "100%", }, boldText: { fontWeight: "bold" } }); }; export interface Props extends WithStyles<typeof styles> { content: string; router: RouterProps; send_to_root: Send_to_root_T; link_class?: string; regular_text_class?: string; } // 正则提到组件外避免重复声明,加粗正则改用非贪婪匹配 const hashtag_regex = /#[^\s!@#$%^&*()=+./,\[{\]};:'"?><،؛؟]+/gi; const url_regex = /[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/gi; const mention_regex = /@[0-9a-zA-Z_](\.?[0-9a-zA-Z-_])*/gi; const bold_regex = /(\*\*.+?\*\*)/g; const bold_strip_regex = /^\*\*(.*)\*\*$/; // 抽取原有的单片段处理逻辑,方便复用 const processTextSegment = (segment: string, props: Props, keyPrefix: string|number) => { return segment.split(" ").map((item, i) => { // 每次匹配前重置正则lastIndex,避免全局匹配的缓存问题 mention_regex.lastIndex = 0; url_regex.lastIndex = 0; hashtag_regex.lastIndex = 0; if (mention_regex.test(item)) { const username = item.slice(1); return ( <span key={`${keyPrefix}-mention-${i}`}> <MUI_Link style={{ cursor: "pointer", }} className={props.link_class} onClick={function(evt: React.MouseEvent) { evt.stopPropagation(); props.send_to_root("SHOW_PROGRESS"); get_a_user_info_by_username$$(username).subscribe({ next: function(result) { const uuid = result.data.uuid; props.router.push( { pathname: `/profile`, query: { user_id: uuid }, }, `/profile/${uuid}`, ); }, }); }} > {`@${username}`} </MUI_Link>{" "} </span> ); } if (url_regex.test(item)) { return ( <span key={`${keyPrefix}-url-${i}`}> <MUI_Link className={props.link_class} target="_blank" referrerPolicy="no-referrer" rel="noopener" href={item.startsWith("http") ? item : `http://${item}`} onClick={function(evt: React.SyntheticEvent) { evt.stopPropagation(); }} style={{ cursor: "pointer" }} > {`${item}`}{" "} </MUI_Link> </span> ); } if (hashtag_regex.test(item)) { return ( <Link href={`/hashtag_posts?hashtag_text=${item.slice(1)}`} as={`/hashtags/${item.slice(1)}/posts`} key={`${keyPrefix}-hashtag-${i}`} > <a style={{ textDecoration: "none" }}> <MUI_Link className={props.link_class} component="span" onClick={function(evt: React.SyntheticEvent) { evt.stopPropagation(); props.send_to_root("SHOW_PROGRESS"); }} style={{ cursor: "pointer" }} > {`${item}`}{" "} </MUI_Link> </a> </Link> ); } return item + " "; }) } function ContentProcessedText(props: Props) { const c = props.classes; const content = props.content; return ( <span className={c.wrapper}> {split_on_new_line(content).map(function(chunk, lineIndex) { // 先按加粗规则分割整行文本 const lineSegments = chunk.split(bold_regex); return ( <span key={lineIndex} className={c.line}> {lineSegments.map((seg, segIndex) => { // 判断当前片段是否是加粗内容 if (bold_strip_regex.test(seg)) { // 提取加粗的文本内容 const boldContent = seg.replace(bold_strip_regex, '$1'); return ( <span key={`line-${lineIndex}-seg-${segIndex}`} className={c.boldText}> {/* 对加粗内容内部也按原有规则处理提及、链接、话题 */} {processTextSegment(boldContent, props, `line-${lineIndex}-bold-${segIndex}`)} </span> ) } // 普通文本直接走原有处理逻辑 return processTextSegment(seg, props, `line-${lineIndex}-normal-${segIndex}`); })} </span> ); })} </span> ); } export default withStyles(styles)(ContentProcessedText);
额外优化点
- 把正则声明提到组件外部,避免每次渲染重复创建正则实例
- 匹配前重置正则的
lastIndex属性,解决全局匹配模式下的缓存错位问题 - 把原有单文本片段的处理逻辑抽成公共函数,普通文本和加粗文本内部都可以复用,支持加粗内容里嵌套提及、话题、链接
- 加粗正则改用非贪婪匹配,避免同一行内多个加粗标记被错误合并
内容的提问来源于stack exchange,提问作者Abolfazl Vaysi
相关产品推荐
相关产品推荐

