挑战坚持学习1024天——前端之JavaScript高级
js基础部分可到我文章专栏去看 ---点击这里
Day91-Day97【2022年11月7日-13日】
学习重点setState原理-性能优化-ref-受控组件、非受控组件-高阶组件-Fragment-过渡动画
1. 非父子组件的通信有哪些方式?分别是什么作用?
-
Context
-
1.创建Context
-
2.使用<context.Provider>包裹后代组件
-
3.在要使用的后代xxxx组件引入context
- xxxx.contextType = context
- 在render方法中可以通过this.context拿到传递过来的值
-
-
事件总线EventBus
- Redux
2. 面试题:React的setState是同步的还是异步的?React18中是怎么样的?
-
在 React 中,可变状态通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新
-
React的setState是异步的 -- 不要指望在调用
setState之后,this.state会立即映射为新的值 -
在react18之前, 在setTimeout,Promise等中操作setState, 是同步操作,在生命周期事件函数中是异步(批处理)
-
在react18之后, 在setTimeout,Promise等中操作setState,是异步操作(批处理)
- 如果需要同步的处理怎么办呢? 需要执行特殊的
flushSync操作
- 如果需要同步的处理怎么办呢? 需要执行特殊的
-
为什么要将setState设计成异步的
- 首先,若是将setState设计成同步的,在
componentDidMount中请求多个网络请求时,会堵塞后面的网络请求
componentDidMount() { // 网络请求一 : this.setState // 网络请求二 : this.setState // 网络请求三 : this.setState // 如果this.setState设计成同步的,会堵塞后面的网络请求 }-
一. setState设计为异步,可以显著的提升性能
- 如果每次调用 setState都进行一次更新,那么意味着render函数会被频繁调用,界面重新渲染,这样效率是很低的
- 最好的办法应该是获取到多个更新,之后进行批量更新
// 在一个函数中有多个setState时, this.setState({}) --> 先不会更新,而是会加入到队列(queue)中 (先进先出) this.setState({}) --> 也加入到队列中 this.setState({}) --> 也加入到队列中 // 这里的三个setState会被合并到队列中去 // 在源码内部是通过do...while从队列中取出依次执行的 -
二: 如果同步更新了state,但是还没有执行render函数,那么state和props不能保持同步
- 首先,若是将setState设计成同步的,在
3. 性能优化:什么是SCU优化?类组件和函数组件分别如何进行SCU的优化?
-
shouldComponentUpdate -- SCU -- React提供给我们的声明周期方法
-
SCU优化就是 一种巧妙的技术,用来减少DOM操作次数,具体为当React元素没有更新时,不会去调用render()方法
-
可以通过
shouldComponentUpdate来判断this.state中的值是否改变shouldComponentUpdate(nextProps, nextState) { const {message, counter} = this.state if(message !== nextState.message || counter !== nextState.counter) { return true } return false } -
React已经帮我们提供好SCU优化的操作
- 类组件: 将class继承自
PureComponent - 函数组件: 使用一个高阶组件
memo
import {mome} from 'react' const HomeFunc = mome(function(props) { console.log("函数式组件") return ( <h4>函数式组件: {props.message}</h4> ) }) export default HomeFunc - 类组件: 将class继承自
4. React为什么要强调不可变的力量?如何实现不可变的力量?
-
不可变的力量: 不要直接去修改this.state中的值(主要指对象),若是想修改的话,应该是将这整个值全部修改掉
- 注意: 值类型,在修改的时候,本身就全部替换掉了,所以不需要其他操作,直接改就可以
-
实现: 将对象浅拷贝赋值给一个新的变量,在将这个新的变量赋值给this.state中的值
5. React中获取DOM的方式有哪些?
-
ref获取DOM
getDOM() { // 方式一: 在react元素上绑定ref字符串 - 这种方式react已经不推荐了 // console.log(this.refs.http) // 方式二: 提前创建好ref对象, createRef(), 将创建出来的对象绑定到元素(推荐) // console.log(this.titleRef.current) // 方式三: 传入一个回调函数, 在对应的元素被渲染之后, 回调函数被执行, 并且将元素传入(16.3之前的版本) // console.log(this.titleEl) } <h3 ref="http">大大怪将军</h3> <h3 ref={this.titleRef}>小小怪下士</h3> <h3 ref={el => this.titleEl = el}>瑞克</h3> <button onClick={() => this.getDOM()}>获取DOM</button> -
ref获取组件实例 --
createRefimport React, { PureComponent, createRef } from 'react' constructor() { super() this.state = {} this.HWRef = createRef() } getComponent() { console.log(this.HWRef.current) this.HWRef.current.test() } <HelloWorld ref={this.HWRef} /> <button onClick={() => this.getComponent()}>获取组件实例</button> -
ref获取函数组件 -- 函数式组件是没有实例的,所以无法通过ref获取他们的实例 --
React.forwardRefimport React, { PureComponent, createRef, forwardRef } from 'react' const HelloWorld = forwardRef(function(props, ref) { return ( <div> <h2 ref={ref}>函数组件</h2> <h4>大大怪将军</h4> </div> ) }) constructor() { super() this.state = {} this.HWRef = createRef() } getComponent() { console.log(this.HWRef.current) } render() { return ( <div> <HelloWorld ref={this.HWRef} /> <button onClick={() => this.getComponent()}>获取DOM</button> </div> ) }
6. 性能优化:React的diff算法和key的作用
-
React的渲染流程
- 在render函数中返回jsx, jsx会创建出
ReactElement对象(通过React.createElement的函数创建出来的) ReactElement最终会形成一颗树结构, 这颗树结构就是vDOM- React会根据这样的vDOM渲染出真实DOM
- 在render函数中返回jsx, jsx会创建出
-
React更新流程
- props/state发生改变
- render函数重新执行
- 产生新的DOM树结构
- 新旧DOM树 进行diff算法
- 计算出差异进行更新
- 最后更新到真实DOM
什么是diff算法? diff算法并非React独家首创,但是React针对diff算法做了自己的优化,使得时间复杂度优化成了O(n) 对比两颗树结构,然后帮助我们计算出vDOM中真正变化的部分,并只针对该部分进行实际的DOM操作,而非渲染整个页面,从而保证了每次操作后页面的高效渲染。 -
React在props或state发生改变时,会调用React的render方法,会创建一颗不同的树
-
React需要基于这两颗不同的树之间的差别来判断如何有效的更新UI
-
如果一棵树参考另外一棵树进行完全比较更新,那么即使是最先进的算法,该算法的复杂程度为 O(n³) ,其中 n 是树中元素的数量
-
如果在 React 中使用了该算法,那么展示 1000 个元素所需要执行的计算量将在十亿的量级范围
-
这个开销太过昂贵了,于是,React对这个算法进行了优化,将其优化成了O(n)
- 同层节点之间相互比较,不会垮节点比较(一旦某个节点不同,那么包括其后代节点都会被替换)
- 不同类型的节点,产生不同的树结构(当根节点为不同类型的元素时,React 会拆卸原有的树并且建立起新的树)
- 开发中,可以通过key属性标识哪些子元素在不同的渲染中可能是不变的
-
在遍历列表时,总是会提示一个警告,让我们加入一个key属性,当子元素拥有 key 时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素。
- 在最后位置插入数据 -- 这种情况,有无key意义并不大
- 在前面插入数据 -- 这种做法,在没有key的情况下,所有的li都需要进行修改
- 在中间插入元素 -- 新增2014, key为2016元素仅仅进行位移,不需要进行任何的修改
<ul> <li key="2015">Duke</li> <li key="2016">Villanova</li> </ul> <ul> <li key="2015">Duke</li> <li key="2014">Connecticut</li> <li key="2016">Villanova</li> </ul>
7. 什么是受控组件和非受控组件,如何使用?
-
受控组件
- 在 React 中,可变状态通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新
- 我们将两者结合起来,使React的state成为“唯一数据源”
- 渲染表单的 React 组件还控制着用户输入过程中表单发生的操作
- 被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”
this.state = { message: "" } changeInput(event) { console.log(event.target.value) this.setState({ message: event.target.value }) } render(){ <input type="text" value={message} onChange={(event) => this.changeInput(event)} /> } -
非受控组件
- 在受控组件中,表单数据是由 React 组件来管理的
- 非受控组件中,表单数据将交由 DOM 节点来处理
this.messageRef.current.value // 在非受控组件中通常使用defaultValue来设置默认值 render(){ <input type="text" defaultValue={message} ref={this.messageRef} /> }
8. 什么是高阶组件?高阶组件在React开发中起到什么作用?
-
高阶函数: (满足一下调教之一) -- filter、map、reduce都是高阶函数
- 接受一个或多个函数作为输入
- 输出一个函数
-
高阶组件: Higher-Order Components,简称为 HOC
- 高阶组件是参数为组件,返回值为新组件的函数 -- 就是传入一个组件,对这个组件进行一些功能的增强,在返回出来新的组件
- 注意: 首先 高阶组件 本身不是一个组件,而是一个函数 其次,这个函数的参数是一个组件,返回值也是一个组件
- HOC 是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式
-
高级组件应用的场景
- props的增强
- 利用高阶组件来共享Context
- 渲染判断鉴权
- 生命周期劫持
- ....
9. 什么是Fragment,有什么作用?
- Fragment 允许将子列表分组,而无需向 DOM 添加额外节点;
- 它的简写看起来像空标签 <> </>
- 如果我们需要在Fragment中添加key,那么就不能使用短语法
10. 什么是React的严格模式,在开发中有什么作用?
严格模式
- StrictMode 是一个用来突出显示应用程序中潜在问题的工具:
- 与 Fragment 一样,StrictMode 不会渲染任何可见的 UI;
- 它为其后代元素触发额外的检查和警告;
严格模式作用
- 识别不安全的生命周期:
- 使用过时的ref API
- 检查意外的副作用 这个组件的constructor会被调用两次; 这是严格模式下故意进行的操作,让你来查看在这里写的一些逻辑代码被调用多次时,是否会产生一些副作用; 在生产环境中,是不会被调用两次的;
- 使用废弃的findDOMNode方法 在之前的React API中,可以通过findDOMNode来获取DOM,不过已经不推荐使用了,可以自行学习演练一下
- 检测过时的context API 早期的Context是通过static属性声明Context对象属性,通过getChildContext返回Context对象等方式来使用Context的
11. React中如何实现过渡动画?常见的过渡动画组件有哪些?
- React社区为我们提供了react-transition-group用来完成过渡动画
常见的过渡动画组件
- Transition 在前端开发中,一般是结合CSS来完成样式,所以比较常用的是CSSTransition;
- CSSTransition 在前端开发中,通常使用CSSTransition来完成过渡动画效果
- SwitchTransition 两个组件显示和隐藏切换时,使用该组件
- TransitionGroup 将多个动画组件包裹在其中,一般用于列表中元素的动画;
精进
11.8精进:多接触比你层次高的人,远离添麻烦的人,远离心理灰暗的人。
11.9精进
思维方式比工作技能更重要,技能随着时间的流逝可通过很多方式而掌握,而思维方式的形成可以让你更清楚什么时候该做什么样的事情,做事情应该遵循什么样的原则。这是更为重要的,工作很忙的时候一定不要开心自己又进步了,而是停下来思考一下,自己是不是可以更高效一点。
11.10精进
真正让自己成长的,永远是那些让你害怕,逃避,疼痛的事情。
11.11精进
费曼学习法基本步骤
1.确定目标
2.模拟教学
3.反馈
4.简化并重复
与《礼记·学记》中“学,然后知不足,教,然后知困”基本原理非常相似,可见成功的方法最终指同一方向,万物归一。
11.13精进
工作生活中的细节和琐碎,是过不去的坎,得不断总结经验教训,不断优化自己的行动。
参考资料
- Coderwhy学习资料
- React官网
- 解锁前端面试体系核心攻略
- 鲨鱼哥整理面试资料
- 其他参考资料 结语
志同道合的小伙伴可以加我,一起交流进步,我们坚持每日精进(互相监督思考学习,如果坚持不下来我可以监督你)。我们一起努力鸭! ——>点击这里
备注
按照时间顺序倒叙排列,完结后按时间顺序正序排列方便查看知识点,工作日更新。