Intro

从这里开始,课程就正式进入了React的知识讲解

什么是React

首先需要简单的介绍一下React

React也被叫做ReactJS,是一个用于构建ui的JS库,是有2010年Facebook原先用于内部的开发工具,于2013年开源

为什么我们应该使用React

React抽象了和DOM的交互,同时允许我们使用组件以一种更加声明式的方式去构建用户界面

在以前,去构建一个组件,我们需要用到诸如getElement , createElement等等的代码

这种构建方式更加注重如何构建,而不是构建什么

而当我们注重于构建什么,例如使用innerHTML的时候

往往会遇到各种安全风险,所以这里就可以引入React一个很重要的特性

虚拟DOM

The Virtual DOM

我们将与DOM的直接交互抽象化,通过向React描述我们想要展示什么,让React自动对DOM进行更新

比方说下面这个例子

1
2
3
4
5
function Person(props) {
return <div>
<p> Hi! {props.name} </p>
</div>
}

每当age产生变化时,React会进行反应并用新的值重新渲染这一模块

协调 (Reconciliation)

协调是指对比差异并同步虚拟 DOM 与真实 DOM,从而为用户渲染出界面更新的过程。

在 React 中,当组件的 state 或 props 发生变化时,React 需要决定是否更新 DOM。这个决定和执行更新的整个过程就叫“Reconciliation”。官方文档中文版通常翻译为“协调”。

同时,React 用来比较两棵虚拟 DOM 树差异的算法。找到差异后,只会把有变化的部分更新到真实 DOM 上,从而提高性能。

React DOM

React本身是跨平台的,他只是一个高效的树结构协调器

React本身完全不知道自己运行在什么环境中。它只负责管理状态、组件生命周期,以及计算出那棵“虚拟树”哪里发生了变化。

react-dom 负责进行文档的转录

React 101

React Component Basics

在React中,所有东西都是一个组件

对于东西的定义

  • 很相似的问题是在Java中,什么定义了一个类
  • 他是界面中某个可以重用的部分
  • 可以有很多子节点,但只能有一个父节点

以Spotify来举例

整个页面可以被分解为

  • Spotify
    • HeaderBar
    • MainContent
      • BrowseAll
        • Card[]
    • BottomBar

函数组件

一个函数组件应该看起来像这样

1
2
3
4
5
6
function Welcome() {
return <div>
<h1>Hello World!</h1>
<p>This is my website.</p>
</div>
}

它返回一个单一的JSX 元素。如果你有多个元素,你可以返回一个 div <div></div> 或者一个片段 <></>

JSX

JSX 是一种结合了 HTML、CSS 和 JSX 的语法

1
2
3
4
5
6
function Welcome(props) {
return <div>
<h1>Hello World!</h1>
<p>{props.msg}</p>
</div>
}

{}可以用来插入JS表达式,并且将其嵌入到DOM中

JSX在运行时会被转译为HTML,CSS和JS

在React中,我们使用JS的对象来表示CSS,我们可将其在JSX中插入

1
2
3
4
5
6
function Welcome(props) {
return <div style={{border: "dotted", padding: "1rem"}}>
<h1>Hello World!</h1>
<p>{props.msg}</p>
</div>
}

附注: 使用 className 而不是 class 来指定 CSS 类。

组件复用

一个硬编码的组件可以被写成

1
2
3
4
5
function Person() {
return <div>
<p>Sally Amaki</p>
</div>
}

虽然这样写没有问题,但是为了组件能够复用,写成这样子更好

1
2
3
4
5
function Person(props) {
return <div>
<p>{props.name}</p>
</div>
}

这样的话我们就能够复用这个组件

1
2
3
4
5
6
7
function App(){ 
return <div>
<h1>Members</h1>
<Person name={Sally Amaki} />
<Person name={Nagomi Saijo} />
</div>
}

props 是以键值对的形式向下传递的ß

语法糖

使用...来一次性向下传递所有props

1
<Recipe {...RECIPE_PIZZA} />

组件里接收的props也可以被解构

1
2
function Person({name, age}) {
}

如果组件不从props里获取任何数据,也可以省略括号里的props

1
2
function Person() {
}

Callback Handlers

在 React 中,事件监听器接收回调函数作为参数,而不是函数调用

1
<Button onClick={() => alert('hello!')}>Say hello!</Button>

React State Basics

State是我们用来追踪组件变化的东西

以这段代码举例

1
2
const [name, setName] = useState("James");
setName("Jim");

在这段代码中,我们会创建一个name,在最开始,它会是James,我们可以在代码中使用setName改变name

useState是一个React Hook,这是React自带的一个功能可以告诉React用新数据去渲染页面

在这其中,name和setName是一个tuple,我们从useState中获取这个tuple

setName是一个mutator function,我们使用这个函数去改变name

需要注意的是,我们需要避免直接改变name,也就是说我们不能直接改变name,一定要使用setName去改变他

当我们使用一个mutator function,他会告诉React我们需要去重新渲染页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React, { useState } from 'react'

export default function Person(props) {
const [name, setName] = useState();

function giveName() {
const allNames = ["Amy", "Ben", "Eli", "Ian", "Leo", "Mia"];
const randI = Math.floor(Math.random() * allNames.length);
setName(allNames[randI]);
}

return <div>
<h1>My name is {name}.</h1>
<button onClick={giveName}>Give New Name</button>
</div>
}

以上面这段代码为例,每次从names里获取一个新的名字,使用setName更新名字

需要注意的是,state的变化也是异步的,也就是说如果我们我们调用

1
console.log(name)

他的结果会是undefined或者上一个值

改变State的两种方法

当我们直接使用mutator function的时候,例如setLikes(0),他则会覆盖现有的值

如果使用setLikes(old => old + 1),这种带有回调函数的方法,则会在现有的值基础上更新

使用useEffect fetch数据

useEffect可以让我们在整个组件的更新周期中在一个特定的时间点去运行其中的函数

1
2
3
useEffect(() => {
// 这段代码将只在组件挂载时运行,且只运行一次。
}, [])

我们需要将其放入useEffect的原因是,每次当state发生改变,React都会重新渲染页面,如果fetch不在useEffect内,每次都会触发fetch,但是实际上,我们并不需要这么多次的fetch

Rendering Basics

一个组件会在以下两种情况发生改变后重新渲染

  1. 他的props发生改变
  2. 他的state发生改变

有关生命周期的更多信息会在后面的课程被提及