开发环境搭建

create-react-app 是一个快速创建 React 开发环境的工具,底层由 Webpack 构件,封装了配置细节

执行命令:

1
npx create-react-app react-test01
  1. npx:Node.js 工具命令,查找并执行后续的包命令
  2. create-react-app:核心包,用于创建 React 项目
  3. react-test01:React 项目的名称(自定义)

创建项目的更多方法:https://zh-hans.react.dev/learn/start-a-new-react-project

image.png

使用命令运行项目:

1
npm start

image.png

成功进入项目页面

image.png

项目目录:

  1. App.js:项目根组件
  2. index.js:项目入口

image.png

JSX

JSX 是 JavaScript 和 XML (HTML)的缩写,表示在 JS 代码中编写 HTML 模块结构,它是 React 中编写 UI 模块的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
const message = 'this is message'

function App() {
return (
<div className="App">
Hello React!
<br/>
{message}
</div>
);
}

export default App;

优势:

  1. HTML 的声明式模块写法
  2. JS 的可编程能力

JSX 并不是标准的 JS 语法,它是 JS 的语法扩展,浏览器本身不能识别,需要通过解析工具做解析之后才能在浏览器中运行

03.png

JSX-表达式

  1. 使用引号传递字符串
  2. 使用JS变量
  3. 函数调用和方法调用
  4. 使用JavaScript对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const num = 666
const getNum = () => {
return num
}

function App() {
return (
<div className="App">
Hello React!
{/* 引号传递字符串 */}
{'this is String'}
{/* 使用JS变量 */}
{num}
{/* 函数调用和方法调用 */}
{getNum()}
{new Date().getDate()}
{/* 使用JS对象 */}
<div style={{color: 'red'}}>Hello</div>
</div>
);
}

注意:if语句、switch语句、变量声明不属于表达式,不能出现在{}

JSX-实现列表渲染

在 JSX 中可以使用原生 JS 中的 map 方法遍历渲染列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const list = [
{id: 1001, name: 'Vue'},
{id: 1002, name: 'React'},
{id: 1003, name: 'Angular'}
]

function App() {
return (
<div className="App">
<ul>
{list.map(item=><li key={item.id}>{item.id}{item.name}</li>)}
</ul>
</div>
);
}

JSX-实现条件渲染

在 React 中,可以通过逻辑与运算符&&三元表达式? : 实现基础的条件渲染

1
2
3
4
5
6
7
8
function App() {
return (
<div className="App">
{true && <div style={{color: 'red'}}>Hello</div>}
{false ? new Date().getMonth() : new Date().getDate()}
</div>
);
}

JSX-实现复杂条件渲染

通过 自定义函数 的方式实现复杂条件渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const type = 1  // 0|1|2

function getArticleJSX(){
if(type === 0){
return <div>无图模式模版</div>
}else if(type === 1){
return <div>单图模式模版</div>
}else(type === 2){
return <div>三图模式模版</div>
}
}

function App(){
return (
<>
{ getArticleJSX() }
</>
)
}

React 事件绑定

基本实现

通过语法 on + 事件名称 = { 事件处理程序 }

1
2
3
4
5
6
7
8
9
function App() {
const clickHandle = () => {
console.log('点击')
}
return (
<button onClick={clickHandle}>点击</button>
);
}
export default App;

使用事件参数

在事件回调函数中设置形参即可

1
2
3
4
5
6
7
8
9
10
function App() {
const clickHandle = (e) => {
console.log('点击了', name)
}
return (
<button onClick={clickHandle}>点击</button>
);
}

export default App;

传递自定义参数

事件绑定的位置改造成箭头函数的写法,在执行实际处理业务函数的时候传递实参

1
2
3
4
5
6
7
8
9
10
function App() {
const clickHandle = (name) => {
console.log('点击了', name)
}
return (
<button onClick={() => clickHandle('按钮')}>点击</button>
);
}

export default App;
  • 注意:不能直接写函数调用,这里事件绑定需要一个函数引用

同时传递事件对象和自定义参数

在事件绑定的位置传递事件实参 e 和自定义参数,需要声明形参

1
2
3
4
5
6
7
8
9
10
function App() {
const clickHandle = (name, e) => {
console.log('点击了', name, e)
}
return (
<button onClick={(e) => clickHandle('按钮', e)}>点击</button>
);
}

export default App;

React 组件使用

在 React 中,一个组件就是首字母大写的函数,内部存放了组件的逻辑和视图UI,渲染组件只需要把组件当成标签书写即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Button(){
return <button>click me</button>
}

function App(){
return (
<div>
{/* 自闭和 */}
<Button/>
{/* 成对标签 */}
<Button></Button>
</div>
)
}

组件状态管理-useState

useState 是一个 React Hook (函数),它允许我们向组件添加一个状态变量,从而实现响应式变量,控制组件渲染结构(数据驱动视图)

  1. useState 是一个函数,返回值是一个数组
  2. 数组中的第一个参数是状态变量,第二个参数是 set 函数,用来修改状态变量
  3. useState 的参数将作为 count 的初始值
1
2
3
4
5
6
7
8
9
10
11
12
13
import { useState } from "react";

function App() {
const [count, setCount] = useState(0)
const clickHandler = () => {
setCount(count + 1)
}
return (
<button onClick={clickHandler}>{count}</button>
);
}

export default App;

状态修改规则

在 React 中,状态是只读的,只能替换它不能修改它,直接修改不会引发视图更新

1
2
3
4
// 例如上面的例子,直接自增是不能实现视图更新的
setCount(++count)
// 需要传入新的值,使新的值得以渲染
setCount(count + 1)

修改对象状态

对于对象类型的状态变量,始终传给 set 方法一个全新的对象来进行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { useState } from "react";

function App() {
const [count, setCount] = useState({
num: 0,
name: 'Alien'
})
const clickHandler = () => {
setCount({
...count,
num: count.num + 1,
name: 'React'
})
}
return (
<div>
{count.name}
<button onClick={clickHandler}>{count.num}</button>
</div>
);
}

export default App;

组件样式处理

  1. 行内样式
1
<div style={{ color:'red'}}>this is div</div>
  1. 对象样式
1
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
const nameStyle = {
color: 'blue',
fontSize: '50px'
} // 通过对象的方式传入样式

function App() {
const [count, setCount] = useState({
num: 0,
name: 'Alien'
})
const clickHandler = () => {
setCount({
...count,
num: count.num + 1,
name: 'React'
})
}
return (
<div>
<div style={nameStyle}>{count.name}</div>
<button onClick={clickHandler}>{count.num}</button>
</div>
);
}

export default App;
  1. class 类名控制(导入样式表)
1
2
3
.foo{
color: red;
}
1
2
3
4
5
6
7
8
9
import './index.css'

function App(){
return (
<div>
<span className="foo">this is span</span>
</div>
)
}

React 表单控制

受控绑定

使用 React 组件的状态(useState)控制表单的状态

01.png

1
2
3
4
5
6
7
8
9
10
function App(){
const [value, setValue] = useState('')
return (
<input
type="text"
value={value}
onChange={e => setValue(e.target.value)}
/>
)
}

非受控绑定

通过获取 DOM 的方式获取表单的输入数据

在 React 组件中获取/操作 DOM,需要使用 useRef React Hook钩子函数,分为两步:

  1. 使用 useRef 创建 ref 对象,并与 JSX 绑定
  2. 在 DOM 可用时,通过 inputRef.current 拿到 DOM 对象
1
2
3
4
5
6
7
8
9
10
11
import { useRef } from "react";

function App() {
const inputRef = useRef(null)
const onChange = () => {
console.log(inputRef.current.value)
}
return (
<input type="text" ref={inputRef} onChange={onChange}></input>
);
}

React 组件通信

父子通信-父传子

  1. 父组件传递数据:在子组件标签上绑定属性
  2. 子组件接收数据:子组件通过 props 参数接收数据
1
2
3
4
5
6
7
8
9
10
11
12
13
function Son(props){
return <div>{props.text}</div>
}

function App() {
const name = 'this is name'

return (
<div>
<Son text={name}></Son>
</div>
);
}

props可以传递任意的合法数据,比如数字、字符串、布尔值、数组、对象、函数、JSX

props是只读对象,子组件只能读取 props 中的数据,不能直接进行修改,父组件的数据只能由父组件修改

当我们的内容嵌套在组件的标签内部时,组件会自动在名为 children 的 props 属性中接收该内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Son(props){
return <div>{props.text}{props.children}</div>
}

function App() {
const name = 'this is name,'

return (
<div>
<Son text={name}>
<span>Hello React</span>
</Son>
</div>
);
}

父子通信-子传父

在子组件中调用父组件中的函数并传递参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Son(props){
const sonMsg = 'this is son\'s message'
return (
<div>{props.text}{props.children}
<button onClick={()=>props.onGetMsg(sonMsg)}>OK</button>
</div>
)
}

function App() {
const name = 'this is name,'
const getMsg = (msg) => console.log(msg)

return (
<div>
<Son text={name} onGetMsg={getMsg}>
<span>Hello React</span>
</Son>
</div>
);
}

兄弟组建通信

借助状态提升机制,通过共同的父组件进行兄弟之间的数据传递

  1. A 组件先通过子传父的方式将数据传递给父组件 App
  2. App 拿到数据之后通过父传子的方式传递给 B 组件
1
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
import { useState } from "react"

function A ({ onGetAName }) {
const name = 'this is A name'
return (
<div>
this is A compnent,
<button onClick={() => onGetAName(name)}>send</button>
</div>
)
}

function B ({ name }) {
return (
<div>
this is B compnent,
{name}
</div>
)
}

function App () {
const [name, setName] = useState('')
const getAName = (name) => {
setName(name)
}
return (
<div>
this is App
<A onGetAName={getAName} />
<B name={name} />
</div>
)
}

跨层组件通信

  1. 使用createContext方法创建一个上下文对象MsgContext
  2. 在顶部组件中通过MsgContext.Provider组件提供数据
  3. 在底部组件中通过useContent钩子函数获取消费数据
1
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
import { createContext, useContext } from "react"

const MsgContext = createContext()

function A () {
return (
<div>
this is A component
<B />
</div>
)
}

function B () {
const msg = useContext(MsgContext)
return (
<div>
this is B component,{msg}
</div>
)
}

function App () {
const msg = 'this is app msg'
return (
<div>
<MsgContext.Provider value={msg}>
this is App
<A />
</MsgContext.Provider>
</div>
)
}

React 副作用管理-useEffect

useEffect 是一个 React Hook 函数,用于在 React 组件中创建不是由事件引起而是由渲染本身引起的操作(副作用),比如发送 AJAX 请求、更改 DOM 等等

10.png

上面的组件中没有发生任何的用户事件,组件渲染完毕之后就需要和服务器要数据,整个过程属于”只由渲染引起的操作“

使用方法

在组件渲染完毕之后,立刻从服务端获取频道列表数据并显示到页面中

1
useEffect(() => { }, [])
  1. 参数1是一个函数,可以把它叫做副作用函数,在函数内部可以放置要执行的操作
  2. 参数2是一个数组(可选参),在数组里放置依赖项,不同依赖项会影响第一个参数函数的执行,当是一个空数组的时候,副作用函数只会在组件渲染完毕之后执行一次

依赖项

useEffect副作用函数的执行时机存在多种情况,根据传入依赖项的不同,会有不同的执行表现

依赖项 副作用函数的执行时机
没有依赖项 组件初始渲染 + 组件更新时执行
空数组依赖 只在初始渲染时执行一次
添加特定依赖项 组件初始渲染 + 依赖项变化时执行

消除副作用

在useEffect中编写的由渲染本身引起的对接组件外部的操作,社区也经常把它叫做副作用操作,比如在useEffect中开启了一个定时器,我们想在组件卸载时把这个定时器再清理掉,这个过程就是清理副作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { useEffect, useState } from "react"

function Son () {
useEffect(() => {
const timer = setInterval(() => {
console.log('定时器执行中...')
}, 1000)

return () => {
clearInterval(timer)
}
}, [])
return <div>this is son</div>
}

function App () {
const [show, setShow] = useState(true)
return (
<div>
{show && <Son />}
<button onClick={() => setShow(false)}>卸载Son组件</button>
</div>
)
}

消除副作用的函数最常见的执行时机是在组件卸载时自动执行

自定义Hook函数

自定义 Hook 是以 use 开头的函数,通过自定义 Hook 函数可以用来实现逻辑的封装和复用

  1. 声明一个以use打头的函数
  2. 在函数体内封装可复用的逻辑(只要是可复用的逻辑)
  3. 把组件中用到的状态或者回调return出去(以对象或者数组)
  4. 在哪个组件中要用到这个逻辑,就执行这个函数,解构出来状态和回调进行使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { useState } from "react"

function useToggle () {
const [value, setValue] = useState(true)
const toggle = () => setValue(!value)
return {
value,
toggle
}
}
function App () {
const { value, toggle } = useToggle()
return (
<div>
{value && <div>this is div</div>}
<button onClick={toggle}>toggle</button>
</div>
)
}

React Hooks 使用规则

  1. 只能在组件中或者其他自定义 Hook 函数中调用
1
2
3
4
const [value, setValue] = useState('') // 错误示范,不能在组件外货自定义Hook函数外调用
function App() {
return ()
}
  1. 只能在组件的顶层调用,不能嵌套在if、for、其它的函数中
1
2
3
4
5
6
function App() {
if(Math.random() > 0.5) {
const [value, setValue] = useState('') // 错误示范,不能嵌套在if、for、其它的函数中
}
return()
}

配置别名路径

通过配置可以实现将@/解析为src/,可以更加方便地填写路径

  1. 路径解析配置(webpack),把 @/ 解析为 src/
  2. 路径联想配置(VsCode),VsCode 在输入@/时,自动联想出来对应的 src/ 下的子级目录

路径解析配置

配置步骤

  1. 安装craco
1
npm i -D @craco/craco
  1. 项目根目录下创建配置文件 craco.config.js
  2. 配置文件中添加路径解析配置
  3. 包文件中配置启动和打包命令

13.png

联想路径配置

配置步骤:

  1. 根目录下新增配置文件jsconfig.json
  2. 添加路径提示配置
1
2
3
4
5
6
7
8
9
10
{
"compilerOptions":{
"baseUrl":"./",
"paths":{
"@/*":[
"src/*"
]
}
}
}