4-1、React 基础

发展要史

1、JS 能操作 DOM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">
<h1>Hello</h1>
<h2>my name is</h2>
<h3>react</h3>
</div>

// 可以使用 JS 实现
function createElememt(tag, props, children) {
const el = document.createElememt(tag)

Object.entries(props).forEach(([key, value]) => {
el.setAttribute(key, value)
})


children.forEach(child => el.appendChild(child))
}

createElememt('div', { id: 'app' }, [
createElememt('h1', null, ['Hello']),
createElememt('h2', null, ['my name is']),
createElememt('h3', null, ['react']),
])

2、根据createElememt就能生成对应的 DOM 结构,但发现有点麻烦,所以 React 让 Babel 帮忙实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const App = () => (
<div id="app">
<h1>Hello</h1>
<h2>my name is</h2>
<h3>react</h3>
</div>
)

// 经过 Babel的 @babel/preset-react 编译后
const App = () => React.createElement("div", { id: "app" },
React.createElement("h1", null, "Hello"),
React.createElement("h2", null, "my name is"),
React.createElement("h3", null, "react")
);

总结:React 核心是写 HTML 片段(JSX),然后让 Babel 编译为可执行的渲染函数,调用该函数能创建 DOM,但 React 做的事情不止于此,但可以这样简单的理解。

基础

创建一个基础的 React 项目

1
npx create-react-app projectName

组件分类

类组件

基础写法

1
2
3
4
5
6
import React, { Component } from 'react'
export default class ClassComName extends Component {
render() {
return <div>I am Class Component</div>
}
}

响应式数据写法

this.statethis.setState

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
import React, { Component } from "react";
export default class ClassComName extends Component {
constructor() {
super();

this.state = {
name: "Class Component~",
number: 1,
};
}

handleClick() {
this.setState({ number: this.state.number + 1 });
}

render() {
return (
<div>
<h1>I am 【{this.state.name}】</h1>
<button onClick={() => this.handleClick()}>Add Number</button>
<div>Number: {this.state.number}</div>
<hr />
</div>
);
}
}

父子传参

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 React, { Component } from "react";
export default class ClassComName extends Component {
constructor(props) {
super(props); // ⭐️ 父子传参-接收传入参数

this.state = {
name: "Class Component~",
number: props.initNumber, // ⭐️ 父子传参-使用父传入的参数
};
}

handleClick() {
const number = this.state.number + 1;
this.setState({ number });

// ⭐️ 父子传参-调用父传入的函数
this.props.callback(number, this.state.name);
}

render() {
return (
<div>
<h1>I am 【{this.state.name}】</h1>
<div>props: {JSON.stringify(this.props)}</div>

<button onClick={() => this.handleClick()}>Add Number</button>
<div>Number: {this.state.number}</div>
<hr />
</div>
);
}
}

生命周期

初始化阶段

constructor:初始化调用,初始化实例状态(state)和绑定事件处理器,不推荐在这里做数据获取或订阅操作。
static getDerivedStateFromProps:在实例创建及后续每次 props 改变时都会调用,返回对象将与当前 state 合并,生成新的 state
componentWillMount已弃用,渲染之前执行,若有了getDerivedStateFromPropsgetSnapshotBeforeUpdate时不执行
render:执行,生成新的虚拟 DOM 用于 DIFF(若需要),但还未去更新 DOM 哦
componentDidMount:渲染完成时调用,常用于网络请求、订阅或者手动 DOM 操作等。

更新阶段

componentWillReceiveProps已弃用,props 改变时触发,建议用getDerivedStateFromProps
shouldComponentUpdate:更新拦截,根据返回的布尔值决定是否更新。可用于数据提交中时锁定其他操作
componentWillUpdate已弃用,更新前调用,建议用getSnapshotBeforeUpdate
render:执行,生成新的虚拟 DOM 用于 DIFF(若需要),但还未去更新 DOM 哦
getSnapshotBeforeUpdate:获取更新 DOM 前的快照,获取元素某些信息(如滚动位置)
componentDidUpdate:更新完成时调用,谨慎调用setState会触发重新渲染

销毁阶段

componentWillUnmount:销毁之前调用,用于清理任何定时器、取消网络请求、解绑事件监听器等资源清理工作。

函数式组件

基础写法

1
2
3
export default function FuncComName() {
return <div>I am Function Component</div>
}

响应式数据写法

useState

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

export default function FuncComName() {
const [name] = useState("Function Component~");

const [number, setNumber] = useState(1);

const handleClick = () => {
setNumber(number + 1);
};
return (
<div>
<h1>I am 【{name}】</h1>
<button onClick={() => handleClick()}>Add Number</button>
<div>Number: {number}</div>
<hr />
</div>
);
}

父子传参

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
import { useState } from "react";

export default function FuncComName(props) { // ⭐️ 父子传参-接收传入参数
const [name] = useState("Function Component~");

const [number, setNumber] = useState(props.initNumber); // ⭐️ 父子传参-使用父传入的参数

const handleClick = () => {
const _number = number + 1;
setNumber(_number);

// ⭐️ 父子传参-调用父传入的函数
props.callback(_number, name);
};
return (
<div>
<h1>I am 【{name}】</h1>
<div>props: {JSON.stringify(props)}</div>

<button onClick={() => handleClick()}>Add Number</button>
<div>Number: {number}</div>
<hr />
</div>
);
}

生命周期

函数式组件是没有生命周期的,只有一些钩子函数来替代生命周期。

useEffect

处理副作用。

1
2
3
useEffect(fun, deps)
fun:它的返回值将在下一次 fun 执行前调用
deps:数组,当值改变时,执行上一次的 fun 返回值,然后再执行 fun
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 代替:getDerivedStateFromProps
// 实例创建时触发:
useEffect(() => { console.log('getDerivedStateFromProps') }, [])

// props 改变时触发:
useEffect(() => { console.log('getDerivedStateFromProps') }, [props])
// 代替:componentDidMount
// 渲染完成后触发:
useEffect(() => { console.log('componentDidMount') }, [])
// 代替:componentWillUnmount
// 销毁前触发:
useEffect(() => {
return () => {
console.log('componentWillUnmount')
}
}, [])
// 代替:componentDidUpdate
// 更新完成后触发:
useEffect(() => {
console.log('componentDidUpdate')
})

补充知识

Fiber

参考资料:
万字长文介绍 React Fiber 架构的原理和工作模式
带你彻底读懂 React VDOM DIFF - 掘金
Fiber:本质是 JS 对象,也可以称为 React 版的虚拟 DOM。链表结构

Vue vs React

Vue:使用 template,写法比较固定,但编译时可以做很多优化。
React:使用 JSX,写法非常灵活


4-1、React 基础
https://mrhzq.github.io/职业上一二事/前端面试/前端八股文/4-1、React 基础/
作者
黄智强
发布于
2024年1月13日
许可协议