第一章:React 是什么?
1.1 概述
- React 是用于构建
Web
和原生交互界面
的库
。
1.2 目前 React 在前端处于什么地位?
- 目前,前端最流行的三大框架是:Vue、React 和 Angular。
- Google 对于前端三大框架的搜索指数:
1.3 React 的技术特点
- ① React 是由前 Facebook(现 Meta)公司来更新和维护的,它是
大量优秀程序员
的思想结晶
。 - ② React 的流行不仅仅局限于
普通开发工程师
对它的认可,大量的其它框架
也借鉴
了 React 的思想。- Vue.js 框架在设计之初,也借鉴和学习了 React,如:React 的 Hooks (Vue 3.x 的 Composition API)。
- ③ React 是
前端的先驱者
,它总是会引领
整个前端的潮流
。
第二章:React 入门
2.1 React 的介绍
2.1.1 概述
- React 是用于构建
Web
和原生交互界面
的库
2.1.2 React 的特点
- ① 声明式编程:
- 目前,整个大前端的
最流行开发模式
是声明式编程
,如:Vue、React、Flutter 等。 - 声明式编程允许
我们只维护自己的状态
,当状态改变的时候
,React 会根据最新的状态去重新渲染我们的 UI 界面
。
- 目前,整个大前端的
- ② 组件化开发:
组件化开发
是目前前端的流行趋势
,我们可以将复杂的界面拆分为一个个小的组件。
- ③ 多平台适配:
- 2013 年,React 发布之初主要是
开发 WEB 页面
。 - 2015 年,Facebook 推出了
ReactNative
,用于开发移动端跨平台
。 - 2017 年,Facebook 推出了
ReactVR
,用于开发虚拟现实 WEB 应用程序
。 - ……
- 2013 年,React 发布之初主要是
2.2 React 入门(⭐)
2.2.1 React 的开发依赖
React 开发需要依赖三个库:
- react:包含 React 所必须得核心代码。
- react-dom:React 渲染到不同平台所需要的核心代码。
- babel:将 jsx 转换为 React 代码的工具。
我们可以在 bootcdn 中搜索:
注意⚠️:
- 我们会发现有 umd 和 cjs 的版本。
- 其中,umd 全称是 Universal Module Definition,可以在前端和后端通用。
- 其中,cjs 全称 CommonJS ,不能在浏览器中运行,通常会在 Node.js 中运行。
其中,amd 的全称是 Asynchronous Module Definition,已经被淘汰。- 其中,esm 的全称是 ES Modules,可以在很多现代浏览器中使用;但是,一般配合打包工具,如:webpack、vite 等使用,以便在所有浏览器都能使用。
- 示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body>
<!-- react:React 的核心 -->
<script crossorigin src="https://cdn.bootcdn.net/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script crossorigin src="https://cdn.bootcdn.net/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script crossorigin src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.22.17/babel.js"></script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2.2.2 React 入门
- 需求:在页面中展示
"Hello React"
。
- 示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script crossorigin src="https://cdn.bootcdn.net/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script crossorigin src="https://cdn.bootcdn.net/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script crossorigin src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.22.17/babel.js"></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class="root"></div>
<script type="text/babel">
// 创建一个虚拟 DOM
const vDOM = (
<h1>Hello React</h1>
)
// React 18之后,创建 React 根元素
const root = ReactDOM.createRoot(document.querySelector('.root'))
root.render(vDOM)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2.2.3 注意点
- ①
script
元素的type
属性必须是text/babel
,即告诉浏览器此处让 babel 解析 jsx 的语法。 - ②
ReactDOM.createRoot(xxx)
的作用是创建一个 React 根元素,然后将虚拟 DOM 渲染到这个根元素之中。其中,参数xxx
就是要挂在到哪一个 HTML 元素上。 - ③
root.render(xxx)
是用来渲染组件(虚拟 DOM )的。 - ④ 在 React 18 之前的语法是:
ReactDOM.render(vDOM, document.querySelector('.root'))
;不过,现在该 API 已经过时。 - ⑤ 我们可以使用
{}
在 jsx 中引入外部的变量或表达式。
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script crossorigin src="https://cdn.bootcdn.net/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script crossorigin src="https://cdn.bootcdn.net/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script crossorigin src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.22.17/babel.js"></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class="root"></div>
<script type="text/babel">
// 创建一个虚拟 DOM
let message = "你好,React"
const vDOM = (
<h1>
{/* 可以使用 {} 来外部的变量或表达式 */ }
{message}
</h1>
)
// React 18之后,创建 React 根元素
const root = ReactDOM.createRoot(document.querySelector('.root'))
root.render(vDOM)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
- ⑥ React 18 之后,为什么要
ReactDOM.createRoot(document.querySelector('.root'))
这样创建根元素?是因为,React 18 之后可以创建多个根元素:
const root = ReactDOM.createRoot(document.querySelector('.root'))
root.render(vDOM)
const app = ReactDOM.createRoot(document.querySelector('.app'))
app.render(vDOM)
2
3
4
2.3 组件化开发(⭐)
2.3.1 引入
- 需求:在页面中,通过点击按钮切换展示的文本。
- 示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 定义变量
let message = '你好,React'
// 定义方法
const change = () => {
// 修改数据
message = '你好,React18'
// 重新渲染页面
appRender()
}
// 渲染页面
appRender()
function appRender() {
// 创建一个虚拟 DOM
const vDOM = (
<div>
<h1>{message}</h1>
{ /* 注意:
① 在 React 中,事件的名称是以 onXxx() 的形式,小驼峰的命名规则
② onClick 中传入的是函数的名称,如:change,而不是 change() ;否则,当点击的时候,就会没有效果,因为 change() 返回的是 undefined
*/ }
<button onClick={change}>改变文本</button>
</div>
)
// 将虚拟 DOM 渲染到页面上
app.render(vDOM)
}
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2.3.2 问题分析
上述的代码虽然能解决问题,但是却产生了如下的问题:
- ① 我们需要手动封装渲染页面的函数,在初次渲染的时候就调用该函数;当我们更改完数据之后,还需要手动调用该函数;难道每次修改完数据,都需要手动调用该函数,React 也太不智能了吧(这是所谓的声明式编程?)。
- ② 代码过于分散,如果开发过 Vue3.x 的都知道,Vue 会将代码封装到
.vue
文件中;那么,React 应该也提供类似的功能,而上述代码的实现却没能很好的体现程序设计的内聚性。 - ③ 我们也知道
root.render(xxx)
不仅可以用来渲染 DOM ,也可以用来渲染组件的;其中,xxx
既可以是虚拟 DOM
,也可以是组件
。 - ④ ……
那么,怎么解决?
注意⚠️:React 中支持
类组件
和函数式组件
,我们以类组件
为切入点
讲解,函数式组件
后面再讲解。。
2.3.3 React 中的类组件
定义:
- ① 定义一个类,继承自 React.Component 类,类名必须采用大驼峰的原则,
- ② 自定义的类需要重写 render() 方法,该方法中返回虚拟 DOM(其实,就是 jsx)。
- ③ 在
root.render(xxx)
方法中,传入<类/>
。
示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建一个组件
class App extends React.Component {
// 渲染内容 render 方法
render() {
return (
<h2>Hello React</h2>
)
}
}
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2.3.4 React 中的数据依赖
我们知道,在 Vue 3.x 中的 Options API 中,我们是将数据封装到
data(){}
配置中的;那么,在 React 中写到哪里?如何让一个类组件的实例获取到实例的数据,有很多实现方式;其中,最简单的就是在构造器中定义了。
同时,定义在组件中的数据,我们会分为两类:
- ①
参与界面更新的数据
:当数据发生变化的时候,需要更新组件渲染的内容。 - ②
不参与界面更新的数据
:当数据发生变化的时候,不需要更新组件渲染的内容。
- ①
所以,参与界面更新的数据,就需要定义在构造器中,并且定义在当前对象的
state
属性中;而且,当数据发生变化的时候,我们不是通过修改this.state
属性的值,而是通过this.setState()
方法来更新数据,以便通知 React 来重新渲染页面。示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建一个组件
class App extends React.Component {
// 组件数据
constructor() {
super()
// 参与界面更新的数据,需要写在 this.state 中
this.state = {
isHot: false
}
}
// 组件方法
change() {
// 内部完成了两件事情
// ① 将 state 中的 isHot 修改了
// ② 自动重新执行 render 函数
this.setState({
isHot: !this.state.isHot
})
}
// 渲染内容 render 方法
render() {
const {isHot} = this.state
return (
<div>
<h2>{'今天天气很' + (isHot ? '炎热' : '凉爽')}</h2>
<button onClick={this.change}>改变内容</button>
</div>
)
}
}
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
2.3.5 this 绑定问题的解决
- 当我们运行上述的程序,会发生
this
是undefined
。
- 官方是如何解决的?
- 那么,我们可以修改下程序:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建一个组件
class App extends React.Component {
// 组件数据
constructor() {
super()
this.state = {
isHot: false
}
// 绑定 this
this.change = this.change.bind(this)
}
change() {
this.setState({
isHot: !this.state.isHot
})
}
render() {
const {isHot} = this.state
return (
<div>
<h2>{'今天天气很' + (isHot ? '炎热' : '凉爽')}</h2>
{ /* 注意:此处不能是 this.change() ,因为我们需要传递函数地址给 React,让其帮我们调用 */ }
{ /* 注意:如果写成 this.change() ,就是手动调用方法,然后方法返回 undefined,是错误写法! */ }
<button onClick={this.change}>改变内容</button>
</div>
)
}
}
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
- 再次查看浏览器:
- 为什么要这么写?我们可以通过如下的代码,来模拟一下:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script>
// 创建一个组件
class App {
constructor() {
this.message = "Hello World"
}
change(){
this.message = "Hello React"
console.log(this,this.message)
}
}
const app = new App()
app.change()
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
- 运行如下:
- 如果,我们将代码修改一下:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script>
// 创建一个组件
class App {
constructor() {
this.message = "Hello World"
}
change(){
this.message = "Hello React"
console.log(this,this.message)
}
}
const app = new App()
const abc = app.change
abc()
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- 此时,结果如下所示:
分析:代码之所以报错,是因为 this 的隐式绑定。当运行
const app = new App()
的时候,change()
方法的 this 确实是app
对象;但是,当运行const abc = app.change; abc()
这段代码的时候,change()
方法的 this 已经是window
对象。又因为我们使用的是类,浏览器会开启严格模式,所以change()
方法的 this 已经是undefined
对象,此时undefined.message
当然会报错了。我们可以通过 bind 来显示改变 this 的指向,并且 bind 的定义是这样的:
- 修改下代码:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script>
// 创建一个组件
class App {
constructor() {
this.message = "Hello World"
}
change(){
this.message = "Hello React"
console.log(this,this.message)
}
}
const app = new App()
let abc = app.change
// 通过 bind 来显示改变 this 的指向
abc = abc.bind(app)
abc()
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
- 运行一下,查看结果:
- 此时,我们再来分析一下 React 中的代码:
- 当然,你也可以这么写,原理是一样的:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建一个组件
class App extends React.Component {
// 组件数据
constructor() {
super()
// 参与界面更新的数据,需要写在 this.state 中
this.state = {
isHot: false
}
}
// 组件方法
change() {
// 内部完成了两件事情
// ① 将 state 中的 isHot 修改了
// ② 自动重新执行 render 函数
this.setState({
isHot: !this.state.isHot
})
}
// 渲染内容 render 方法
render() {
const {isHot} = this.state
return (
<div>
<h2>{'今天天气很' + (isHot ? '炎热' : '凉爽')}</h2>
{ /* 注意:此处需要手动绑定 this */ }
<button onClick={this.change.bind(this)}>改变内容</button>
</div>
)
}
}
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
2.4 React 案例(⭐)
2.4.1 数组列表案例
- 需求:需要将数组中的元素渲染到页面上。
注意⚠️:
- 在 React 中,没有 Vue 的差值语法;React 中的 jsx 语法很简单,都是
{}
。- React 中
{}
可以插入数组,会自动遍历数组中的元素并渲染到页面上;但是,不支持对象。
- 示例:实现方式一(通过数组的索引,不推荐)
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建一个组件
class App extends React.Component {
constructor() {
super();
// 组件数据
this.state = {
message: '你好,React',
movies: ['星际穿越', '大话西游', '梦幻西游', '诛仙']
}
}
// 渲染内容 render 方法
render() {
const {message, movies} = this.state
return (
<div>
<h2>{message}</h2>
<ul>
{/* 通过数组的索引来实现,不推荐 */}
<li>{this.state.movies[0]}</li>
<li>{this.state.movies[1]}</li>
<li>{this.state.movies[2]}</li>
<li>{this.state.movies[3]}</li>
</ul>
</div>
)
}
}
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
- 示例:实现方式二(通过手动拼接数组)
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建一个组件
class App extends React.Component {
constructor() {
super();
// 组件数据
this.state = {
message: '你好,React',
movies: ['星际穿越', '大话西游', '梦幻西游', '诛仙']
}
}
// 渲染内容 render 方法
render() {
const {message, movies} = this.state
// 通过手动拼接数组
let liEls = [];
movies.forEach((item, index) => {
liEls.push(<li key={index}>{item}</li>)
})
return (
<div>
<h2>{message}</h2>
<ul>
{/* jsx 中的 {} 支持放入数组,jsx 会自动遍历数组,并将数组中的元素渲染到页面上;但是,不支持对象。 */}
{liEls}
</ul>
</div>
)
}
}
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
- 示例:实现方式三(通过数组的 map 方法,用于对数组中的每个元素进行操作,并返回一个新的数组)
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建一个组件
class App extends React.Component {
constructor() {
super();
// 组件数据
this.state = {
message: '你好,React',
movies: ['星际穿越', '大话西游', '梦幻西游', '诛仙']
}
}
// 渲染内容 render 方法
render() {
const {message, movies} = this.state
return (
<div>
<h2>{message}</h2>
<ul>
{/* jsx 中的 {} 支持放入数组,jsx 会自动遍历数组,并将数组中的元素渲染到页面上;但是,不支持对象。*/}
{/* 可以通过数组的 map 方法实现。*/}
{
movies.map((item,index) => <li key={index}>{item}</li>)
}
</ul>
</div>
)
}
}
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
2.4.2 计数器案例
- 需求:对计数进行
+1
或-1
操作。
- 示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建一个组件
class App extends React.Component {
constructor() {
super();
// 组件数据
this.state = {
count: 0
}
}
increment() {
const {count} = this.state
this.setState({
count: count + 1
})
}
decrement() {
const {count} = this.state
this.setState({
count: count - 1
})
}
// 渲染内容 render 方法
render() {
const {count} = this.state
return (
<div>
<h2>当前计数为:{count}</h2>
<button onClick={this.increment.bind(this)}>点我+1</button>
<button onClick={this.decrement.bind(this)}>点我-1</button>
</div>
)
}
}
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
第三章:JSX 语法
3.1 初识 JSX 语法(⭐)
- 像下面的一段代码:
const element = <h1>Hello, world!</h1>
- 这个有趣的标签语法既
不是字符串
也不是 HTML
;如果,我们将script
元素的type='text/babel'
属性去掉,就会报错,因为浏览器压根不认识。 - 其实,这就是
JSX
(J
avaS
cript eX
tension),是一种JavaScript
的语法扩展;也有人称其是JavaScript XML
,因为看起来就像XML
语法。 - 在 React 开发中,建议配合使用 JSX ,因为其可以很好地描述 UI 应该呈现出它应有交互的本质形式。
- JSX 不同于 Vue 中的模板语法;因此,我们不再需要专门学习模板语法中的各种指令,如:
v-if
、v-else
、v-for
等。 JSX 拥有 JavaScript 的一切功能!!!
3.2 为什么 React 选择了 JSX ?(⭐)
- React 认为
渲染逻辑
本质上与其他 UI 逻辑
存在内在耦合,如:- 在 UI 中需要绑定处理事件。
- 在某些时刻状态发生变化时需要通知到 UI。
- 需要在 UI 中展示准备好的数据。
- React 并没有采用将
标记与逻辑分离到不同文件
这种人为的分离方式,而是通过将二者共同存放在称之为组件
的松散耦合单元之中,来实现关注点分离
。
维基百科的定义:
- 在计算机科学中,关注点分离(Separation of concerns,SoC),是将计算机程序分隔为不同部分的设计原则。每一部分会有各自的关注焦点。关注焦点是影响计算机程式程式码的一组资讯。关注焦点可以像是将程式码优化过的硬件细节一般,或者像实例化类别的名称一样具体。展现关注点分离设计的程序被称为模组化程序[1]。模组化程度,也就是区分关注焦点,通过将资讯封装在具有明确界面的程序代码段落中。封装是一种资讯隐藏手段[2]。资讯系统中的分层设计是关注点分离的另一个实施例(例如,表示层,业务逻辑层,数据访问层,持久数据层)[3]。
- 关注点分离,是对只与“特定概念、目标”(关注点)相关联的软件组成部分进行“标识、封装和操纵”的能力,即标识、封装和操纵关注点的能力。是处理复杂性的一个原则。由于关注点混杂在一起会导致复杂性大大增加,所以能够把不同的关注点分离开来,分别处理就是处理复杂性的一个原则,一种方法。分离关注点使得解决特定领域问题的程式码从业务逻辑中独立出来,业务逻辑的程式码中不再含有针对特定领域问题程式码的调用(将针对特定领域问题程式码抽象化成较少的程式码,例如将程式码封装成function或是class),业务逻辑同特定领域问题的关系通过侧面来封装、维护,这样原本分散在整个应用程序中的变动就可以很好的管理起来。
- 关注点分离的价值在于简化计算机程序的开发和维护。当关注点分开时,各部分可以重复使用,以及独立开发和更新。具有特殊价值的是能够稍后改进或修改一段代码,而无需知道其他部分的细节必须对这些部分进行相应的更改。
- React
不强制要求
使用 JSX,但是大多数人发现,在 JavaScript 代码中将 JSX 和 UI 放在一起时,会在视觉上有辅助作用。 JSX
还可以使 React显示
更多有用
的错误和警告消息。
3.3 JSX 的基本使用(⭐)
3.3.1 JSX 的基本语法
- JSX 的基本语法:
- ① JSX 中
只能
有一个
根元素,除非使用Fragment
标签包裹。 - ② 为了方便阅读,我们通常会在 JSX 的外层包裹一个
()
。 - ③ 在 JSX 中,
标签
可以是单标签
,也可以是双标签
;但是,如果是单标签,必须以<标签 />
结尾(这点,很像 XML 语法)。
- ① JSX 中
注意⚠️:JSX 是在
类式组件
中render()
方法的return
后面书写,函数式组件
且听后文讲解。
- 示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
message : '你好,React'
}
// 渲染内容 render 方法
/* ① jsx 中顶层只能有一个根元素,除非使用 Fragment 标签包裹 */
/* ② 为了方便阅读,我们通常会在 jsx 的外层包裹一个 () */
/* ③ jsx 中的标签可以单标签,也可以是双标签;但是,如果是单标签,必须以 <标签 /> 结尾 */
render() {
return (
<div>
{this.state.message}
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
3.3.2 JSX 语法中的注释
JSX 语法中的注释只有一种,就是
{ /* 我是注释内容 */ }
。示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
message : '你好,React'
}
// 渲染内容 render 方法
render() {
return (
<div>
{ /* 我是注释内容 */ }
{this.state.message}
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
3.3.3 JSX 中嵌入变量作为子元素
3.3.3.1 概述
在 Vue 中,如果是
插入文本
,需要使用插值语法
;如果需要插入大段子元素
(如:ul
中插入大量的li
),需要使用v-for
等指令。在 React 中,可以使用
{}
中嵌入变量
的方式来作为子元素
;换言之,结合了 Vue 中的上述两种写法。示例:插入变量作为文本
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
message: '你好,React',
}
// 渲染内容 render 方法
render() {
const { message } = this.state
return (
<div>
{/* jsx 中嵌入变量作为子元素 */}
{/* 插入变量作为文本 */}
<h2>{message}</h2>
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
- 示例:插入变量作为子元素
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
movies: ['星际穿越', '大话西游', '梦幻西游', '诛仙'],
}
// 渲染内容 render 方法
render() {
const { movies } = this.state
return (
<div>
{/* jsx 中嵌入变量作为子元素 */}
<ul>
{
movies.map((item,index) => <li key={index}>{item}</li>)
}
</ul>
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
3.3.3.2 JSX 中嵌入变量的各种情况
- 情况①:当
变量
是Number
、String
、Array
类型的时候,可以直接显示
;当然,Array 类型会帮助我们遍历数组中的元素并显示到界面上。 - 情况②:当
变量
是null
、undefined
、Boolean
的时候,显示的内容为空
。
注意⚠️:如果依然希望可以显示
null
、undefined
、Boolean
,需要转换为String
(实际开发中,一般不需要)。
情况③:当
变量
是Object 对象类型
的时候,会直接报错
(not valid as a React child)。示例:情况①
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
message: '你好,React', // 字符串
movies: ['星际穿越', '大话西游', '梦幻西游', '诛仙'], // 数组
name: '张三', // 字符串
age: 18 // 数值
}
// 渲染内容 render 方法
render() {
const { name,age, message,movies } = this.state
return (
<div>
{/* jsx 中嵌入变量作为子元素 */}
<h2>{message}</h2>
<ul>
{
movies.map((item,index) => <li key={index}>{item}</li>)
}
</ul>
<h2>我的名字是:{name},我的年龄是:{age}</h2>
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
- 示例:情况②
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
aaa: undefined,
bbb: null,
ccc: true,
}
// 渲染内容 render 方法
render() {
const { aaa,bbb, ccc } = this.state
return (
<div>
{/* jsx 中嵌入变量作为子元素 */}
<div>{aaa}</div>
<div>{bbb}</div>
<div>{ccc}</div>
{/*之所以会显示,是因为转换为 String 类型;实际开发中,没有必要*/}
<div>{ccc.toString()}</div>
</div>
)
}
}
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
- 示例:情况③
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
obj: { name: '张三', age: 18 }, // 对象类型
}
// 渲染内容 render 方法
render() {
const { obj } = this.state
return (
<div>
{/* jsx 中嵌入变量作为子元素 */}
{/* 如果是对象类型,会直接报错;那么,将对象中的每个属性获取就可以了!!! */}
<div>{obj.name} - {obj.age}</div>
</div>
)
}
}
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
3.3.3.3 JSX 中嵌入表达式
Vue 中的插值语法中可以嵌入表达式;React 中的 JSX 中也可以。
React 中的 JSX 中嵌入的表达式常见的有:
运算表达式
、三元表达式
、执行一个函数
。示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
state = {
isHot: false
}
show(){
return this.state.isHot
}
// 渲染内容 render 方法
render() {
const {isHot} = this.state
return (
<div>
{/* jsx 中嵌入变量作为子元素 */}
{/* 插入表达式: 运算表达式 */}
<div>{10 + 20}</div>
<div>{"张" + "三"}</div>
{/* 插入表达式: 三元运算符 */}
<div>今天天气很{isHot ? '炎热' : '凉爽'}</div>
{/* 插入表达式: 执行一个函数 */}
<div>{this.show().toString()}</div>
</div>
)
}
}
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
3.4 JSX 绑定属性(⭐)
3.4.1 JSX 绑定基本属性
- 在 Vue 中,对于元素身上的基本属性是通过
v-bind
指令或:
语法糖来实现的:
<template>
<h2 :title="title">我是h2元素</h2>
</template>
<script setup>
import {ref} from 'vue'
const title = ref('哈哈')
</script>
2
3
4
5
6
7
8
<template>
<img :src="imgUrl" alt='' width='200px' />
</template>
<script setup>
import {ref} from 'vue'
const imgUrl = ref('https://mei.png')
</script>
2
3
4
5
6
7
8
<template>
<a :href="href" target='_blank'>百度一下,你就知道</a>
</template>
<script setup>
import {ref} from 'vue'
const href = ref('https://www.baidu.com')
</script>
2
3
4
5
6
7
8
在 React 中,依然是使用
{}
来绑定基本属性。示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
title: '哈哈哈',
imgUrl: 'https://t7.baidu.com/it/u=4071610061,2917123939&fm=193&f=GIF',
href: 'https://www.baidu.com/',
}
// 渲染内容 render 方法
render() {
const { title, imgUrl, href } = this.state
return (
<div>
{ /* 基本属性绑定 */}
<h2 title={title}>我是h2元素</h2>
<img src={imgUrl} alt='' width='200px' /> <br />
<a href={href} target='_blank'>百度一下,你就知道</a>
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
3.4.2 JSX 绑定 style 属性
- 在 Vue 中,对于 style 属性,是通过绑定 JavaScript 的
对象值
来实现的:
<template>
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
</template>
<script setup>
import {ref} from 'vue'
const activeColor = ref('red')
const fontSize = ref(30)
</script>
2
3
4
5
6
7
8
9
- 在 React 中,依然是使用
{}
来绑定基本属性;但是,{}
中是通过 JavaScript 的对象值
来实现的。
注意⚠️:
- 在原生 CSS 中,一些属性是通过
-
连接的,如:background-color 等;- 在 Vue 和 React 中,都需要使用小驼峰的命名规则,如:backgroundColor 。
- 很好理解,因为这是 JavaScript 中规定的,在 JavaScript 中变量的命名是不能使用
-
连接的;即使在 对象中,如果属性名需要使用-
连接,也需要加上''
。
- 示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
objStyle: {
color: 'blue',
fontSize: '18px'
}
}
// 渲染内容 render 方法
render() {
const { objStyle } = this.state
return (
<div>
{ /* 绑定 style 属性 */}
<h2 style={{ color: 'red', fontSize: 14 + 'px' }}>呵呵呵</h2>
<h2 style={objStyle}>呵呵呵</h2>
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
3.4.3 JSX 绑定 class 类
- 在 Vue 中,对于 class 类,是通过传递一个
对象
来动态切换class
:
<template>
<div class="static" :class="{ active: isActive, 'text-danger': hasError }"></div>
</template>
<script setup>
import {ref} from 'vue'
const isActive = ref(true)
const hasError = ref(false)
</script>
2
3
4
5
6
7
8
9
- 在 Vue 中,对于 class 类,还可以传递一个
数组
来渲染多个class
:
<template>
<div :class="[activeClass, errorClass]"></div>
</template>
<script setup>
import {ref} from 'vue'
const activeClass = ref('active')
const errorClass = ref('text-danger')
</script>
2
3
4
5
6
7
8
9
- 在 React 中,和 Vue 有点不同,需要使用
className
来完成 class 的绑定。
注意⚠️:在 React 中,如果想要实现类似 Vue 中对象动态切换 class 的那种效果,需要使用第三方库 classnames 。
- 示例:class 属性的直接绑定
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 渲染内容 render 方法
render() {
return (
<div>
{ /* class属性的绑定 */}
<h2 className='abc'>哈哈哈</h2>
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
- 示例:通过字符串的拼接
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<style>
.abc {
background-color: pink;
}
.active {
font-size: 18px;
font-weight: bold;
}
</style>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
isActive: true,
}
// 渲染内容 render 方法
render() {
const { isActive } = this.state
// class 绑定写法一:字符串的拼接
const activeClassName = `abc cba ${isActive ? 'active' : ''}`
return (
<div>
{ /* class属性的绑定 */}
<h2 className={`abc cba ${isActive ? 'active' : ''}`}>哈哈哈</h2>
<h2 className={activeClassName}>哈哈哈</h2>
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
- 示例:通过数组的拼接
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<style>
.abc {
background-color: pink;
}
.active {
font-size: 18px;
font-weight: bold;
}
</style>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
isActive: true,
}
// 渲染内容 render 方法
render() {
const {isActive } = this.state
// class 绑定写法二:数组的拼接
const classList = ['abc', 'cba']
if (isActive) {
classList.push('active')
}
return (
<div>
{ /* class属性的绑定 */}
<h2 className={classList.join(' ')}>哈哈哈</h2>
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
3.5 JSX 的事件绑定(⭐)
3.5.1 概述
原生 JavaScript 中的事件监听有三种方式:
- ① 直接在 HTML 中编写 JavaScript 代码(很少使用)。
- ② 通过元素的 on 属性来监听事件。
- ③ 通过 EventTarget 的 addEventListener 来监听事件(推荐)。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Title</title>
</head>
<body>
<!-- 直接在 HTML 中编写 JavaScript 代码(了解) -->
<button onclick="console.log(`按钮1发生了点击`)">按钮1</button>
<button class="btn2">按钮2</button>
<button class="btn3">按钮3</button>
<script>
// 获取元素
var btn2El = document.querySelector(".btn2")
var btn3El = document.querySelector(".btn3")
// onclick 属性
btn2El.onclick = function () {
console.log(`按钮2发生了点击`)
}
// addEventListener(推荐)
btn3El.addEventListener('click', function () {
console.log(`按钮3发生了点击`)
})
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
在 React 中的事件监听和原生 JavaScript 的事件监听有稍许不同:
- React 中的
事件命名
采用的是小驼峰
的命名规则,如:onClick
、OnDbClick
等。 - 我们可以通过
{}
插入一个事件处理函数,该事件处理函数会在事件发生的时候被执行。
- React 中的
示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建一个组件
class App extends React.Component {
constructor() {
super();
this.state = {
message: '你好,React'
}
}
// 组件方法
change = () => {
this.setState({
message: '你好,React18'
})
}
// 渲染内容 render 方法
render() {
const {message} = this.state
return (
<div>
<h2>{message}</h2>
{/* React 中的`事件命名`采用的是`小驼峰`的命名规则,如:`onClick`、`OnDbClick` 等。 */}
{/* 我们可以通过 `{}` 插入一个事件处理函数,该事件处理函数会在事件发生的时候被执行。 */}
<button onClick={this.change.bind(this)}>改变内容</button>
</div>
)
}
}
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
3.5.2 this 绑定的最佳实践
3.5.2.1 this 绑定方式一
我们可以通过在
构造器
或调用方法处
通过手动通过 bind 绑定 this
来解决在类中的方法中,this 丢失的问题。示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
constructor() {
super();
this.state = {
count: 0
}
// 在构造器中手动对函数中的 this 进行绑定
this.btn1Click = this.btn1Click.bind(this)
}
btnClick() {
this.setState({
count: this.state.count + 1
})
}
render() {
const {count} = this.state
return (
<div>
<h2>当前计数为:{count}</h2>
<button onClick={this.btnClick}>点我</button>
</div>
)
}
}
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
- 示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
constructor() {
super();
this.state = {
count: 0
}
}
btnClick() {
this.setState({
count: this.state.count + 1
})
}
render() {
const {count} = this.state
return (
<div>
<h2>当前计数为:{count}</h2>
{/* 之所以也可以成功,是因为 render 方法中是可以访问 this 的,而我们就可以手动的通过 bind 来对 btnClick 方法进行 this 绑定了*/}
<button onClick={this.btnClick.bind(this)}>点我</button>
</div>
)
}
}
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
3.5.2.2 this 绑定方式二
- 在 ES6+ 中,类中可以写
class fields
,即类属性
。
- 可能不是很明白,我们可以看下面的代码:
'use strict';
class Animal {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayName = function(){console.log(this.name);};
sayAge = () =>{ console.log(this.age);}
}
let tom = new Animal('tom',19);
let [sayName, sayAge] = [tom.sayName, tom.sayAge];
sayAge(); // 正常输出 19
sayName(); // this 为 undefined,会报错
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 箭头函数不会创建自己的 this,而是会继承外层作用域的 this 。当我们将 tom.sayName 和 tom.sayAge 赋值给 sayName 和sayAge 变量时,这 sayAge 仍然保留了对 tom 实例的引用。
- 当然,更加具体的解释在这里:
所以,我们可以在 React 中,通过 ES6 的 class fields 语法来解决 this 绑定问题。
示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
state = {
count: 0
}
btnClick2 = () => {
this.setState({
count: this.state.count + 1
})
}
render() {
const {count} = this.state
return (
<div>
<h2>当前计数为:{count}</h2>
{/* ES6 的 class fields */}
<button onClick={this.btnClick2}>点我</button>
</div>
)
}
}
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
3.5.2.3 this 绑定方式三
- 我们也可以通过在
事件监听
中传入箭头函数
的方式来解决 this 绑定问题(强烈推荐
)。
注意⚠️:
() => {}
箭头函数也是表达式,所以{}
中就可以插入箭头函数
。
- 示例:在事件监听中传入箭头函数
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
state = {
count: 0
}
render() {
const {count} = this.state
return (
<div>
<h2>当前计数为:{count}</h2>
{/* 直接传入一个箭头函数 */}
{/* 我们会发现当点击按钮的时候,就触发了 alert("abc") */}
{/*
原理类似:
const click = () => alert("abc")
click()
*/}
<button onClick={() => alert("abc")}>点我</button>
</div>
)
}
}
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
- 示例:在事件监听中传入箭头函数,并通过 this 调用类中的方法
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
state = {
count: 0
}
btnClick3() {
this.setState({
count: this.state.count + 1
})
}
render() {
const {count} = this.state
return (
<div>
<h2>当前计数为:{count}</h2>
{/* 直接传入一个箭头函数 */}
{/*
原理类似
const click = () => this.btnClick3()
click()
当按钮被点击的时候,会触发 click() ,进而会执行 this.btnClick3()
又因为 () => this.btnClick3() 中的箭头函数中没有 this 的,那么 this 就是当前实例对象(因为 render 函数中是有 this 的)。
而当前实例对象身上是有 btnClick3() 方法的,而 btnClick3 是这样定义的 btnClick3() {},
所以,this 没有丢失。
*/}
<button onClick={() => this.btnClick3()}>点我</button>
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
3.5.3 事件参数传递
3.5.3.1 event 参数的传递
- 对于原生 JavaScript 而言,我们是在回调函数中,通过参数
event
来得到事件对象
的,如:
boxEl.addEventListener('click',function(event){
// event 就是事件对象
})
2
3
- 在 React 中,我们也可以通过在
类方法
中通过参数event
来得到事件对象
。
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
message: '你好,React'
}
btn1Click = (event) => {
console.log('btn1Click', event, this)
}
render() {
return (
<div>
{/* event 参数的传递 */}
<button onClick={this.btn1Click}>按钮1</button>
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
- 但是,对于在
事件监听
中传入箭头函数
的方式而言,需要手动传递event
事件对象参数:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
message: '你好,React'
}
btn2Click(event) {
console.log('btn2Click', event, this)
}
render() {
return (
<div>
{/* event 参数的传递 */}
<button onClick={(event) => this.btn2Click(event)}>按钮2</button>
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
3.5.3.2 额外参数的传递
- 有的时候,我们除了要在类方法中获取 event 事件对象参数之外,还需要传递额外的参数;此时,就推荐在
事件监听
中传入箭头函数
的方式了。
注意⚠️:这种方式传递参数的
个数
和位置
是很直观的的,而其他两种方式不是很直观(因为 event 参数最后一个,增加了记忆成本)。
- 示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
message: '你好,React'
}
btn2Click(event, name, age) {
console.log('btn2Click', event, this, name, age)
}
render() {
return (
<div>
{/* 额外参数的传递 */}
{/* 这种方式传递参数的`个数`和`位置`是很直观的的,指的是 this.btn2Click(event, '张三', 18) */}
<button onClick={(event) => this.btn2Click(event, '张三', 18)}>按钮3</button>
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
3.5.4 案例
- 需求:电影选中,颜色高亮。
实现思路:
- ① 在
state
中保存当前的选中数组的索引值,初始为0
,即第一个数组元素。- ② 在
li
元素中通过 onClick 事件,获取当前选中的索引值,并将当前的索引值修改到state
对应的变量中。- ③ 在
li
元素的 className 属性中根据当前的索引值 = 选中的索引值
,来动态
添加或删除active
类。
- 示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<style>
.active {
color: red;
}
</style>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
movies: ['星际穿越', '大话西游', '梦幻西游', '诛仙'], // 电影列表数据
currentIndex: 0 // 当前的索引
}
// 改变当前选中的索引
changeCurrentIndex(index) {
this.setState({
currentIndex: index
})
}
render() {
const {movies, currentIndex} = this.state
return (
<div>
<h2>电影列表</h2>
<ul>
{
movies.map((movie, index) => {
return (
<li key={index} className={currentIndex === index ? 'active' : ''}
onClick={() => this.changeCurrentIndex(index)}>
{movie}
</li>)
})
}
</ul>
</div>
)
}
}
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
3.6 JSX 的条件渲染(⭐)
3.6.1 概述
- 在 Vue 中,我们会使用
v-if
、v-else
或v-show
等指令进行条件渲染;但是,React 倡导的是all in js
;那么,在原生 JavaScript 怎么玩,在 React 中也怎么玩。
条件渲染:我们可以创建不同的组件来封装各种我们需要的行为。然后,依据应用的不同状态,只渲染对应状态下的部分内容。
3.6.2 if 条件判断
if 条件判断语句,适合逻辑较多的情况;原理很简单,就是根据
判断
当前的状态
,然后在render
方法中返回(return)
不同的jsx (虚拟 DOM)
或组件
。示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
message: '你好,React',
isReady: true
}
render() {
const { isReady } = this.state
if (isReady) {
return (
<div>
<h2>已经准备好了</h2>
</div>
)
} else {
return (
<h2>稍等,还没准备好</h2>
)
}
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
3.6.3 元素变量
- 我们可以通过变量来存储元素,这样可以帮助我们有条件的渲染组件的一部分,而其他的渲染部分并不会因此而改变。
注意⚠️:元素变量是 if 条件判断的一种变种而已。
- 示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
message: '你好,React',
isReady: true
}
render() {
const { isReady } = this.state
let showElement
if (isReady) {
showElement = (
<div>
<h2>已经准备好了</h2>
</div>
)
} else {
showElement = (
<h2>稍等,还没准备好</h2>
)
}
return (
<div>
{ /* 条件判断语句:根据条件给变量赋值不同的内容,适合逻辑较多的情况~ */}
{showElement}
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
3.6.4 三元运算符
三元运算符逻辑比较简单的情况;虽然也可以用于较为复杂的表达式,但是看起来不够直观。
示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
message: '你好,React',
isReady: true
}
render() {
const { isReady } = this.state
return (
<div>
{/* 三元运算符:适合逻辑比较简单的情况~ */}
{
isReady ? (<h2>已经准备好了</h2>) : (<h2>稍等,还没准备好</h2>)
}
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
3.6.5 短路 &&
我们知道,在原生 JavaScript 中,
true && expression
总是返回expression
;而false && expression
总是返回false
。同理,如果
条件
是true
,在 React 中,&& 右侧的元素
就会
被渲染;如果条件
是false
,React 就不会
渲染。示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
message: '你好,React',
friend: {
name: '张三',
age: 23
}
}
render() {
const { friend } = this.state
return (
<div>
{/* && 逻辑与运算,当某一个值有可能为 null 或 undefined */}
{
friend && (<div>姓名:{friend.name} ,年龄:{friend.age}</div>)
}
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
3.6.6 可选链 ?.
可选链运算符(
?.
)允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。?.
运算符的功能类似于.
链式运算符,不同之处在于,在引用为空 (nullish ) (null
或者undefined
) 的情况下不会引起错误,该表达式短路返回值是undefined
。与函数调用一起使用时,如果给定的函数不存在,则返回undefined
。当尝试访问可能不存在的对象属性时,可选链运算符将会使表达式更短、更简明。在探索一个对象的内容时,如果不能确定哪些属性必定存在,可选链运算符也是很有帮助的。
在 React 中,JSX 中的
变量
如果是undefined
,React 就不会
渲染。示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
message: '你好,React',
friend: null
}
render() {
const { friend } = this.state
return (
<div>
{/* 可选链 ?. ,当某一个值有可能为 null 或 undefined */}
{
(<div>姓名:{friend?.name} ,年龄:{friend?.age}</div>)
}
</div>
)
}
}
// 渲染组件
app.render(<App />)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
3.6.7 案例
- 需求:通过点击按钮,模拟出 Vue 中的
v-if
效果。
- 示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
message: '你好,React',
isShow: true
}
changeShow() {
const {isShow} = this.state
this.setState({
isShow: !isShow
})
}
render() {
const {message, isShow} = this.state
return (
<div>
<button onClick={() => this.changeShow()}>切换</button>
{/* 逻辑&& */}
{isShow && (<h2>{message}</h2>)}
</div>
)
}
}
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
3.7 JSX 本质(⭐)
- 其实,JSX 仅仅只是
createElement(type, config, children)
函数的语法糖;换言之,所有的 JSX 都会被转换为createElement(type, config, children)
函数调用。
注意⚠️:在 React 18 之前,是使用
React.createElement(component, props, ...children)
;但是,目前该 API 已经过时。
- 其中,
type
参数表示的是当前 ReactElement 的类型
:- 如果是
标签
元素,则使用字符串来表示,如:"div"
。 - 如果是
组件
元素,则直接使用组件的名称,如:Demo
。
- 如果是
- 其中,
config
参数是用来存储 JSX 中的属性
:- 都是以对象的形式存储的。
- 其中,
children
参数是用来表示标签的内容
,以 children数组
的方式来存储的。
3.8 虚拟 DOM 的创建过程
- 我们知道,可以通过
createElement(type, config, children)
的方式手动创建 React 元素,它可以作为 JSX 的替代方案。
- 其实,React 就是通过
createElement
创建出来的对象,组成了一个JavaScript 对象树
,并且这个JavaScript 对象树
就是虚拟 DOM
(Virtual DOM)。 - 以下面的代码为例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
// 组件数据
state = {
message: '你好,React',
isShow: true
}
changeShow() {
const {isShow} = this.state
this.setState({
isShow: !isShow
})
}
render() {
const {message} = this.state
const result = (
<div>
<button onClick={() => this.changeShow()}>切换</button>
<h2>{message}</h2>)
</div>
)
console.log('@@@',result)
return result
}
}
// 渲染组件
app.render(<App/>)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
- 我们来看下,这个虚拟 DOM 到底怎么形成的:
- 如何证明?我们可以将虚拟 DOM 打印出来:
- 其实,查看 createElement 函数的源码, 我们也知道:createElement 函数返回的就是一个 JavaScript 对象。
var ReactElement = function (type, key, ref, self, source, owner, props) {
var element = {
$$typeof: REACT_ELEMENT_TYPE,
type: type,
key: key,
ref: ref,
props: props,
_owner: owner
};
// 其余略
return element;
};
function createElement(type, config, children) {
// 其余略
return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 所以,最终的开发模式就是这样:我们只需要写 JSX ,其余的就交给 React 了。
3.9 声明式编程(⭐)
虚拟 DOM 的出现
将开发者的编程模式
从命令式编程
转变到声明式编程
。虚拟 DOM
是一种编程概念
。在这个概念里, UI 以一种理想化的,或者说“虚拟的”
表现形式被保存于内存中,并通过如 ReactDOM 等类库使之与“真实的”
DOM 同步。这一过程叫做协调
。- 这种编程的方式
赋予
了 React 声明式的 API :- 我们告诉 React 希望让 UI 是什么状态,React 就确保 DOM 匹配该状态。
- 这使我们可以从
属性操作
、事件处理
和手动 DOM 更新
这些在构建应用程序时必要的操作中解放出来。
3.10 购物车案例(⭐)
- 需求:展示购物车。
要求:
- ① 在界面上以
表格
的形式展示书籍的数据。 - ② 在底部显示书籍的
总价格
。 - ③ 点击
+
或-
就增加书籍的数量或减少书籍的数量;但是,如果书籍的数量为1
,就不能继续减少。 - ④ 点击
移除
按钮,就将当前书籍移除。 - ⑤ 如果所有书籍
都
移除了,界面显示暂无数据
。
- ① 在界面上以
示例:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
<style>
table {
border-collapse: collapse;
text-align: center;
}
table th, td {
border: 1px solid black;
padding: 8px 16px;
}
table thead {
background-color: #f5f5f5;
}
.active {
background-color: pink;
}
</style>
<!-- react:React 的核心 -->
<script src='./js/react18.development.js'></script>
<!-- React-DOM:React 渲染在不同平台上所需要的核心代码 -->
<script src='./js/react-dom18.development.js'></script>
<!-- babel:将 jsx 转换为 React 代码的工具 -->
<script src='./js/babel.js'></script>
</head>
<body>
<!-- 准备一个容器 -->
<div class='app'></div>
<script type='text/babel'>
// 创建根元素
const app = ReactDOM.createRoot(document.querySelector('.app'))
// 创建一个组件
class App extends React.Component {
state = {
books: [
{
id: 1,
name: '《算法导论》',
date: '2006-9',
price: 85.00,
count: 1
},
{
id: 2,
name: '《UNIX编程艺术》',
date: '2006-2',
price: 59.00,
count: 1
},
{
id: 3,
name: '《编程珠玑》',
date: '2008-10',
price: 39.00,
count: 1
},
{
id: 4,
name: '《代码大全》',
date: '2006-3',
price: 128.00,
count: 1
},
{
id: 5,
name: '《你不知道的JavaScript》',
date: '2014-8',
price: 88.00,
count: 1
},
]
}
// 改变购物车中书籍的数量,如果不传递 num ,就是新增;否则,传递 -1 就是减少
changeCount(id, num = 1) {
const {books} = this.state
if (id) {
const newBooks = books.map(book => {
if (book.id === id) {
book.count += num
}
return book
})
this.setState({
books: newBooks
})
}
}
// 删除购物车中的书籍
delete(id) {
const {books} = this.state
if (id) {
const newBooks = books.filter(book => book.id !== id)
this.setState({
books: newBooks
})
}
}
// 当购物车中的书籍数据不为空的时候的渲染函数
renderBookList() {
const {books} = this.state
const totalPrice = books.map(item => item.price * item.count).reduce((prev, next) => prev + next, 0)
return (<div>
<table>
<thead>
<tr>
<th>序号</th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{
books && books.map((book, index) => {
return (
<tr key={index}>
<td>{index}</td>
<td>{book.name}</td>
<td>{book.date}</td>
<td>{"¥" + book.price.toFixed(2)}</td>
<td>
<button disabled={book.count <= 1} onClick={() => this.changeCount(book.id, -1)}>-</button>
{book.count}
<button onClick={() => this.changeCount(book.id)}>+</button>
</td>
<td>
<button onClick={() => this.delete(book.id)}>移除</button>
</td>
</tr>
)
})
}
</tbody>
<tfoot>
<tr>
<td colSpan="6">总价格:{"¥" + totalPrice.toFixed(2)}</td>
</tr>
</tfoot>
</table>
</div>)
}
// 当购物车中的书籍数据为空的时候的渲染函数
renderBookEmpty() {
return (<div>暂无数据</div>)
}
render() {
const {books} = this.state
return books.length ? this.renderBookList() : this.renderBookEmpty()
}
}
// 渲染组件
app.render(
<App/>
)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179