原文链接(格式更好):《4-1 React 基础》
发展要史
1、JS 能操作 DOM
<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 帮忙实现
const App = () => (
<div id="app">
<h1>Hello</h1>
<h2>my name is</h2>
<h3>react</h3>
</div>
)
// 经过 Babel的 @babel/preset-react 编译后
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
const App = () => _jsxs("div", {
id: "app",
children: [
_jsx("h1", { children: "Hello"}),
_jsx("h2", { children: "my name is"}),
_jsx("h3", { children: "react" })
]
});
总结:React 核心是写 HTML 片段(JSX),然后让 Babel 编译为可执行的渲染函数,调用该函数能创建 DOM,但 React 做的事情不止于此,但可以这样简单的理解。
基础
创建一个基础的 React 项目
npx create-react-app projectName
组件分类
类组件
基础写法
import React, { Component } from 'react'
export default class ClassComName extends Component {
render() {
return <div>I am Class Component</div>
}
}
响应式数据写法
this.state、this.setState
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>
);
}
}
父子传参
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:已弃用,渲染之前执行,若有了getDerivedStateFromProps或getSnapshotBeforeUpdate时不执行
render:执行,生成新的虚拟 DOM 用于 DIFF(若需要),但还未去更新 DOM 哦
componentDidMount:渲染完成时调用,常用于网络请求、订阅或者手动DOM操作等。
更新阶段
componentWillReceiveProps:已弃用,props 改变时触发,建议用getDerivedStateFromProps
shouldComponentUpdate:更新拦截,根据返回的布尔值决定是否更新。可用于数据提交中时锁定其他操作
componentWillUpdate:已弃用,更新前调用,建议用getSnapshotBeforeUpdate
render:执行,生成新的虚拟 DOM 用于 DIFF(若需要),但还未去更新 DOM 哦
getSnapshotBeforeUpdate:获取更新 DOM 前的快照,获取元素某些信息(如滚动位置)
componentDidUpdate:更新完成时调用,谨慎调用setState会触发重新渲染
销毁阶段
componentWillUnmount:销毁之前调用,用于清理任何定时器、取消网络请求、解绑事件监听器等资源清理工作。
函数式组件
基础写法
export default function FuncComName() {
return <div>I am Function Component</div>
}
响应式数据写法
useState
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>
);
}
父子传参
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
处理副作用。
useEffect(fun, deps)
fun:它的返回值将在下一次 fun 执行前调用
deps:数组,当值改变时,执行上一次的 fun 返回值,然后再执行 fun
// 代替: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
参考资料:
Fiber:本质是 JS 对象,也可以称为 React 版的虚拟 DOM。链表结构
Vue vs React
Vue:使用 template,写法比较固定,但编译时可以做很多优化。
React:使用 JSX,写法非常灵活