Skip to content

如果拉取失败与react基本使用(Day1 2/4) ​

本页对应 React + TypeScript + 医通项目,共 6 段视频,约 57 分钟。

学习目标 ​

  1. 能在 VS Code 中通过“首选项→配置用户代码片段”新建或编辑代码片段,正确设置 prefix、scope、body,并使用 $1/$2、\t、转义引号生成 React 模板,通过唤词插入并验证效果。
  2. 能说明并演示 React 容器与根节点一一对应、render 重复调用时后者覆盖前者、根节点不使用 body 和 html,并能根据控制台警告定位问题。
  3. 能对比虚拟 DOM 与真实 DOM,说明虚拟 DOM 本质是属性较少的对象、可通过 render 转为真实 DOM,并能解释直接操作真实 DOM 导致重排重绘而虚拟 DOM 可减少重排重绘。
  4. 能使用 React.createElement 创建无属性 React 元素,正确使用 null、空对象或 undefined 占位第二参数,并验证渲染结果。
  5. 能使用 className 设置样式类,说明其与 class 的区别及控制台警告原因,并验证最终渲染为 class。
  6. 能通过 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 元素。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

示例一: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 引发重排重绘。

常见错误 ​

  1. 配好代码片段后输入唤醒词毫无反应 现象:在文件里敲 !react 没有任何提示弹出。 原因:scope 没有覆盖当前文件类型(如在 .html 文件里用只在 javascript 生效的片段),或 prefix 拼写不一致、文件保存后未生效。 修法:核对 prefix 与 scope,把 scope 补上 html/javascript/javascriptreact,保存后在对应类型的文件中触发;仍无效则重开文件或重启编辑器。

  2. 控制台报 "container has already been passed to createRoot()" 现象:页面能渲染,但控制台出现红色警告,后创建的那个根节点内容生效。 原因:对同一个容器调用了两次 ReactDOM.createRoot,容器与根节点不是一一对应。 修法:一个容器只创建一次根节点,后续更新统一走 root.render(...);需要多个区域时给每个区域独立的容器元素。

  3. 控制台报 "Invalid DOM property class. Did you mean className?" 现象:样式类名看起来加上了,但控制台有警告。 原因:创建 React 元素(虚拟 DOM)时用了 HTML 的 class,而虚拟 DOM 属性需与真实 DOM 的 className 对应。 修法:createElement 的属性对象中一律写 className;真实 DOM 侧(如 querySelector 后打印)看到的仍是 className,属正常现象。

  4. 渲染后整个页面原有结构全部消失 现象:刷新后 body 里只剩渲染内容,原有的 script、其他标签都没了。 原因:把 document.body 或 document.documentElement 当作根容器。 修法:根容器使用自己创建的独立元素(如 <div id="root"></div>),绝不用 body/html。

  5. createElement 创建的元素内容不显示或属性错位 现象:标签名对了,但文字没出来,或属性被当成了子元素。 原因:元素没有属性时省略了第二个参数,导致实参与形参位置错位。 修法:第二参数必须占位,推荐 null、undefined 或 {};虽然空数组、数字也能占位,但会产生歧义,不推荐。

  6. 嵌套结构写成一坨导致渲染异常 现象:想渲染 div > div.swiper > h3 + p + span + span,结果只出来一层或直接报错。 原因:把子元素当成字符串拼接,或没意识到子元素本身也要先 createElement 成 React 元素再作为 children 传入。 修法:自内向外逐层创建,父元素的 children 位置传已创建好的 React 元素;多个子元素按顺序作为第三、四、五个参数依次传入。

练习 ​

  1. 代码片段配置:新建用户代码片段,prefix 设为 !react,scope 覆盖 html、javascript、javascriptreact,body 生成"引入两个 script + <div id="root"> + <script> 标签"的完整模板,要求用 \t 控制缩进、用 $1/$2 设置光标停留位置,并在 .html 与 .js 两种文件中分别验证生效情况。

  2. 占位方式对比:用 React.createElement 创建一个无属性的 span(内容为"我是span"),第二参数分别用 null、undefined、{}、[]、123 占位各试一次,记录渲染结果,并说明为什么只推荐前三种。

  3. 重复渲染验证:对同一个根节点连续调用两次 root.render,观察最终显示内容;再创建两个根节点操作同一个容器,记录控制台警告原文,并用一句话说明正确做法。

  4. 嵌套元素创建:用 createElement 创建 div.wrapper > div.swiper > (h3 + p + span + span) 结构,文字分别为"我是轮播图""描述文字""上一张""下一张",渲染到 #root,并检查最终 DOM 中类名是以什么属性名呈现的。

  5. 根容器边界测试:分别用 document.body 和 document.documentElement 作为容器渲染一次,观察页面原有结构的变化,说明为什么必须使用独立的 div#root。

原视频对照 ​

#视频时长
1vscode如何配置代码片段14:18
2注意事项09:13
3虚拟dom和真实dom的区别19:58
4创建没有属性的react元素04:27
5特殊属性className07:21
6react元素嵌套作业01:48

小结 ​

  1. 代码片段三要素是 prefix(唤醒词)、scope(生效文件类型)、body(数组,一个元素一行);$1/$2 控制 Tab 光标顺序,\t 控制缩进,双引号需转义。
  2. 容器与根节点必须一一对应,重复 createRoot 虽能渲染但会触发警告,更新只应通过 root.render 完成。
  3. render 可重复调用,后一次覆盖前一次——这是后续更新界面的常规方式。
  4. 根容器只能是自己创建的普通元素,用 body 或 html 会清空原有结构,属禁止操作。
  5. createElement 的第二个参数不可省略,无属性时用 null/undefined/{} 占位,保证 children 位置正确。
  6. 虚拟 DOM 属性与真实 DOM 属性一一对应,样式类名在 createElement 中必须写 className。
  7. 复杂结构要先自内向外逐层创建 React 元素,再把元素作为 children 传给父级,而不是拼接字符串。
  8. 控制台红色信息先复制翻译再看,重点掌握 invalid、property、did you mean 这类高频调错词汇,能显著提升排错效率。