我们先来看看 react官网 怎么介绍react:
我们在来看看目前react的热度:
The State of JS 近期(报告数据截止2022年底)对近 40,000 名 Web 开发人员的调查结果再次显示了,React 仍然是使用最广泛的前端框架,使用率为 81.8%,领先于 Angular(48.8%) 和 Vue(46.2)。(ps:在这个调查中:在回答有关 JavaScript 编程风格问题的人中,TypeScript 的使用率高达 98.9%);
ps:当然了这些数据是国外调查的,和国内的使用具体情况多多少少会有点偏差,但是总体上 react 依旧是主流;
The State of JS 2022: Front-end Frameworks
说白了react就是我们可以在一个jsx文件里面,既写js又写dom(虚拟),把其结合起来形成一个组件,以一个个组件的形式形去开发,形成我们项目;而且react通过对dom模拟使用了虚拟dom,最大限度的减少与dom的交互;
虚拟dom是什么?
虚拟DOM本质上是一个js对象,通过对象来表示真实的DOM结构
要想了解虚拟dom存在的意义,就要先知道浏览器dom渲染过程:构建dom树,构建cssom树,把dom树和cssom树结合起来构建render树,然后布局,最后合成图层显示在屏幕上;
如果每次都时刻去进行dom操作,例如突然来一个遍历10w次dom操作,对性能方面真的很浪费,但是有了虚拟dom就不一样了,我们可以先在虚拟dom对象上先进行操作(diff算法)(因为是虚拟的,不会每次变更就触发重新渲染),等在这个虚拟dom上完成操作在挂载到真实的dom上(创建一个新节点、删除原先节点、将新节点挂载上去),再去更新,性能不比渲染10w次dom香,另外由于虚拟dom保存的是js对象,天然的具有跨平台的能力,而不仅仅局限于浏览器;
jsx是什么?
jsx是js的语法扩展,它充分具备JavaScript的能力,是js的扩展版(js能做的,jsx也能做,js不能做的,jsx也能做),运用于react架构中
当然你在浏览器、node等上是无法直接编译运行jsx文件的,需要借助babel去将jsx转译成js去运行,不然你以为在jsx上面写一些dom能直接用呀,jsx结合上react具体编译如下:
jsx可以提高我们代码的可阅读性,提高我们的开发效率,不用使用React.createElement()去开发
jsx都知道是啥了,学过ts的应该知道tsx是啥吧,不展开啦!
万事万物都有创建到销毁的过程,这一个过程就叫做生命周期,react 也有自己的生命周期,要想在react开发过程中对性能有高要求,那就必须对 react 的生命周期足够的了解,你必须了解 react 是如何从挂载
到渲染
最后再到卸载
的;
ps:我们下面讲的主要是react中类的生命周期,函数式组件是没有生命周期函数的(当然在后面章节介绍函数式组件中我会谈谈如何通过hooks来模拟生命周期)
生命周期的三个阶段
挂载(Mounting)
:这是一个组件被初始化创建到最后插入到dom中的过程;渲染/更新(Updating)
:这是一个组件中state或props发生变更后到重新提交到dom中的过程;卸载(Unmounting)
:这是将组件从dom中移除的过程1、挂载--Mounting(开始环节)
这是react组件的生命周期中的第一个环节,这个环节相关周期函数如下
constructor
在constructor()
这个环节我们的组件进行中了初始化的工作,例如对组件的 state 进行初始化,这个方法只会执行一次,在这个生命周期函数中,我们可以直接对this.state
进行赋值(其他生命周期函数中如果想要修改state的话要使用this.setState
)
我们一般使用这个生命周期做什么?
super(props)
操作;this.state
注意:
props
和context
,我们想要在当前组件下使用的话就需要在super
传人参数,不然再该组件中无法正常去使用props
props
的值赋给state
,毫无必要你完全可以直接使用this.props
,你把props
赋给state
然后使用的是state
,那当props
发生变更时,state
并不会影响受到影响的,别瞎折腾!!!constructor(props) { super(props); this.state = { color: props.color // 别瞎折腾,这是错误的 }; }
componentWillMount (16.9后弃用)
在组件挂载至 DOM 之前调用,并且只会调用一次。它在render
方法之前调用,因此在componentWillMount
中调用this.setState
不会触发额外的渲染;
我们一般会在这个生命周期做什么?
在 componentWillMount 中引入订阅内容吧,哎呀很少人用,不推荐用
注意:
细心的同学看上面的生命周期图会发现没有componentWillMount
这个钩子,在react 16.9版本之后就被弃用了,虽然你还可以用,但是控制台会有警告
为啥弃用componentWillMount
?
因为新的异步架构会导致它被多次调用,所以网络请求以及事件绑定应该放到 componentDidMount
中,React想约束使用者,让大家写出容易维护和扩展的代码的代码吧(个人理解,欢迎讨论)
当然弃用的还包括我们后面说的componentWillUpdate
、componentWillReceiveProps
道理一样他们有可能会多次调用
那有什么可以替代上面说的三个生命周期函数吗?
有,react16.3 中推出了 getDerivedStateFromProps
,
render(必要)
render()
方法是类组件中唯一必须实现的方法,它的返回值将作为页面渲染的视图。render
函数应该为纯函数,也就是对于相同的 state 和 props,它总是返回相同的渲染结果。
render 函数被调用时,会返回以下四种类型之一:
componentDidMount(常用)
在完成render()
后接下来就是compomentDidMount()
,他在组件被插到dom树上后(发生在浏览器更新视图之前)立即调用,该方法也只会执行一次;
我们一般会在这个生命周期做什么?
这个生命周期函数使用频率就高了,一般我们会在上面做ajax请求,然后通过请求返回的数据去更新state使组件重新渲染,或者在这个阶段做一些订阅例如监听事件
注意:
compomentDidMount()
中使用this.setState()
会触发额外的渲染再一次调用render函数,但是浏览器中的视图更新只会执行一次;compomentDidMount()
中做订阅,一定要记得在conponentWillUnmount()
中取消订阅啊!!!2、更新 -- Updating(运行中的环节)
这是react组件的生命周期中的运行中的环节,只要组件不卸载,这个环节就会一直在,这个环节相关周期函数如下:
ps:在这个阶段的生命周期勾子函数使用setState
一点要非常非常慎重,最好不用吧,就算要用也要加上判断逻辑,不然就陷入死循环了,那不就gg了!!!
componentWillReceiveProps(旧生命周期 - 16.9后弃用)
当已经挂载的组件中的props发生变更就会触发,在这个函数中你可以比较新旧两个props的区别,进而去修改state,但这破坏了props数据的单一数据源。
我们一般在这个生命周期做什么?
这个函数中你可以比较新旧两个props的区别,进而去修改state,但这破坏了props数据的单一数据源,前面我们也说了不建议使用!
注意:
反正我们一般不用它
shouldComponentUpdate
在render
前调用,当props或state发生变化之前(即将变更但还没有变的时候)就会触发该组件的调用,默认返回的是 true,当你改为返回 false 时,那么不会更新组件,也就是不会执行后面的render
,它接受nextProps
和nextState
两个参数,我们可以将 this.props
(旧) 和 nextProps
(新) 进行比较;
我们一般在这生命周期做什么?
生命周期方法通常用来做性能优化,我们可以在这个生命周期进行函数,例如可以将this.props
和nextProps
比较,以及将this.state
与nextState
比较,并返回 false,让组件跳过更新;
注意:
shouldComponentUpdate
可以先对比值有没有变更在判断要不要更新,这个工作我们其实可以不用自己去写,也可以继承pureComponent
来性能优化,pureComponent
会帮你比较props或state有没有变更,有变更在调用shouldComponentUpdate
;pureComponent
,你以为对是值否发生变化不需要时间呀;shouldComponentUpdate(nextProps, nextState) { if (nextState.name !== this.state.name) { return true } return false }
componentWillUpdate(旧生命周期 - 16.9后弃用)
当props或state发生变化后(已发送state或props变更),此时就会调用这个生命周期函数;
我们一般在这个生命周期做什么?
哎呀,感觉没什么用
我们可以用componentDidUpdate
或getSnapshotBeforeUpdate
代替这个生命周期。如果你在此方法中读取 DOM 信息(例如,为了保存渲染前的dom宽度),则可以将此逻辑移至getSnapshotBeforeUpdate()
中。
getSnapshotBeforeUpdate(新生命周期 - 16.3后引入)
getSnapshotBeforeupdate
在最后一次渲染(提交到dom节点)之前调用,替换componetnWillUpdate
;
此生命周期函数在最近一次渲染提交至 dom 树之前执行,此时 dom 树还未改变,我们可以在这里获取 dom 改变前的信息,例如:更新前 dom 的滚动位置。
它接收两个参数,分别是:prevProps
、prevState
,上一个状态的 props 和上一个状态的 state。它的返回值将会传递给 componentDidUpdate 生命周期钩子的第三个参数。
我们一般在这个生命周期做什么呢?
需要获取更新前 DOM 的信息时。例如:需要以特殊方式处理滚动位置的聊天线程等。
注意:
如果return false
的话,则componentDidUpdate
不会被回调
componentDidUpdate(常用)
componentDidUpdate
在重新渲染完成后调用;
它接收两个参数,分别是:prevProps
、prevState
、snapshot
,上一个状态的 props 、上一个状态的 state、getSnapshotBeforeupdate
给他return的参数(默认undefined)
3、卸载 -- Unmounting(卸载中的环节)
当组件从 dom 中移除时开启这个环节
componenWillUnmount(常用)
componentWillUnmount
在组件卸载和销毁之前调用
我们一般在这个生命周期做什么呢?
我们想要在这一步去做一些清楚的动作,例如清除定时器、延时器、监听事件、网络请求、音/视频播放销毁、取消相关订阅;
注意:
这是一个必须的,减少bug发生的好编程习惯,一定要有,不要以为组件销毁了就拍拍屁股走人!!!
4、新的生命周期补充
getDerivedStateFromProps(新生命周期 - 16.3后补充,不建议用)
虽然不建议用当时还是讲下:
getDerivedStateFromProps
是一个静态方法,接收 nextProps 和 prevState 两个参数。它会在调用 render 方法之前被调用,不管是在初始挂载时还是在后续组件更新时都会被调用。
static getDerivedStateFromProps(nextProps, prevState) { const {name} = nextProps; // 当传入的name发生变化的时候,更新state if (name !== prevState.name) { return { name, }; } // 否则,对于state不进行任何操作 return null; }
react 官方文档
The State of JS 2022: Front-end Frameworks