主题
如果拉取失败与react基本使用(Day1 2/4)
本页对应 React + TypeScript + 医通项目,共 6 段视频,约 57 分钟。
学习目标
- 能在 VS Code 中通过“首选项→配置用户代码片段”新建或编辑代码片段,正确设置
prefix、scope、body,并使用$1/$2、\t、转义引号生成 React 模板,通过唤词插入并验证效果。 - 能说明并演示 React 容器与根节点一一对应、
render重复调用时后者覆盖前者、根节点不使用body和html,并能根据控制台警告定位问题。 - 能对比虚拟 DOM 与真实 DOM,说明虚拟 DOM 本质是属性较少的对象、可通过
render转为真实 DOM,并能解释直接操作真实 DOM 导致重排重绘而虚拟 DOM 可减少重排重绘。 - 能使用
React.createElement创建无属性 React 元素,正确使用null、空对象或undefined占位第二参数,并验证渲染结果。 - 能使用
className设置样式类,说明其与class的区别及控制台警告原因,并验证最终渲染为class。 - 能通过
React.createElement嵌套创建指定 React 元素结构并渲染到容器,完成轮播图示例作业。
知识点
本节说明与适用场景
本节笔记的主题是“如果拉取失败”,只给出两个处理方案:放弃目录所有变更;删除当前目录后重新克隆。需要先说明:提供的转写稿主要讲 VSCode 代码片段配置、React 注意事项、虚拟 DOM 与真实 DOM、className 等内容,与“拉取失败”不直接对应。为避免编造,下面只依据本节笔记扩写,不把无关转写内容硬接到本节;本节笔记也没有附代码,因此不虚构命令代码。
为什么需要:拉取失败时,本地目录可能已有未提交修改、冲突状态或与远程不一致的内容,继续操作容易让目录更乱。先有一个明确的恢复方案,才能避免误删、误覆盖。
怎么想:先判断本地变更是否还有价值。若确认本地改动都不要,优先用方案一快速回到干净状态;若目录状态已经混乱、方案一不能解决,再考虑方案二重新克隆。
怎么做:不要一看到失败就删目录。先确认当前项目路径、远程仓库地址和本地是否有需要保留的文件,再按下面两个方案选择。
易错点:最容易错的是把“放弃变更”当成可撤销操作,以及删除目录前没有确认路径和备份。
方案一:放弃目录所有变更

为什么需要:当拉取失败是因为本地目录存在不想保留的修改时,放弃变更可以让目录回到版本控制记录的状态,避免本地改动继续阻碍拉取。这个方案适合“本地改坏了、确定不要了”的情况。
怎么想:先问自己:这些改动有没有提交?有没有备份?如果答案都是“没有且不需要”,才选择放弃。图片展示的是放弃目录变更的入口;操作前要确认作用范围是整个目录,而不是单个文件。
怎么做:按照笔记,在版本控制界面中选择“放弃目录所有变更”,让当前目录的变更被清理,然后再重新执行拉取。注意:本节没有给出具体命令,实际以你使用的编辑器或 Git 工具界面为准;不要自行添加未经验证的命令。
易错点:放弃变更通常不可恢复,未提交内容可能直接丢失;不要把“放弃变更”误当成“撤销某一次提交”;如果目录里还有新建但未纳入版本控制的文件,也要单独确认是否需要保留。
方案二:删除当前目录,重新克隆
为什么需要:当本地目录状态已经混乱,或者方案一处理后仍无法正常拉取时,删除当前目录并重新克隆是最彻底的恢复方式。它能让本地重新获得一份干净的远程仓库副本。
怎么想:这个方案风险更高,所以要先确认三件事:当前目录是不是目标项目;里面有没有未备份的笔记、配置或临时文件;删除后是否能按“1-1 步骤”重新克隆。确认无误后再做。
怎么做:按笔记执行:删除当前目录,然后重新执行 1-1 步骤完成克隆。删除前先关闭可能占用该目录的编辑器、终端或预览服务;删除后回到正确的父目录重新克隆,避免克隆到错误位置。
易错点:不要删错同级目录;不要在没有确认远程地址和分支的情况下重新克隆;删除后不要忘记按 1-1 步骤检查项目是否完整。若目录中有课程要求保留的本地文件,应先备份再删除。
选择顺序与检查清单
为什么需要:两个方案风险不同,先选轻量方案可以避免不必要的删除。方案一解决不了再进入方案二,符合“先小后大”的排查思路。
怎么想:推荐顺序是:先确认失败现象和本地变更 → 能放弃就选方案一 → 方案一无效或目录已混乱 → 再删除目录重新克隆。转写稿没有提供本节对应的调试细节,因此这里不补充不存在的报错分析。
怎么做:操作前做检查:当前路径是否正确、是否有未备份文件、是否知道 1-1 克隆步骤、删除后是否能在正确位置恢复。操作后重新拉取或打开项目,确认目录内容正常。
易错点:不要为了省事直接删目录;不要忽略“放弃变更”的不可逆性;不要把本节与转写稿中的 React 内容混在一起。记住本节的核心只有两个恢复动作:放弃目录变更,或删除目录后按 1-1 步骤重新克隆。
1. 导入 React 对象
为什么需要:React 的核心能力由 React 对象提供,后面的元素创建、组件等能力都离不开它。怎么想:先用 script 引入开发版文件,让页面得到一个全局 React。怎么做:在 <head> 中引入 ./lib/react.development.js,并用 console.log(React) 确认。易错点:路径要写对;开发包控制台可能提示正在使用开发环境,项目能正常运行就不必紧张。
2. 导入 ReactDOM 对象
为什么需要:React 负责“描述界面”,真正把内容放到页面 DOM 上,需要 ReactDOM。怎么想:React 是核心,ReactDOM 是操作 DOM 的渲染入口。怎么做:引入 ./lib/react-dom.development.js,用 console.log(ReactDOM) 检查。易错点:不要把它和 React 混用;createRoot、render 都从 ReactDOM 或根节点上使用。
3. 提供渲染舞台
为什么需要:React 渲染必须有一个真实 DOM 容器作为落点。怎么想:先在 HTML 中准备一个普通 div,给它 id="root"。怎么做:写 <div id="root"></div>。易错点:这个容器是真实 DOM;不要一上来就把 body 或 html 当根节点,转写稿中测试过,会把原有结构清掉,风险很大。
4. 创建根节点
为什么需要:createRoot 让 React 和真实 DOM 容器建立联系。怎么想:先拿到真实元素,再创建根节点。怎么做:const root = ReactDOM.createRoot(document.getElementById('root'));。易错点:参数必须是真实 DOM 元素,不是选择器字符串;同一个容器重复 createRoot 会出警告,转写稿中表现为后创建、后渲染的根节点生效,但控制台会提示容器之前已经被创建过。
5. 使用 render 渲染
为什么需要:根节点只是建立联系,真正显示内容要靠 render。怎么想:把要显示的内容交给 root.render(...)。怎么做:root.render('我是react代码');,内容会出现在 div#root 中。易错点:render 可以重复调用,后一次会覆盖前一次;这像舞台换幕,不是简单追加。后续创建 React 元素时,没有属性也要给第二个参数占位,常用 null、空对象或 undefined,保证第三个参数位置正确。
完整示例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--1. 得到一个全局对象 React 提供了react核心的方法-->
<script src="./lib/react.development.js"></script>
<!--1. 得到一个全局对象 ReactDOM,用来操作 dom的 -->
<script src="./lib/react-dom.development.js"></script>
</head>
<body>
<!--2. 提供一个舞台 -->
<div id="root"></div>
<script>
console.log(React);
console.log(ReactDOM);
// 3. 让react 和 dom建立联系
/**
* 创建一个根节点
* create 创建
* root 根
* createRoot 创建一个根节点
*
*/
const root = ReactDOM.createRoot(document.getElementById('root'));
// 4. 在根节点中使用render方法渲染,render的内容会出现在 div#root中
root.render('我是react代码');
</script>
</body>
</html>调试与延伸易错点
课堂先用 VS Code 用户代码片段生成模板,减少手敲 script、舞台和根节点;但核心仍是上面五步。调试时看控制台红色警告:例如重复 createRoot 同一容器,会提示容器之前已经创建过根节点。创建 React 元素时,样式类名要用 className,因为 class 是 JavaScript 保留字;真实 DOM 最终仍显示为 class。还要理解虚拟 DOM:本质是属性较少的对象,最终通过 render 转成真实 DOM;直接操作真实 DOM 的宽高、位置等几何属性,容易引发大量重排重绘,所以 React 先操作虚拟 DOM,再集中渲染。最后的嵌套练习,就是在外层 div 中继续创建子 React 元素,逐层嵌套后交给 root.render。
代码片段:为什么需要、怎么配
为什么需要:每个页面都要写 script、舞台容器等重复结构,手动敲很费劲。怎么想:把固定骨架做成“唤醒词 + 模板”,输入关键词就能展开。怎么做:在 VS Code 里点“文件 → 首选项 → 配置用户代码片段 → 新建代码片段”,命名后回车,会得到一个 JSON 文件;保留对象结构,填写 prefix(唤醒词)、scope(生效文件类型)、body(生成内容数组,每个元素一行)。按转写稿的函数模板,可整理成类似结构:
json
{
"函数": {
"prefix": "func",
"scope": "javascript,typescript",
"body": [
"function $1($2) {",
"\t$3",
"}",
"$4"
],
"description": "快速创建函数"
}
}$1、$2、$3、$4 是光标停留位置,生成后按 Tab 依次跳转;\t 表示制表符缩进。易错点:scope 没包含当前文件类型时,唤醒词不会生效;比如在 HTML 里用只对 JS/TS 生效的 func 就没反应。删除代码片段不能只靠右键,需要“查看 → 外观 → 勾选痕迹导航”看到路径,再按目录找到文件删除;Windows 下 AppData 可能是隐藏目录,需要开启显示隐藏项目。
React 根节点与 render 的注意事项
为什么需要:根节点和容器关系弄错,页面可能能显示但控制台报警,后面排错很费时间。怎么想:一个容器对应一个 createRoot 创建的根;render 可以重复调用,后一次覆盖前一次。怎么做:
js
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(React.createElement('div', null, '第一次'));
root.render(React.createElement('div', null, '第二次'));第二次会覆盖第一次。若对同一个容器重复 createRoot,会出现 “has already been created” 一类警告。根节点不要用 body 或 html,否则可能把原有页面结构清空。易错点:看到红色警告先复制到翻译工具里看关键词;不要养成“能显示就不管警告”的习惯。
虚拟 DOM 与真实 DOM:为什么要用虚拟 DOM
为什么需要:真实 DOM 属性很多,直接改宽高、位置、边框等几何属性,会影响后续元素位置,触发重排和重绘,效率低。怎么想:虚拟 DOM 本质是属性较少的对象,React 元素就是虚拟 DOM;先把变化在对象层面算完,再通过 render 一次性转成真实 DOM。怎么做:理解真实 DOM 是浏览器里的元素对象,虚拟 DOM 是 React 创建的对象;后续用 React.createElement 或 JSX 创建。易错点:虚拟 DOM 不是真实 DOM,不能直接当页面元素操作;重排是重新计算位置,重绘是重新渲染,二者概念不要混。
创建没有属性的 React 元素
为什么需要:createElement 第二个参数是属性位,没有属性也不能省略,否则第三个子元素参数会错位。怎么想:需要一个“空占位”。怎么做:
js
const el = React.createElement('span', null, '我是span');
root.render(el);占位常用 null、{}、undefined。易错点:虽然空数组、数字等也能占位,但容易让人误解为有业务含义,不推荐。
特殊属性 className
为什么需要:React 元素里写 class 会报警,因为 class 是 ES6 定义类的关键字。怎么想:React 元素属性用 className,最终渲染成真实 DOM 时仍是 class。怎么做:
js
const el = React.createElement('div', { className: 'wrapper' }, '内容');易错点:真实 DOM 上写 class,React 元素里写 className,不要反过来。
React 元素嵌套作业思路
为什么需要:练习“子元素也可以是 React 元素”,为后续组件嵌套打基础。怎么想:先创建最内层 h3、p、span,再把它们作为外层 div 的子元素。怎么做:按转写稿结构,外层 div 里再放一个 div,其中包含标题、段落和两个 span;每个子元素都用 React.createElement 创建,最后 root.render 渲染。易错点:子元素不是普通字符串时,不能直接写文字,要继续创建 React 元素。
课堂画面






代码示例
示例一:React 基本使用(完整 HTML 模板)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--1. 得到一个全局对象 React 提供了react核心的方法-->
<script src="./lib/react.development.js"></script>
<!--1. 得到一个全局对象 ReactDOM,用来操作 dom的 -->
<script src="./lib/react-dom.development.js"></script>
</head>
<body>
<!--2. 提供一个舞台 -->
<div id="root"></div>
<script>
console.log(React);
console.log(ReactDOM);
// 3. 让react 和 dom建立联系
/**
* 创建一个根节点
* create 创建
* root 根
* createRoot 创建一个根节点
*/
const root = ReactDOM.createRoot(document.getElementById('root'));
// 4. 在根节点中使用render方法渲染,render的内容会出现在 div#root中
root.render('我是react代码');
</script>
</body>
</html>要点与易错点
- 两个 script 必须先引入
react.development.js,再引入react-dom.development.js,顺序颠倒会直接报错(ReactDOM 依赖全局 React)。 - 路径
./lib/...是相对当前 html 文件的位置,包放错目录就是 404,页面白屏。 ReactDOM.createRoot(document.getElementById('root'))里传的是真实 DOM 容器,容器不存在则拿到null,渲染失败。- 用
react.development.js开发包时,浏览器里 React 图标是红色,上线换生产包才变蓝;控制台出现“正在使用开发环境”的黑色提示属正常,不影响运行。 - 一个容器只能对应一个根节点,重复
createRoot同一个容器虽能渲染(后创建的后生效),但控制台会给出警告。 root.render()可以重复调用,后面的会覆盖前面的内容,相当于同一舞台换下一幕布景。
示例二:创建没有属性的元素 + 特殊属性 className
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>createElement</title>
<style>
.wrapper { color: red; }
</style>
<script src="./lib/react.development.js"></script>
<script src="./lib/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
<script>
const root = ReactDOM.createRoot(document.getElementById('root'));
// 第二个参数是属性,没有属性也必须占位:{} / null / undefined 都可以
const span = React.createElement('span', {}, '我是span');
// class 是 ES6 关键字,React 中要写成 className
const box = React.createElement(
'div',
{ id: 'box', className: 'wrapper' }, // 不要写 class
span
);
root.render(box);
</script>
</body>
</html>要点与易错点
React.createElement(标签名, 属性对象, 子元素...),第二个参数不能省略,否则第三个参数会错位跑到属性位置上去。- 占位推荐用
{}、null、undefined(都表示“空”);用[]、123之类虽然也能跑,但语义有歧义,不推荐。 - 样式类名必须写
className,写class会渲染成功但控制台报Invalid DOM property 'class'. Did you mean 'className'?—— 因为 class 是 JS 保留字。 className渲染到真实 DOM 后,元素上的属性名仍是class,这正是“虚拟 DOM 属性与真实 DOM 属性一一对应”的体现。- 属性对象里除了内置属性(id、className),也可以写自定义属性。
- 多个子元素时,
createElement从第三个参数开始依次往后排,无需数组。
示例三:React 元素嵌套(作业:div > div.swiper > h3/p/span/span)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>元素嵌套</title>
<script src="./lib/react.development.js"></script>
<script src="./lib/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
<script>
const root = ReactDOM.createRoot(document.getElementById('root'));
// 先创建最内层:h3 / p / span / span
const h3 = React.createElement('h3', null, '我是轮播图');
const p = React.createElement('p', null, '我是描述文字');
const prev = React.createElement('span', null, '上一张');
const next = React.createElement('span', null, '下一张');
// 再创建中间层:div.swiper,它的子元素是上面创建的 react 元素
const swiper = React.createElement(
'div',
{ className: 'swiper' },
h3,
p,
prev,
next
);
// 最外层容器
const container = React.createElement('div', { className: 'container' }, swiper);
// 最后一次性渲染到舞台
root.render(container);
</script>
</body>
</html>要点与易错点
- 嵌套的关键是由内向外创建:子元素本身就是
createElement的返回值(一个 React 元素/虚拟 DOM 对象),可以直接当作父元素的第三个参数。 - 子元素是“React 元素”时,不要加引号写成字符串,否则会被当成纯文本渲染出
[object Object]。 - 多个子元素直接按顺序往后传(
h3, p, prev, next),也可以用数组/React.Fragment组织,本阶段按顺序传即可。 - 每个
createElement无属性的位置都要占位(null/{}/undefined),否则参数错位。 - 只要调用一次
root.render()即可,React 会一次性把整棵虚拟 DOM 树转成真实 DOM 挂到#root中,避免多次操作真实 DOM 引发重排重绘。
常见错误
配好代码片段后输入唤醒词毫无反应 现象:在文件里敲
!react没有任何提示弹出。 原因:scope没有覆盖当前文件类型(如在.html文件里用只在javascript生效的片段),或prefix拼写不一致、文件保存后未生效。 修法:核对prefix与scope,把scope补上html/javascript/javascriptreact,保存后在对应类型的文件中触发;仍无效则重开文件或重启编辑器。控制台报 "container has already been passed to createRoot()" 现象:页面能渲染,但控制台出现红色警告,后创建的那个根节点内容生效。 原因:对同一个容器调用了两次
ReactDOM.createRoot,容器与根节点不是一一对应。 修法:一个容器只创建一次根节点,后续更新统一走root.render(...);需要多个区域时给每个区域独立的容器元素。控制台报 "Invalid DOM property
class. Did you meanclassName?" 现象:样式类名看起来加上了,但控制台有警告。 原因:创建 React 元素(虚拟 DOM)时用了 HTML 的class,而虚拟 DOM 属性需与真实 DOM 的className对应。 修法:createElement的属性对象中一律写className;真实 DOM 侧(如querySelector后打印)看到的仍是className,属正常现象。渲染后整个页面原有结构全部消失 现象:刷新后
body里只剩渲染内容,原有的 script、其他标签都没了。 原因:把document.body或document.documentElement当作根容器。 修法:根容器使用自己创建的独立元素(如<div id="root"></div>),绝不用body/html。createElement创建的元素内容不显示或属性错位 现象:标签名对了,但文字没出来,或属性被当成了子元素。 原因:元素没有属性时省略了第二个参数,导致实参与形参位置错位。 修法:第二参数必须占位,推荐null、undefined或{};虽然空数组、数字也能占位,但会产生歧义,不推荐。嵌套结构写成一坨导致渲染异常 现象:想渲染
div > div.swiper > h3 + p + span + span,结果只出来一层或直接报错。 原因:把子元素当成字符串拼接,或没意识到子元素本身也要先createElement成 React 元素再作为 children 传入。 修法:自内向外逐层创建,父元素的 children 位置传已创建好的 React 元素;多个子元素按顺序作为第三、四、五个参数依次传入。
练习
代码片段配置:新建用户代码片段,
prefix设为!react,scope覆盖html、javascript、javascriptreact,body生成"引入两个 script +<div id="root">+<script>标签"的完整模板,要求用\t控制缩进、用$1/$2设置光标停留位置,并在.html与.js两种文件中分别验证生效情况。占位方式对比:用
React.createElement创建一个无属性的span(内容为"我是span"),第二参数分别用null、undefined、{}、[]、123占位各试一次,记录渲染结果,并说明为什么只推荐前三种。重复渲染验证:对同一个根节点连续调用两次
root.render,观察最终显示内容;再创建两个根节点操作同一个容器,记录控制台警告原文,并用一句话说明正确做法。嵌套元素创建:用
createElement创建div.wrapper > div.swiper > (h3 + p + span + span)结构,文字分别为"我是轮播图""描述文字""上一张""下一张",渲染到#root,并检查最终 DOM 中类名是以什么属性名呈现的。根容器边界测试:分别用
document.body和document.documentElement作为容器渲染一次,观察页面原有结构的变化,说明为什么必须使用独立的div#root。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | vscode如何配置代码片段 | 14:18 |
| 2 | 注意事项 | 09:13 |
| 3 | 虚拟dom和真实dom的区别 | 19:58 |
| 4 | 创建没有属性的react元素 | 04:27 |
| 5 | 特殊属性className | 07:21 |
| 6 | react元素嵌套作业 | 01:48 |
小结
- 代码片段三要素是
prefix(唤醒词)、scope(生效文件类型)、body(数组,一个元素一行);$1/$2控制 Tab 光标顺序,\t控制缩进,双引号需转义。 - 容器与根节点必须一一对应,重复
createRoot虽能渲染但会触发警告,更新只应通过root.render完成。 render可重复调用,后一次覆盖前一次——这是后续更新界面的常规方式。- 根容器只能是自己创建的普通元素,用
body或html会清空原有结构,属禁止操作。 createElement的第二个参数不可省略,无属性时用null/undefined/{}占位,保证 children 位置正确。- 虚拟 DOM 属性与真实 DOM 属性一一对应,样式类名在
createElement中必须写className。 - 复杂结构要先自内向外逐层创建 React 元素,再把元素作为 children 传给父级,而不是拼接字符串。
- 控制台红色信息先复制翻译再看,重点掌握
invalid、property、did you mean这类高频调错词汇,能显著提升排错效率。