在纯HTML页面里跑通React,是理解这个库运行机制最直接的方式。不借助任何脚手架,仅靠几个<script>标签就能让组件渲染出来,但这条路上布满了细节陷阱:JavaScript文件引入顺序不对会报ReactDOM未定义,<script>标签的type属性写错会让JSX原样输出成文字,容器节点的ID对不上则直接白屏。下面把React与HTML集成的完整链路拆开,从文件职责、引入方式到故障排查逐一讲透。

一、React与HTML集成需要引入哪些JavaScript文件
React在设计上被拆成了两个独立的库,这是很多人第一次集成时最容易忽略的事实。react这个文件只提供核心能力,包括组件定义、状态管理、Hooks以及虚拟DOM的比对算法,它本身完全不知道浏览器DOM的存在;而react-dom才是真正把虚拟DOM翻译成真实DOM节点的那一层。换句话说,缺了任何一个,页面都不可能渲染出内容。
第三个文件是Babel。如果你打算在HTML里直接书写JSX语法,浏览器是看不懂的,因为JSX本质上是一层语法糖,必须先被转译成标准的JavaScript函数调用。Babel的浏览器版本可以在页面加载时实时完成这层转译,代价是性能开销大,所以它只适合学习和演示场景。
总结下来,一个能在浏览器中直接打开的React页面,至少需要依次引入三个JavaScript文件:
- react:React核心库,提供组件与状态相关的一切API;
- react-dom:DOM渲染器,负责把组件挂载到页面的真实节点上;
- babel:转译器,让浏览器能够直接执行写在HTML里的JSX代码。
二、完整集成示例:让第一个组件渲染出来
先看一份可以直接保存为HTML文件并双击打开的完整代码。文件引入使用CDN地址,如果你处于离线环境,也可以把这三个文件下载到本地,例如统一放在 C:\react\ 目录下,再把src属性改成对应的本地路径。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>React与HTML集成示例</title>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
return <h1>你好,React</h1>;
}
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<App />);
</script>
</body>
</html>这份代码里有三个关键点值得展开。第一,业务代码所在的<script>标签必须加上type="text/babel",这个属性告诉Babel需要对标签内的内容做转译,漏写它是最典型的白屏原因之一,浏览器会把JSX当作非法JavaScript直接抛出语法错误。
第二,三个库文件的引入顺序不可颠倒。react-dom内部依赖React全局对象,必须排在react之后;Babel则要在你的业务代码被解析之前就位。浏览器按顺序同步执行这些标签,顺序一乱就会报出某某变量未定义的错误。
第三,React 18对挂载API做了一次破坏性变更。旧版本的ReactDOM.render接收两个参数,新版则要求先通过createRoot创建根节点再调用render,两种写法不能混用:
// React 17 及更早版本的写法
ReactDOM.render(
<App />,
document.getElementById('root')
);
// React 18 的写法
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);如果你引入的是18版本的文件,却沿用了17的挂载写法,控制台会给出明确的警告提示,虽然页面可能仍然渲染成功,但并发特性将无法生效,正式环境里必须修正。
三、页面空白的常见原因与逐项排查
集成失败的表现高度一致:页面空白、组件不渲染,或者干脆把JSX代码当文字显示出来。遇到这类情况不要急着改组件代码,先按下面的清单逐项检查,绝大多数问题都能在十分钟内定位。
- 文件路径错误:打开浏览器开发者工具的Network面板,查看三个JavaScript文件是否全部返回200状态码。本地引用时相对路径容易写错,比如文件实际在 ./js/ 目录下而src写成了根路径,404会直接导致后续所有依赖它的代码失效;
- 引入顺序颠倒:react-dom排在react前面,或业务代码排在库文件前面,都会让全局对象在使用的瞬间还不存在;
- type属性遗漏:写了JSX却没加
type="text/babel",浏览器解析到尖括号就直接报语法错误; - 容器ID不匹配:页面里容器元素的id是app,代码里却用
getElementById('root')去取,拿到null后createRoot会直接抛异常; - 版本API混用:18的文件配17的写法,或反过来,表现为报错或警告;
- 脚本执行时机:如果把业务脚本放在容器节点之前,又给
<script>标签加了async属性,DOM还没构建完就去取节点,同样会拿到null。
排查白屏问题的第一动作:打开控制台看报错,打开Network面板看资源加载状态,这两步能解决八成以上的集成故障。
排查时有一个效率很高的技巧:在业务代码的第一行写一句console.log(React, ReactDOM),如果输出undefined,说明问题出在文件引入环节;如果两个对象都正常打印,再把注意力转向容器节点与挂载写法。这种二分法能把故障范围迅速缩小一半。
另外提醒一点,document.getElementById返回null是白屏场景里出镜率最高的直接原因,但它只是表象。可以在控制台手动执行取节点语句验证结果,确认ID拼写、大小写完全一致,再检查<script>标签是否位于容器之后,或者干脆把业务代码统一放到body的最底部,从结构上杜绝时机问题。
四、CDN直引与构建工具的取舍
浏览器端Babel虽然方便,但它要在页面加载时实时编译所有JSX,页面越大卡顿越明显,而且生产环境直接暴露源码也不利于维护。所以在学习阶段过后,正式项目应当切换到构建工具方案。两种方式的差异可以用一张表看清:
| 对比维度 | CDN直引 | 构建工具 |
|---|---|---|
| 上手成本 | 极低,一个HTML文件即可运行 | 需要Node.js环境与工程化配置 |
| JSX处理 | 浏览器端实时转译,性能开销大 | 构建阶段预编译,运行时零开销 |
| 依赖管理 | 手动维护script标签与版本号 | npm统一管理,锁定版本 |
| 适用场景 | 学习、演示、原型验证 | 正式项目与团队协作开发 |
两者的边界其实很清晰:当你需要快速验证一个组件想法、给同事演示某个交互效果,或者在教学环境中讲解React基础概念时,CDN直引是成本最低的选择;一旦项目要长期迭代、涉及路由与状态管理的复杂度上升,就应该迁移到Vite或Create React App这类工具上,让JSX在构建阶段完成编译。
迁移的过程也不复杂。先用npm安装react与react-dom依赖,把组件代码拆分到独立的js或jsx文件中,构建工具会自动处理转译与打包,HTML里只需要保留一个容器节点和一条指向打包产物的脚本引用。原先在纯HTML环境里踩过的那些坑,在工程化方案里大多会被工具自动规避,这也是脚手架流行的根本原因。理解了纯HTML集成的底层逻辑,再回头看脚手架生成的项目结构,你会发现它做的事情本质上没有变,只是把每一步都自动化了。
React JSHTML集成JavaScript渲染修改时间:2026-09-30 04:03:54