本文是结合实践中和学习技术文章总结出来的笔记(个人使用),如有雷同纯属正常((✿◠‿◠))
React是目前国内最热门的两大前端MVVM框架之一,它拥有高效率的函数式组件和class类组件,通过输入数据吐出UI的模式受到很多前端工作者的喜爱
说到React,还得从React的JSX语法说起,可以说JSX的优越性让使用它的前端不得不说一个字:爽
以下将通过以下几个方面来详细展开React:
1.React的JSX语法
2.React的生命周期
//篇幅太大还是决定单开来做笔记
// 3.React15的diff算法
// 4.React16的fiber架构
// 5.React的数据流动
// 6.React-Redux
// 7.React-Hooks
// 8.React路由
// 9.React17的新特性
// 10.React18的新特性
一、JSX语法
2013年React带着JSX问世,可以说是从模板语法以来数据驱动视图方面有了质的飞跃。那么JSX语法是什么呢? 下面这个示例代码,render返回的内容就是由JSX语法填充的:
import React from 'react'; //react17以后不再需要引入
import ReactDOM from "react-dom";
export class App extends React.PureComponent {
state = {
text:'我是一段文字'
}
render() {
return (
<div>
<p>{this.state.text}</p>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
JSX是Javascript的一种语法扩展,但是它充分具备Javascript的能力,我相信大家应该都看到过官网的这句话,JSX是Javascript的语法扩展这个没有什么异议,但是充分具备Javascript的能力是怎么体现出来的呢?
我们把上面的代码输入到Babel编辑器中:
React17版本
React17以下版本(16,15等等)
可以清楚的看到,我们的div标签和p标签都被转为了_jsx调用。JSX语法看起来很像我们之前写的HTML标签套了模板语法的样子,但是只是看起来像罢了,在React17之前JSX 的本质是React.createElement这个 JavaScript 调用的语法糖,但是在React17之后_jsx替换了React.createElement。
从此我们也不再需要手动书写import React from 'react',react/jsx-runtime中的JSX新的解析器将取代原来
React.createElement的工作,这将极大地减低了初学者的学习成本,入门React的时候经常会因为忘记引入React而导致报错懵逼,每次都要在头部引入一次React这不得不令人烦恼,自从React17以后再也不用了,React越来越人性化了
二、生命周期
React生命周期可以说是面试中的热门话题,很少有面试官不问这个的。要说React生命周期还要从React15的生命周期说起,此篇只是说说生命周期存在的意义和流程,不会带你反复咀嚼生命周期的具体使用方法和实践
React15.x生命周期
在 React 15 中,我们关注的生命周期主要是以下几个:
//构造器
constructor()
//初始化渲染组件(组件挂载之前)
componentWillMount()
//初始化渲染组件(组件挂载之前)
componentDidMount()
//父组件修改组件的props时会调用
componentWillReceiveProps()
//更新拦截器(个人理解)
shouldComponentUpdate()
//组件更新时调用
componentWillUpdate()
//组件更新后调用
componentDidUpdate()
//渲染
render()
//组件卸载时调用
componentWillUnmount()
组件的声明周期一共经历3个阶段,挂载、更新、卸载,下面我们详细展开说明
Mounting阶段 组件初始化挂载
constructor我们通常都干些什么呢,一般是把state放在这里,还有一些需要延迟加载的方法之类的:
constructor(props) {
super(props)
this.state = {
text: "这是React文本内容"
}
}
不过我们没有特殊情况的时候通常省略constructor的写法:
state = {
text: "这是React文本内容"
}
componentWillMount、componentDidMount这两个方法在挂载阶段只调用一次,componentWillMount方法会在render之前调用,通常我们不在componentWillMount里面处理逻辑(比较鸡肋),因为componentWillMount能做的事情componentDidMount都能做。componentDidMount方法通常用来请求数据和做一些页面数据初始化。
render这里并不会操作真实的Dom,而是将需要渲染的内容返回出来,Dom的操作是有ReactDom.render来完成的
Updating阶段:组件的更新与渲染
组件更新有两种情况,一种是组件本身调用setState触发的更新,一种是父组件传入props更新导致的更新,
流程示意图如下:
组件更新流程由父组件触发的时候会多出一个componentWillReceiveProps生命周期方法:
componentWillReceiveProps(nextProps)
此方法传入一个最新的props用来对比旧的props以此来处理一些根据更新状态修改数据之类的操作。
注意点 :如果父组件更新导致组件重新渲染,即使props没有改变以后调用此方法,componentReceiveProps并不是由props变化而触发的而是由父组件更新而触发的。
shouldComponentUpdate方法是用于处理是否进行render的方法,默认是返回true:
shouldComponentUpdate(nextProps,nextState){
return true
}
可在此生命周期里面处理组件是否更新的性能优化逻辑操作,具体详情请见React性能优化
componentWillUpdate方法是在组件更新之时,render之前调用:
componentWillUpdate(nextProps, nextState) {
console.log("componentWillUpdate方法执行");
}
componentDidUpdate方法是在组件更新之后调用,通常用于处理更新之后的Dom操作,数据修改等等,componentDidUpdate执行完毕之后,标志着更新阶段彻底结束。
componentDidUpdate(nextProps, nextState)
Unmounting阶段 : 组件的卸载
Unmounting阶段只有一个生命周期componentWillUnmount,当组件被销毁时调用,通常用于清除组件数据,清除储存在Redux models里的state数据,页面表单数据等。
React16.4生命周期
上面回顾了一下React15生命周期的流程,下面来详细讲讲React16的变化与革新:图片引自开源项目
我们可以明细的看出React16.4声明周期对比React15少了以下三个方法:
//初始化渲染组件(组件挂载之前)
componentWillMount()
//父组件修改组件的props时会调用
componentWillReceiveProps()
//组件更新时调用
componentWillUpdate()
多了以下2个生命周期方法:
// 初始化/更新时调用
static getDerivedStateFromProps(props, state) {
console.log("getDerivedStateFromProps方法执行");
return null
}
// 组件更新时调用
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("getSnapshotBeforeUpdate方法执行");
return null
}
Mounting阶段 组件挂载/渲染
从大图我们不难看出官方放弃了componentWillMount和componentWillReceiveProps,新增了getDerivedStateFromProps,此时可能有人会认为官方是想拿getDerivedStateFromProps来替代componentWillMount和componentWillReceiveProps,其实不然。
1 .getDerivedStateFromProps这个API是一个静态方法,它的唯一用处就是用来使用props派生或者更新state,我们并不能在方法内部或者到this,所以也就不能进行this.setState等操作,在一定程度上规避了一些劣质代码的产生。
关于getDerivedStateFromProps,官方给过说明:
与 componentDidUpdate 一起,这个新的生命周期涵盖过时componentWillReceiveProps 的所有用例
----React官网
componentWillReceiveProps被放弃,其实是React官方出于综合考虑:
- componentWillReceiveProps这个方法里滥用setState容易导致死循环
- componentWillReceiveProps与要更新的Fiber架构理念不合,Fiber架构具备可打断,任务拆分的特性,而componentWillReceiveProps处于render阶段,在Fiber架构的三个阶段中render阶段是可以随时打断暂停重启的,如果重启更新,又将进入componentWillReceiveProps, 已经处理过得ths.setState.XXX + 1,是不是又要重新运行一次,这将带来非常严重的BUG。
2 .getDerivedStateFromProps这个方法需要一个对象格式返回值,如果你不需要处理state的话,最好不要写这个方法或者可以返回一个null,否则会报错。getDerivedStateFromProps接收2个参数父组件的props, 当前组件的state,getDerivedStateFromProps方法更新state并不是覆盖,而是跟setSate一样定向更新,比如:
state = {
text:"我今年16岁",
name:'小明'
}
// 初始化/更新时调用
static getDerivedStateFromProps(props, state) {
return {
text:"我今年36岁了"
}
}
Updating阶段 :组件更新/渲染
getSnapshotBeforeUpdate这个方法与getDerivedStateFromProps有点类似,代码如下所示:
// 组件更新时调用
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("getSnapshotBeforeUpdate方法执行");
return null;
}
都是返回一个值,但是getSnapshotBeforeUpdate的返回值作为componentDidUpdate第三个参数传进去的,
它的执行时机是在render方法执行之后,真实DOM修改之前,在这个方法里可以拿到更新之后的props/state和当前的props/state,方便我们对比前后更新差异来处理一些特殊的需求场景。比如在首页做一个可视化大屏,
从后端请求数据回来,此时子组件就可以根据当前的state数据来对比前后数据差异,来判断是否更新数据
Unmounting阶段 : 组件的卸载
这个阶段与React15生命周期一致这里就不重复描述了
React-Hooks
1.函数组件会捕获 render 内部的状态,这是两类组件最大的不同。
2.Hooks 的本质:一套能够使函数组件更强大、更灵活的“钩子”,钩子是什么,就是需要什么就钩进来什么,不需要的就不引入,这极大的减低了React的学习成本,不再需要关注复杂的this,和难以理解的class 庞杂的生命周期
3.随着React16更新Fiber架构,优化diff算法,Fiber架构的可打断,任务拆分特性使React项目的性能大幅度提升,告别了以往的diff一算到底导致页面卡死的尴尬处境.
4.技术革新:React17又更新了一些特性
- 新的 JSX 转换逻辑 React17中不需要再每个组件的头部引入import React from 'react'了原因是原来版本中 React 中对 JSX 代码的转换依赖的是 React.createElement 这个函数,而React17以后JSX 解析器将取代 React.createElement 完成 JSX 的编译工作,这极大地减低了开发者的学习成本,因为你不需要再因为忘记引入React报错而烦恼了..新手往往更加懵逼
- 事件系统重构.放弃利用 document 来做事件的中心化管控, 17版本之前,React 会通过将所有事件冒泡到 document 来实现对事件的中心化管控,这将导致开发者相阻止冒泡的时候困难重重,很多时候只能阻止它在React合成事件中的冒泡,并不能阻止真实的DOM冒泡,因此可能会导致一些bug,17版本改变了这一机制,将事件系统管控改而转移到了React组件自己的容器节点上去了,,比如我们初始挂载在APP.jsx中的root节点