如何使用正则与原生JavaScript从纯文本中提取React组件
从混合纯文本中提取React组件方案
首先明确结论:正则无法100%覆盖所有React组件(JSX)的匹配场景。JSX属于上下文无关文法,正则仅能处理正则文法,遇到多层嵌套、props中包含尖括号(如传箭头函数、字符串内含</>)的场景时,必然会出现错配、漏配。
下面按适用场景给三个可落地方案:
方案1:简单场景正则方案
仅适合以下可控场景使用:
- 所有React组件严格遵循大写开头命名规范(React本身的强制要求,可直接和小写原生HTML标签区分)
- 组件props中不会出现未转义的尖括号字符
- 不需要处理超过1层的嵌套组件
核心正则代码:
// 兼容<后带空格的写法,匹配自闭合标签、单层成对标签 const simpleMatchRegex = /<\s*[A-Z][\s\S]*?(?:\/\s*>|<\s*\/\s*[A-Z][a-zA-Z0-9]*\s*>)/g
用示例测试:
- 匹配
< HelloThere />可正常返回完整内容 - 遇到多层嵌套的
<Component><Box>...</Box></Component>会提前截断,无法拿到完整组件块
方案2:无依赖原生JS栈匹配方案(推荐通用场景使用)
不用正则硬扛嵌套逻辑,用栈结构做标签配对,不管嵌套多少层都能准确拿到完整组件块,核心逻辑:
- 遍历文本字符,遇到
<时跳过后续空格,判断后续字符是否为大写开头的组件名 - 遇到开标签(含自闭合标签)时将标签名入栈,栈为空时记录当前位置为组件起始点
- 遇到对应标签名的闭标签时将栈顶元素出栈,栈清空时当前位置即为组件结束点,截取中间内容即可
- 可额外加逻辑跳过props中字符串、注释内的尖括号,避免干扰匹配
核心实现代码:
function extractReactComponents(text) { const result = [] const tagStack = [] let componentStart = -1 const strLen = text.length for (let i = 0; i < strLen; i++) { if (text[i] !== '<') continue // 跳过<后所有空格 let cursor = i + 1 while (cursor < strLen && /\s/.test(text[cursor])) cursor++ // 识别是否为闭合标签 const isCloseTag = text[cursor] === '/' if (isCloseTag) { cursor++ while (cursor < strLen && /\s/.test(text[cursor])) cursor++ } // 匹配大写开头的React组件名 const tagNameMatch = text.slice(cursor).match(/^[A-Z][a-zA-Z0-9]*/) if (!tagNameMatch) continue const currentTagName = tagNameMatch[0] if (!isCloseTag) { // 记录新组件的起始位置 if (tagStack.length === 0) componentStart = i tagStack.push(currentTagName) // 判断是否为自闭合标签 const tagEndPos = text.indexOf('>', i) if (text.slice(i, tagEndPos).trimEnd().endsWith('/')) { tagStack.pop() // 栈空说明匹配到完整自闭合组件 if (tagStack.length === 0) { result.push(text.slice(componentStart, tagEndPos + 1)) componentStart = -1 } } } else { // 闭合标签和栈顶标签匹配则出栈 if (tagStack[tagStack.length - 1] === currentTagName) { tagStack.pop() const tagEndPos = text.indexOf('>', i) // 栈空说明匹配到完整嵌套组件 if (tagStack.length === 0 && componentStart !== -1) { result.push(text.slice(componentStart, tagEndPos + 1)) componentStart = -1 } } } } return result }
这个实现支持任意层级嵌套、带任意props的普通组件和自闭合组件,如果需要兼容props里带尖括号的场景,只需要加一段判断:遍历到标签内的单引号、双引号、反引号时,直接跳到对应引号闭合位置再继续标签判断即可,扩展成本很低。
方案3:复杂JSX场景的工程化方案
如果文本中包含复杂JSX写法(比如带点的嵌套组件名<Layout.Content />、props中传入JSX、泛型组件等),最准确的方案是用JSX解析器将文本中的JSX片段解析为AST,直接提取AST中的JSXElement节点即可,完全不会出现匹配错误,适合本地构建脚本等允许引入依赖的场景。
另外提到的在组件首尾加特殊标记的方案是成本最低、准确率100%的方案,如果可以修改内容生成侧的逻辑,优先选这个,不需要做任何复杂的文本匹配。
内容的提问来源于stack exchange,提问作者jansmolders86
相关产品推荐
相关产品推荐

