跳到主要内容
代码工具2026-07-265 分钟阅读

HTML 转 JSX 指南:属性、style、事件与注释

为什么需要 HTML 转 JSX

在 React 开发中,我们经常需要把现成的 HTML 嵌进组件:设计稿导出、旧项目片段、UI 文档示例。直接粘贴进 JSX,控制台通常会先甩出:

Warning: Invalid DOM property `class`. Did you mean `className`?

JSX 长得像 HTML,本质却是 JavaScript 语法扩展,属性名与写法有一套独立规则。不想逐项手改时,可以用 HTML 转 JSX 工具:className、驼峰属性、style 对象、事件、注释与自闭合标签会一并处理。

HTML 与 JSX 的核心差异

1. class → className

class 是 JavaScript 保留字,JSX 里要用 className:

<!-- HTML -->
<div class="container card shadow">
  <p class="text-sm text-gray-600">Hello</p>
</div>
{/* JSX */}
<div className="container card shadow">
  <p className="text-sm text-gray-600">Hello</p>
</div>

2. style:字符串 → 对象

HTML 的 style 是字符串;JSX 要求对象,且 CSS 属性名用驼峰:

<!-- HTML -->
<div style="margin-top: 20px; background-color: #f0f0f0; font-size: 14px;">
  content
</div>
{/* JSX */}
<div style={{ marginTop: '20px', backgroundColor: '#f0f0f0', fontSize: '14px' }}>
  content
</div>

外层 {{ }}:外层是 JSX 表达式,内层才是对象字面量。连字符属性(margin-top)必须写成 marginTop,否则 React 可能静默忽略。

3. for → htmlFor

<label> 的 for 同样因保留字改名:

<!-- HTML -->
<label for="email">Email</label>
<input id="email" type="email">
{/* JSX */}
<label htmlFor="email">Email</label>
<input id="email" type="email" />

4. 自闭合标签

HTML 允许 <br>、<img>、<input> 不写闭合;JSX 要求标签闭合。没有子节点时用自闭合:

<!-- HTML -->
<img src="logo.png" alt="logo">
<br>
<input type="text" placeholder="Enter name">
<meta charset="utf-8">
{/* JSX */}
<img src="logo.png" alt="logo" />
<br />
<input type="text" placeholder="Enter name" />
<meta charSet="utf-8" />

charset 也要写成 charSet。若源码里已经是 <img />,转换器不应再叠成 <img / />。

5. 事件处理

HTML 事件全小写、值是字符串;JSX 用驼峰,值是函数引用或表达式:

<button onclick="handleClick()">点击我</button>
<button onClick={handleClick}>点击我</button>
HTML 属性JSX 属性
onclickonClick
onchangeonChange
onsubmitonSubmit
onkeydownonKeyDown
onmouseoveronMouseOver
onfocusonFocus
onbluronBlur

转换器可以把 onclick="handleClick()" 收成 onClick={handleClick},复杂语句则包成 () => { ... }。贴进组件后仍要确认作用域里确有对应函数,并把依赖全局脚本的写法改成 props / hooks。

6. 其他属性名差异

HTML 属性JSX 属性说明
tabindextabIndex驼峰
readonlyreadOnly驼峰;无值布尔写法也会改
maxlengthmaxLength驼峰
cellspacingcellSpacing驼峰
colspan / rowspancolSpan / rowSpan驼峰
contenteditablecontentEditable驼峰
crossorigincrossOrigin驼峰
autocompleteautoComplete驼峰
autofocusautoFocus驼峰
autoplayautoPlay驼峰
acceptcharsetacceptCharset驼峰
defaultvaluedefaultValue非受控 input / textarea
defaultcheckeddefaultChecked非受控 checkbox / radio

多词 HTML 属性在 JSX 里几乎都要驼峰化。

7. SVG 短横线属性

SVG 大量使用 stroke-width、fill-opacity、clip-path。React DOM 期望驼峰(strokeWidth 等);data-* / aria-* 则保持原样。迁移图标片段时,别只改外层 div,路径属性也要一起过一遍。

常见转换陷阱

注释写法不同

<!-- HTML 注释 -->
{/* JSX 注释 */}

条件注释、IE 专用标记在 React 里没有对应物,应删除或改写。

布尔属性

HTML 里属性存在即生效;JSX 可写布尔值,也可写无值属性:

<input disabled>
<input checked>
<input readonly>
<input disabled />
<input checked />
<input readOnly />

dangerouslySetInnerHTML

需要注入原始 HTML(富文本)时,不能直接设 innerHTML,要用:

<div dangerouslySetInnerHTML={{ __html: '<p>原始 HTML 内容</p>' }} />

API 名称刻意丑陋,提醒 XSS 风险:只对可信内容使用,并对不可信输入做消毒。

正则转换的边界

在线转换器多是属性层重写,不是完整 HTML 解析器。结构良好的片段效果好;含 Vue/Angular 模板语法、严重残缺、或属性值里裸露 > 的标记,应先清理/格式化再转。整站静态页更适合拆成小组件逐步迁移,而不是一次粘贴整页。

非受控默认值 vs 受控状态

设计稿常导出 <input value="…"> 或 <input checked>。在 React 里,只有 value / checked、没有 onChange 时,字段会变成只读(受控但值冻住了)。HTML 里那是「初始值」;React 里那是「永远等于这个值」。

HTML 意图更稳妥的 JSX 起点说明
初始文本defaultValue="…"非受控;值先由 DOM 持有,再按需提升状态
初始勾选defaultCheckedcheckbox / radio 同理
表单实时状态value={x} + onChange受控;你是唯一真相源

源码若已写成 defaultvalue / defaultchecked,转换器会改成驼峰。若导出仍用普通 value / checked 表示初值,粘贴后需自行改名,或在首次渲染前接上受控状态。嵌套难看时,可先走 HTML 格式化。

片段、多根节点与 key

HTML 片段经常是没有包裹层的兄弟节点。JSX 历史上要单一父节点;现在可用 <>…</> 或 React.Fragment。把转换结果放进 map 时,每个顶层项仍需要稳定的 key——转换器不会替你编造。更稳妥的做法是:把粘贴的卡片收进一个组件根,而不是把一堆平铺兄弟直接丢进父级循环。

推荐工作流

  1. 用 HTML 格式化 理清缩进与嵌套(可选)。
  2. 把片段贴进 HTML 转 JSX,检查 className、style、事件、注释与表单默认值。
  3. 复制到组件,接上真实 handler,删掉仍依赖全局脚本的调用。
  4. 需要进文档时,再用 HTML ↔ Markdown 处理说明文。

转换器应能覆盖:class / for、style 对象化、自闭合、属性驼峰(含 defaultValue / acceptCharset)、事件名与简单 handler、注释、常见 SVG 短横线属性。复杂业务逻辑仍要人工组件化。

实现注记

站内转换器是正则重写,不是浏览器 HTML5 解析器:按固定顺序处理属性、style、事件、注释与空元素闭合,并把注释槽位保护起来,避免注释里的 class= 被误改。它不会修复残缺嵌套、Vue/Angular 指令,或未加引号属性值里的 >。取舍是换本地、快速的草稿输出;请在 TypeScript 组件里打开结果核对,不要当成与 DOM 同构的保证。

小结

记住几条原则即可避开大部分坑:

  1. 属性名用驼峰——className、htmlFor、onClick、charSet 等
  2. style 是对象——连字符 CSS 属性同步驼峰
  3. 标签必须闭合——空元素用自闭合,且不要叠 / />
  4. 事件是表达式——字符串脚本要改成函数引用
  5. 注释换写法——<!-- --> → {/* */}

配合本地转换工具,HTML 片段迁进 React 会轻快很多;剩下的是组件边界与状态设计,而不是逐字符改属性名。