You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中安全渲染含HTML标签文本及Medium式高亮实现方案

React安全渲染带HTML标签的内容(无需dangerouslySetInnerHTML)

你已经有了拆分后的令牌数组(比如["Te", "<span>", "x", "</span>", "t"]),可以通过解析令牌并转换成React元素的方式安全渲染,避免直接使用dangerouslySetInnerHTML带来的XSS风险。核心思路是遍历令牌,区分文本和标签,构建嵌套的React元素结构:

import React from 'react';

function parseTokensToReactNodes(tokens) {
  const stack = [];
  const rootNodes = [];
  let currentChildren = rootNodes;

  // 遍历所有令牌,构建DOM结构树
  for (const token of tokens) {
    if (token.startsWith('<') && token.endsWith('>')) {
      const tagMatch = token.match(/<\/?([a-z0-9]+)>/i);
      if (!tagMatch) continue; // 跳过无效标签
      
      const tagName = tagMatch[1];
      if (token.startsWith('</')) {
        // 处理闭合标签:弹出栈,回到父元素的子节点列表
        stack.pop();
        currentChildren = stack.length ? stack[stack.length - 1].children : rootNodes;
      } else {
        // 处理开始标签:创建元素节点,推入栈
        const elementNode = { type: tagName, children: [] };
        currentChildren.push(elementNode);
        stack.push(elementNode);
        currentChildren = elementNode.children;
      }
    } else {
      // 处理文本节点:直接添加到当前子节点列表
      currentChildren.push(token);
    }
  }

  // 将结构树转换为React元素
  function convertToJSX(node) {
    if (typeof node === 'string') return node;
    return React.createElement(node.type, {}, node.children.map(convertToJSX));
  }

  return rootNodes.map(convertToJSX);
}

// 使用示例
const tokens = ["Te", "<span>", "x", "</span>", "t"];
function HighlightedText() {
  return <div>{parseTokensToReactNodes(tokens)}</div>;
}

这个方法完全基于React的虚拟DOM机制,所有标签都是可控创建的,不会引入未知的HTML风险。


实现类似Medium的文本高亮的其他方法

除了掩码数组算法,还有几种更贴合Medium交互式高亮体验的实现思路:

1. Range API直接操作选区

Medium的高亮核心是基于用户选区,利用浏览器原生的Range和Selection API可以直接处理选区:

  • 通过window.getSelection()获取用户选中的文本范围
  • 创建Range对象后,调用surroundContents()方法给选中内容包裹高亮元素(比如<mark>或自定义组件)
  • 在React中配合useRef获取文本容器的DOM节点,确保操作的是真实DOM,同时通过状态同步高亮信息到虚拟DOM

优点:直接对接浏览器原生选区能力,交互流畅;缺点:需要处理React虚拟DOM与真实DOM的同步冲突,适合轻量场景。

2. 基于富文本编辑器库

如果需要支持高亮的编辑、删除、移动等复杂交互,直接基于成熟的富文本编辑器库是更高效的选择:

  • Draft.js:通过自定义InlineStyle标记高亮文本,渲染时映射为对应的样式组件
  • Slate.js:用自定义的mark节点类型存储高亮信息,编辑器自带选区处理和节点管理能力

优点:无需自己处理选区拆分、文本嵌套、编辑同步等复杂逻辑;缺点:需要引入第三方库,学习成本较高,但稳定性和扩展性更强。

3. 文本位置标记法

存储高亮在原始文本中的起始/结束索引,渲染时根据索引拆分文本并包裹高亮组件:

  • 比如原始文本为"Hello World",高亮范围是[3,7],则拆分为["Hel", <mark>lo W</mark>, "orld"]
  • 当文本内容修改时,需要更新高亮的位置索引(可以通过差分算法计算偏移)

优点:数据结构简单,高亮信息易持久化;缺点:需要处理文本编辑后的位置偏移问题,适合文本内容不频繁修改的场景。


内容的提问来源于stack exchange,提问作者IVOBOT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 01:35:20