"一觉醒来又有BUG!"—— 大家好!我是周一觉
还在为React庞大的知识体系而苦恼吗?
还在为学习React不知道从何入手而焦虑吗?
还在为...算了,废话不多说,从这篇React基础知识&脑图开始,我们一起学起来吧!
React基础知识
开始学习之前我会默认您
1.会使用npm安装依赖
2.掌握原生Javascript并有一定的编程基础
3.对React有一定的了解
一、脚手架的选择
(1)create-react-app(官方脚手架,默认webpack打包规则)
(2)Vite+React(尤雨溪团队基于rollup开发的打包工具)
(3)其他......
二、MVC是什么
MVC分别是模型(Model)、视图(View)和控制器(Controller),是一种单向驱动模式。模型表示应用程序中的数据和业务逻辑,视图表示用户界面,控制器则负责处理用户输入并更新模型和视图。MVC 的主要优点是分离关注点,使代码更易于维护和修改。
扩展:MVC和MVVM的区别
三、JSX
1.JSX是什么?
JSX(JavaScript XML)是一种JavaScript的扩展语法,它允许开发者使用类似于XML的语法编写JavaScript代码。在React中,JSX被用作声明式UI的编写方式。
2.JSX渲染机制
第一步:把我们编写的JSX语法,编译为虚拟DOM对象「virtualDOM」
第二步:把构建的virtualDOM渲染为真实DOM
注意:第一次渲染页面是直接从virtualDOM->真实DOM。但是后期视图更新的时候,需要经过一个DOM-DIFF的对比,计算出补丁包PATCH(两次视图差异的部分),把PATCH补丁包进行渲染!!
扩展:React中的DOM-DIFF
四、事件委托
React17以前
委托给document容器,而且只做了冒泡阶段的委托可以通过设置
capture:true
属性来启用捕获阶段的委托
React17及以后
委托给#root容器,捕获和冒泡都做了委托对于没有实现事件传播机制的事件
才是单独做的事件绑定例如onMouseEnter/onMouseLeave
五、组件
1.类组件(动态组件)
(1)class语法(有this)
主要知识点
1.super 等于 react.component.call(this,...)
2.默认有个state属性挂载到实例上
3.基于render渲染
(2)生命周期函数
第一次渲染(执行过程)
componentWillMount => render => componentDidMount
组件更新(执行过程)
- shouldComponentUpdate
- componentWillUpdate
- 修改状态值/属性值「让this.state.xxx改为最新的值」
- render
- componentDidUpdate
若是this.forceUpdate()
强制更新,则会跳过should直接从will开始,若是父组件更新触发子组件更新,则子组件内部最开始会多一步componentWillReceiveProps,剩余过程一样
组件卸载(执行过程)
componentWillUnmount
(3)视图更新(修改状态)
this.setState(partialState)
既可以修改状态,也可以让视图更新
- this.setState的第一个参数,可以是一个函数,这个函数的第一个参数是存储之前的状态值prevState,返回的对象是我们想要修改的新状态值,可以实现循环多次只渲染最后一次使用prevState来避免出现不一致或不可预测的状态
- this.setState的第二个参数,是一个回调函数callback,在状态更改/视图更新完毕后触发执行,也可以说只要执行了setState,callback一定会执行。
若我们基于shouldComponentUpdate阻止了状态/视图的更新,componentDidUpdate周期函数肯定不会执行了,但是我们设置的这个callback回调函数依然会被触发执行!!
扩展:React中setState是同步还是异步?
this.forceUpdate()
强制更新
当组件的状态或属性更改时,React 会自动重新渲染组件。 但是有时需要强制重新渲染组件,即使它们的状态或属性没有更改,这时可以使用 forceUpdate( )方法。
注意:它会跳过 React 的优化和性能优化
(4)性能优化
PureComponent
PureComponent和Component的区别是
PureComponent会给类组件默认加一个shouldComponentUpdate周期函数
对新老属性做一个浅比较,经过比较后若发生改变则更新,否则不更新
2.函数组件(静态组件)
配合Hooks时为动态组件
(1)直接创建函数(无this)
(2)Hooks函数(大于等于React16.8版本)
useState
(在函数组件中使用状态,并且后期基于状态的修改,可以让组件更新)
useState自带了性能优化,修改状态会和之前做比较,类似于PureComponent,参数prev储存上一次的状态值,return返回的信息是我们要修改的状态值
useEffect
(在函数组件中,使用生命周期函数)
- useEffect没设置依赖
第一次渲染执行callback//=>componentDidMount
组件每一次更新完毕执行callback//=>componentDidUpdate - useEffect(callback,[])设置依赖,但是无依赖
只有第一次渲染完毕后,才会执行callback,
每一次视图更新完毕后,callback不再执行//=>componentDidMount - useEffect(callback,[xxx,xxx,...])设置依赖,有依赖
第一次渲染和任意依赖值改变时,都会执行callback
组件更新时若依赖无变化,则不会执行callback
callback的return的返回值(小函数),会在组件释放时执行
理解为组件发生更新后,执行上一次的return
useLayoutEffect
(实现一些需要在 DOM 更新前执行的操作)
他会在 DOM 更新前同步执行,而 useEffect 则是在渲染完成之后异步执行,尽量避免在 useLayoutEffect中修改DOM,可能导致浏览器重绘、闪烁等问题。
useMemo
(用于对函数式组件进行性能优化,依赖改变则重新执行)
他的callback是计算函数, 它可以缓存函数的计算结果,并在下一次调用时直接返回缓存结果从而避免重复计算由于 useMemo 的作用是优化性能,因此应该尽量避免在计算函数中进行副作用操作,比如修改组件状态或执行网络请求等操作
useCallBack
(用于对函数式组件进行性能优化,依赖改变则重新执行)
他的callback是回调函数,若依赖无变动则不会开辟堆内存,以便提高性能。
父组件嵌套子组件时,父组件要把一个内部的函数, 基于属性传递给子组件时,此时传递的这个方法,用useCallback处理一下会更好。
useImperativeHandle
(父组件使用子组件的方法)
第一个参数ref用于引用子组件
第二个参数createHandle是一个回调函数,用于创建一个对象,包含了父组件需要暴露给子组件的方法。
createHandle它指向子组件的实例对象,你可以在这个函数中访问子组件的属性和方法 并返回一个对象,该对象上的方法将被暴露给父组件。
useRef
(创建一个ref对象)
下面会具体讲ref相关内容
其他。。。(以上是最常用的Hooks函数)
(3)自定义Hook
自定义hook是一种将状态逻辑封装为可重用函数的方式。
可以帮助开发人员在组件之间共享状态逻辑,从而避免代码重复
要创建自定义hook,只需要编写一个函数,该函数以“use”开头,然后在函数内部编写状态逻辑。通常自定义hook使用React的状态钩子 useState
来管理状态
(4)性能优化
React.memo
:在纯函数组件中使用 相当于类组件的PureComponentuseMemo/useCallBack
3.组件调用
- 单闭合调用
<Component/>
- 双闭合调用
<Component> ... </Component>
4.flushsync方法
flushSync()
函数用于同步 React 中的异步更新
在 React 中,当状态或属性发生更改时,React 会异步地重新渲染组件并更新 DOM。
如果需要确保 DOM 中的所有更改都已应用,可以使用 flushSync() 函数来同步这些异步更新
六、Ref
给元素标签设置ref目的是获取对应的DOM元素
1.类组件中使用ref
(1)<h2 ref="titleBox">...</h2>
此方法不推荐使用因为在React.StrictMode模式下会报错
获取:this.refs.titleBox
(2)<h2 ref={x=>this.titleBox=x}>...</h2>
获取:this.titleBox
(3)this.titleBox=React.createRef(); <h2 ref={this.titleBox}>...</h2>
获取:this.titleBox.current
(4)赋值给一个函数子组件会直接报错
解决方案
通过React.forwardRef
实现ref的转发,配合useImperativeHandle
来达到获取函数子组件内部的某个元素的目的
扩展:React中的高阶组件
2.函数组件中使用ref
(1)<h2 ref={x=>titleBox=x}>...</h2>
获取:titleBox
(1)const titleBox = React.createRef(); <h2 ref={titleBox}>...</h2>
获取:titleBox.current
(1)const titleBox = useRef(); <h2 ref={titleBox}>...</h2>
获取:titleBox.current
3.小结
React.creatRef
:类组件、函数组件
React.useRef
:函数组件
在函数组件中使用React.creatRef会浪费性能,因为组件每一次更新都会创造一个全新的 ref 对象,使用React.useRef不会,因为只是函数重新执行。
七、复合组件通信
一、props方案
可以通过将属性传递给子组件来实现通信
- 类子组件:在render中,通过
this.props
解构获取属性 - 函数子组件:在函数内部,基于形参
props
中解构获取属性
二、Context方案
React的creatContext
方法可以让组件在不需要逐层传递props的情况下访问全局数据,通过创建一个Context对象,可以将其传递给整个组件树。任何一个组件都可以订阅这个Context从而访问其中的数据。
(1)类组件中使用Context
- 创建上下文对象
ThemeContext=React.creatContext()
- 在祖先组件render的return中渲染的组件用
<ThemeContext.Provider>
包起来 ThemeContext.Provider中的value={{ 存放传递的数据和状态 }} - 后代使用
①先导入上下文对象ThemeContext
②static contextType=ThemeContext
之后在render中通过this.context解构来获取之后使用
③.在render的return中渲染的组件{context=>{从context中解构来获取,return返回渲染组件 }}
用<ThemeContext.Consumer>
组件包起来使用
(2)函数组件中使用Context
- 创建上下文对象
ThemeContext=React.creatContext()
- 在祖先组件render的return中渲染的组件用
<ThemeContext.Provider>
包起来 ThemeContext.Provider中的value={{ 存放传递的数据和状态 }} - 后代使用
①先导入上下文对象ThemeContext
②在return中渲染的组件{context=>{ 从context中解构来获取,return返回渲染组件} }
用<ThemeContext.Consumer>
组件包起来使用
③ 从react中解构出useContext
这个hook,通过const {xx,xx} = useContext(ThemeContxt)
来解构获取,最后在return的渲染元素中直接使用即可
三、ref方案(详情见上面 ref 相关内容)
补充脑图↓↓
- 脚手架的选择
- MVC是什么
- JSX是什么
- React中的事件委托
- React中的组件
- React中的ref
- 复合组件通信方案
这七方面是React中最基础的部分,正所谓万丈高楼平地起,大家一定要吃透这张脑图,后续我会慢慢给本文补充代码来方便大家理解。关于文中扩展部分大家可以自己思考一下,有时间我会单独写一篇关于此部分的文章。
写在最后:本文花费你觉(作者)极大的心思和时间,希望能对大家有所帮助!
PS:这是你觉(作者)第一次发表文章,不足之处还望多多包涵
PS:React全家桶 —— 干中之干系列,我会一点一点来更新,敬请期待
PS:评论区欢迎大家留言讨论
PS:最后点个关注,谢谢各位啦!哈哈哈哈~(周星驰笑)!
作者VX:IBeancurd 有问题可以随时与我联系
下期预告:“干中之干2 —— React路由知识!!”