JSX语法
- 使用 JSX 语法,可以更好的描述UI呈现的样子,可读性强;
- JSX是 React.creatElement() 的语法糖,JSX用babel编译以后就是 React.createElement() 方法的调用;
- 相对于 Vue template 来说,JSX不需要引入新概念,用法与 JS 相同;
react的特性
- 使用JSX语法,可以更好的描述UI呈现的样子,可读性强;没有引入新概念,使用JSX语法,相比之下,Vue就有很多模版语法;
- 可以跨平台,可以与其他库一起使用,例如react-native;
- 使用Virtual DOM (Vue也用)和 Diff算法差异化更新,减少操作DOM,加快更新速度,但是需要消耗额外的内存(因为新增V-DOM);
- 使用Fiber架构,将 Virtual DOM 原本的结构替换为 fiber 节点,将原本的递归遍历改为循环遍历,切分工作单元,为不同类型的任务添加优先级,且允许中止/复用/重启渲染任务,这就是更新后的react diff算法;
fiber / React fiber
- fiber(纤维),意在表示比线程(Thread)更小的工作单元;
- diff 算法进行比较时,采用 fiber 节点替换原有的 Virtual DOM 结构,能够使得渲染过程可中断、复用、重启;
- react15之前流程:setState => 先协调(reconciler)任务 => 生成WorkInProgress Tree =>给有修改的 V-DOM 节点打 diff 标记 => render 修改节点,渲染页面
- 改为fiber节点以后,协调器从stack reconciler 换成 React fiber,将同步渲染改成了异步渲染;
- 流程变化:setState => 先协调(reconciler)任务 => 生成WorkInProgress fiber Tree =>给有修改的 fiber 节点打 diff 标记 => 生成新的WorkInProgress fiber Tree => render 修改节点(替换WorkInProgress fiber Tree),渲染页面
class组件与函数组件区别
- 编写形式的不同
类组件使用es6去编写;继承于React.Component;
函数组件通过函数的形式去实现一个React组件; - 状态管理:类组件使用this.state改变状态值;函数组件使用hook进行状态管理,每一个状态值都有自己的setstate;
- 生命周期:类组件有生命周期;函数组件是无状态组件没有生命周期,可以用useEffect来代替生命周期的作用;
- 值捕获特性(capture value 固化值)
类组件可以拿到最新state;
class CaptureValue extends React.Component {
constructor(props) {
super(props);
this.state = {
num: 0
}
}
handleClick(){
setTimeout(() => {
console.log(this.state.num);//打印1
}, 3000)
this.setState({ num: 1 });
}
render() {
return <button onClick={()=>this.handleClick()} >我是类组件</button>
}
}
函数式组件有值捕获特性,每一次render都会保存自己的props和state,每一次render都是一个独立的过程;state 和 effect都具有固化值特性,所以导致下方打印的num是上一次render后缓存的num值;但是useRef可以跳过Capture Value,保持唯一引用,拿到最终状态,所以可以通过useRef获取最新值;
第一次render => 缓存这一次的state和props => 点击button,调用handleClick,获取到第一次的num => setNum(1) => 触发render => 缓存这次render后的固化值state => …
export default function CaptureValueFunction() {
const [num, setNum] = useState(0);
const handleClick = () => {
setTimeout(() => {
console.log(num);//打印0
}, 3000)
setNum(1);
}
return <button onClick={handleClick}>我是函数组件</button>
}
- 调用方式:类组件需要先实例化组件,再调用实例的render方法;函数组件是一个函数,直接调用传入props即可;
//类组件code部分
class myComp extends React.Component {
render(){
return <div>我是类组件</div>
}
}
//React内部执行
const instance = new myComp(props); //创建了一个myComp的实例,将props传进去
const result = instance.render(); //<div>我是类组件</div>
//函数组件code部分
function myComp(){
return <div>我是函数组件</div>
}
//React内部执行
const result = myComp(props);//<div>我是函数组件</div>
hook和生命周期之间有什么关联?
- constructor
函数组件创建执行相当于constructor,函数组件不需要constructor,可以使用useState初始化state; - static getDerivedStateFromProps相当于useState里面的更新函数
使用setState更新state,可以达到getDerivedStateFromProps的目的;它是一个静态函数; - shouldComponentUpdate相当于useMemo和React.memo
可以使用React.memo包裹组件,它会浅比较props,缓存组件;也可以用useMemo缓存每一个节点; - render
函数组件的返回值就是render的返回值,render相当于函数组件体本身; - componentDidMount相当于useEffect
componentDidMount里面需要执行的内容可以放在useEffect里面执行,它会在render后执行; - getSnapshotBeforeUpdate
在render之后DOM更新之前执行,获取最新的DOM数据,返回的值作为componentDidUpdate的第三个参数; - componentDidUpdate(prevProps, prevState, snapshot)
- componentWillUnMount相当于useEffect返回的清除函数cleanup
componentWillUnMount中需要清除的副作用可以在useEffect中返回一个清除函数,在这个清除函数中执行,它会在卸载之前执行; - componentDidUnMount
- componentDidCatch和getDerivedStateFromError(捕获错误)
暂时没有对应的hook;任何生命周期或者子组件的渲染阶段发生错误就会执行componentDidCatch(error,info);componentDidCatch打印错误信息;getDerivedStateFromError渲染备用UI;
class CatchError extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };//用这个state区分降级UI
}
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能够显示降级后的 UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 错误日志发给服务器
logErrorToMyService(error, errorInfo);
}
render() {
if(this.state.hasError){
//降级UI
return <h1>Someing is wrong.</h1>
}
return <button onClick={()=>this.handleClick()} >我是类组件</button>
}
}
哪些操作会触发react页面重新渲染
setState无条件触发 || props改变 || 父组件重新渲染 || context变化(可以使用useMemo包裹provider中的value可以避免不必要的re-render) || hooks变化