React 入门
观看超哥视频做的笔记:www.bilibili.com/video/BV1bS…
1. React 简介
为什么学?
- 原生 JS 操作 DOM 繁琐,效率低
- 使用 JS 直接操作 DOM,浏览器会进行大量的重绘重排
- 原生 JS 没有组件化编码方案,代码复用低
React 特点:
- 虚拟 DOM
- 声明式
- 基于组件
- 支持服务器端渲染
React 的一些主要优点?
- 它提高了应用的性能
- 可以方便在客户端和服务器端使用
- 由于使用 JSX,代码的可读性更好
- 使用 React,编写 UI 测试用例变得非常容易
2. React 基础案例
- 首先需要引入几个 react 包
- React 核心库、操作 DOM 的 react 扩展库、将 jsx 转为 js 的 babel 库 【先引入react.development.js,后引入react-dom.development.js】
react.development.js
- react 是 react 核心库,只要使用 react 就必须要引入
- 下载地址:unpkg.com/react@18.0.…
react-dom.development.js
- react-dom 是 react 的 dom 包,使用 react 开发 web 应用时必须引入
- 下载地址:unpkg.com/react-dom@1…
babel.min.js
- 浏览器不能正常识别 JSX,所以当我们使用 JSX 时,还必须引入 babel来完成对代码的编译,将 JSX 转化为 JS。
- babel 下载地址:unpkg.com/babel-stand…
- 创建一个容器
- 创建虚拟DOM,渲染到容器中
<!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>Hello World</title>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="script/react.development.js"></script>
<!-- 引入 react 的 DOM 库 -->
<script type="text/javascript" src="script/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
<script>
// 1. 创建一个react元素
const h1 = React.createElement('h1', {}, 'Hello')
// 2. 获取根元素对应的react元素
const root = ReactDOM.createRoot(document.getElementById('root'))
// 3. 将div渲染到根元素中
root.render(h1)
</script>
</body>
</html>
新方式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>hello_react</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
/* 此处一定要写babel */
//1.创建虚拟DOM
const VDOM = <h1>Hello</h1>
//2.渲染虚拟DOM到页面
const root = ReactDOM.createRoot(document.querySelector('#test'))
root.render(VDOM)
</script>
</body>
</html>
💡 三个API:
-
React.createElement()
-
React.createElement(type, [props], [...children]) -
用来创建 React 元素,React 元素无法修改
- React元素最终会通过虚拟DOM转换为真实的DOM元素
- React元素一旦创建就无法修改,只能通过新创建的元素进行替换
-
参数:
- 元素名(组件名,html 标签必须小写)
-
元素中的属性
- 设置事件时,属性名需要修改为驼峰命名法
- class 属性需要使用 className 来设置
- 元素的子元素(内容)
-
-
ReactDOM.createRoot()
createRoot(container[, options])- 用来创建 React 的根容器,容器用来放置 React 元素
- 需要一个 DOM 元素作为参数
-
ReactDOM.render()
-
root.render(element) -
用来将 React 元素渲染到根元素中,当首次调用时,容器节点里的所有 DOM 元素都会被替换,后续的调用则会使用 React 的 DOM 差分算法(DOM diffing algorithm)进行高效的更新。
- 根元素中所有的内容都会被删除,被 React 元素所替换
- 当重复调用 render() 时,React 会将两次的渲染结果进行比较,
- 它会确保只修改那些发生变化的元素,对 DOM 做最少的修改
-
不会修改容器节点(只会修改容器的子节点)。可以在不覆盖现有子节点的情况下,将组件插入已有的 DOM 节点中。
-
<button id="btn">我是按钮</button>
<div id="root"></div>
<script>
const button = React.createElement('button', {
type: 'button',
// class 在 react 中是类组件关键字,class 属性需要使用 className 来设置
className: 'hello',
// 设置事件时,属性名需要修改为驼峰命名法,而且事件回调要用函数表示
onClick: () => { alert("你好啊~") }
}, '点我一下')
// 元素内容可以放子元素、文本等,逗号隔开
const div = React.createElement('div', {}, '我是一个div', button)
// ReactDOM.render(div, document.getElementById('root')); // 老版本的React中使用方法
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(div)
const btn = document.getElementById('btn')
btn.addEventListener('click', () => {
// React 元素一经创建,无法修改,只能新建元素进行替换
const button = React.createElement('button', {
type: 'button',
className: 'hello',
onClick: () => { alert("你好啊~") }
}, 'click me')
const div = React.createElement('div', {}, '我是一个div', button)
// 修改React元素后,必须重新对根元素进行渲染
// 当调用render渲染页面时,React会自动比较两次渲染的元素,只在真实DOM中更新发生变化的部分,没发生变化的保持不变
root.render(div)
})
</script>
3. JSX 语法
JSX 是 JavaScript 的语法扩展,JSX 使得我们可以以类似于 HTML 的形式去使用 JS。JSX 是 React 中声明式编程(简单理解就是以结果为导向的编程)的体现方式。使用 JSX 将我们所期望的网页结构编写出来,然后 React 再根据 JSX 自动生成 JS 代码。所以我们所编写的 JSX代码,最终都会转换为以调用React.createElement()创建元素的代码。
浏览器并不能正常识别 JSX, JSX 最终需要转换为 JS 代码执行。JSX 是 React.createElement() 的语法糖,React 内部其实是通过 babel 将 JSX 转换为 JS 代码。
<!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>JSX</title>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="script/react.development.js"></script>
<!-- 引入 react 的 DOM 库 -->
<script type="text/javascript" src="script/react-dom.development.js"></script>
<!-- 引入 babel,将 jsx 转为 js -->
<script type="text/javascript" src="script/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<!--设置js代码被babel处理-->
<script type="text/babel">
// 创建一个 React 元素中的属性
// 命令式编程
// const button = React.createElemet('button', {}, '我是按钮')
// 声明式编程
// 在 React 中可以通过 JSX(JS扩展)来创建 React 元素,JSX 需要被翻译成 JS 代码,才可以被 React 执行
// 要在 React 中使用 JSX,必须引入 babel 来完成“翻译”工作
const button = <button>我是按钮</button>
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(button)
</script>
</body>
</html>
👇 注意点:
-
JSX 不是字符串,不要加引号
-
JSX 的 html 标签应该小写,React 组件应该大写开头
-
不能有多个根标签,只能有一个根标签
-
标签中使用
{}嵌入 JS 表达式id = {myId.toUpperCase()}关于JS表达式和JS语句:
- JS表达式:返回一个值,可以放在任何一个需要值的地方 a a+b demo(a) arr.map() function text(){}
- JS语句:if(){} for(){} while(){} swith(){} 不会返回一个值
-
JSX 的标签必须正确结束(自结束标签必须写/)
<input type="text"/> -
布尔类型、Null 以及 Undefined 将会忽略
-
在 JSX 中,属性可以直接在标签中设置
const div = <div id="box" onClick={() => { alert('哈哈') }} className="box1" style={{backgroundColor: "yellowgreen", border: '10px red solid'}} > </div>;- class 需要使用
className代替 - style 必须使用对象设置(
{{}}、驼峰命名)
- class 需要使用
-
注释要写在花括号里
const div = <div> {/*这里注释*/} </div>; -
数组
JSX 允许在模板中插入数组,数组自动展开全部成员
const arr = [ <h1>小丞</h1>, <h2>同学</h2>, ]; const div = <div> {arr} </div> const root = ReactDOM.createRoot(document.getElementById('root')); root.render(div);{} 只能用来放 js 表达式,而不能放语句(if for), 但在语句中可以去操作 JSX
<div id="root"></div> <script type="text/babel"> const data = ['a', 'b', 'c'] const div = <div> <ul> {data.map((item, index) => <li key={index}>{item}</li>)} </ul> </div > const root = ReactDOM.createRoot(document.getElementById('root')); root.render(div); </script>
4. 虚拟 DOM
在 React 中,我们操作的元素被称为 React 元素。React 通过虚拟 DOM 将 React 元素 和 原生 DOM 进行映射,虽然操作的 React 元素,但是这些操作最终都会在真实 DOM 中体现出来
虚拟DOM的好处:
- 降低 API 复杂度
- 解决兼容问题
- 提升性能(减少 DOM 的不必要操作)
每当我们调用 root.render() 时,页面就会发生重新渲染
React 会通过 diff 算法,将新的元素和旧的元素进行比较,只对变化的元素进行修改,没有发生的变化不予处理
diff 算法: 原文链接:blog.csdn.net/weixin_4270…
-
比较两个虚拟 dom 树,对根节点 root 进行执行
patch(oldVnode,newVnode)函数,比较两个根节点是否是相同节点。如果不同,直接替换(新增新的,删除旧的)- 比较节点的 sel 选择器(标签名、id、class)、key 是否一致(都没有定义 key 的话,都为 undefined 也是相等的)
-
如果相同,对两个节点执行
patchVnode(oldVnode, newVnode),比较属性,文本,以及子节点。(新增,删除,修改文本内容)。- 当都存在子节点时,并且 oldVnode === newVnode 为 false 时(如果为 true,代表子级肯定也完全相等),执行
updateChildren函数,去进一步比较他们的子节点。
- 当都存在子节点时,并且 oldVnode === newVnode 为 false 时(如果为 true,代表子级肯定也完全相等),执行
-
首先会将新旧 vnode 的子节点提取出来,并分别加上两个指针oldStart, oldEnd, newStart, newEnd。分别指向第一个节点和最后一个节点。
updateChildren函数的比较分 3 大类。-
如果
oldStart === newStart,oldStart === newEnd,oldEnd === newStart,oldEnd === newEnd这4种情况中任何一种匹配(这里的等于应该是 sel 选择器和 key)。则会执行patchVnode进一步比较。同时指针往中间移动。 -
当
oldStart > oldEnd或者newStart > newEnd时。表示匹配结束。此时,多余的元素删除,新增的元素新增。 -
上面几种情况都不匹配。这个时候 key 是否存在,起到关键性作用了。
-
存在 key 时,直接通过 key 去找到节点原来的位置。如果没有找到,就新增节点。找到了,就移动节点位置,进行比较。查找效率非常高。
-
没有 key,直接新增这个节点。这就导致这个节点下的所有子节点都会被重新新增。会出现明显的性能损耗。
-
比如新旧 dom 中列表的顺序发生改变时,有唯一标识的 key,可以得到性能的优化。
下面的例子中,key 在列表中是唯一的,所以在进行 diff 算法比较时,只需要更新唐僧所在的子节点。若没有唯一 key,则四个 li 标签都需要进行更新。
<body> <button id="btn">点我一下</button> <hr> <div id="root"></div> <script type="text/babel"> //创建一个数据 const data = ['孙悟空', '猪八戒', '沙和尚']; // 创建列表 const list = <ul> {/*data.map(item => <li key={item}>{item}</li>)*/} {data.map((item, index) => <li key={index}>{item}</li>)} </ul>; // 获取根元素 const root = ReactDOM.createRoot(document.getElementById('root')); // 渲染元素 root.render(list); document.getElementById('btn').onclick = function () { // 重新渲染页面 //创建一个数据 const data = ['唐僧', '孙悟空', '猪八戒', '沙和尚']; // 创建列表 const list = <ul> {data.map((item, index) => <li key={item}>{item}</li>)} </ul>; // 渲染元素 root.render(list); </script> </body>
-
-