持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
什么是React?
React是一个网页UI框架
JQuery诞生于2005年,但限于浏览器兼容
Angular太复杂,双向绑定是最大的特色
React为什么要用JSX?
一句话解释JSX:JSX是一个js的语法扩展
核心概念:可以不用jsx,原理都是用React.CreateElement,最终都会被babel转化
React.CreateElement,但是jsx天生具有可读性,对于开发者而言,jsx是React.CreateElement的语法糖
对比其他形式:
模板(类似Angular,引入概念多)
模板字符串(结构复杂)
JSON(语法提示差)
如何避免生命周期中的坑?
挂载 -> 更新-> 卸载 才是生命周期,其他的只是生命周期的函数
挂载阶段
constructor
过去通常用于初始化数据
不推荐做初始化以外的操作
不属于生命周期,只是class的初始化函数
constructor移除之后,代码也更加简洁
getDerivedStateFromProps
作用是使组件在props变化时更新state
触发时机:当props被传入时,当state变化时,forceUpdate被调用时
UNSAFE-componentWillMount
用于加载前做某些操作,目前被弃用因为在React异步渲染机制下,改方法可能被多次调用
render
render是一个纯函数,返回jsx结构,用于描述具体内容,不能在这里调用setState,否则会进入死循环,因为state的改变会重新调用render函数
componentDidMount
主要用于组件加载完的操作,一般用于网络请求,或者绑定事件
更新阶段
getDerivedStateFromProps
跟挂载阶段的作用一样
shouldComponentUpDate
返回true和false,性能优化的关键场所,避免不必要的渲染,做比较值有没有发生变化去判断要不要更新
getSnapshotBeforeUpdate
该方法是根据新的渲染机制出现的,在DOM发生更新前调用,返回值作为componentDidMount的第三个参数
卸载阶段
componentWillUNmount
主要用于执行清理工作
比较常见的问题就是忘记清理定时器,一定要取消绑定,清理定时器
会发生重新渲染的情况
-
函数组件
任何情况都会重新渲染,没有生命周期,官方提供React.memo优化手段
-
React.Component
- 当state发生变化时
- 当父级组件传入的Props传入时
-
React.PureComponent 默认实现了shouldComponentUpdate函数 仅在props和state进行浅比较后,确认有变化再改变
渲染时的报错,只能通过componentDidCatch捕获
函数组件和类组件的区别?
相同点
最终呈现效果是一样的
不同点
类组件是面向对象编程,有继承,有属性,有内部状态管理
函数组件是函数式编程,结构化编程,有输入输出
函数组件更简单,纯粹,好测试。
其次就是this的指向。
业务逻辑散落在生命周期中,未来趋势也是函数式组件
类组件通过shouldComponentUpDate去阻断渲染
函数组件靠React.memo来优化
setState是同步还是异步?
合成事件:加入一个ul下面有10000个li,那么原生的添加事件就要10000个onClick事件,这就很不友好,所以react有一个事件委托机制,让他在ul上面写监听事件,然后判断是哪个li触发的事件
通常情况下setState看起来像是异步的
是否觉得react的SetState执行像一个队列
React根据队列逐一执行,合并state数据完成后执行回调,根据结果更新虚拟DOM触发渲染,根据队列逐一执行,合并状态数据完成后执行回调,根据结果更新虚拟DOM触发渲染
通过异步的操作方式,累计更新后批量处理,减少渲染次数,提升性能。
有一个控制点,isBatchingUpdates,在React的声明周期事件和合成事件中,可以拿到isBatchingUpdates的控制权,把isBatchingUpdates的值变为true,将状态放进队列之中,控制执行节奏,但是在原生事件中,如setTimeout,addEventListen,setIinterval中,不能拿到isBatchingUpdates的控制点,不走合并操作,所以还是为false,就执行同步更新。
一般情况为异步的目的就是优化,减少渲染,但是react官方团队补充了两点,第一点是为了保持内部的统一性,因为props不是同步更新,第二点是启用并发更新完成异步渲染。
更新队列是核心关键点。
虚拟dom的工作原理是什么?
为什么要用虚拟dom?
起源是facebook简化前端开发,以及避免xss攻击。
react.createElement返回的结果应该是一个Object,有一个child
react有两个函数,一个是diff,一个是渲染函数,这也是为什么要引入两个库,react引入的是diff函数,react-dom引入的是渲染函数。react主要工作是组件实现、更新调度,reactDOM提供了网页渲染的基础。
虚拟dom的优势,性能优越,避免xss,可跨平台(只要加上react-native提供native层元素就可以)
虚拟dom比真实dom慢,但是直接操作大量虚拟dom会页面性能下降,所以基于虚拟dom和diff处理可以降低dom操作的范围,提高性能。首次和微量的时候虚拟dom是比较慢的。虚拟dom内部确保字符转义,但是也不是百分比安全,因为有一个api跳过了转义。
也不是没有虚拟dom就不可以跨平台,nativeScript没有虚拟dom层,他是通过提供兼容原生api的js api实现跨平台,但是虚拟dom跨平台的成本更低。
缺点就是虚拟dom占用比较高。
react的虚拟算法有何不同?
更新之后生成一个patch,根据patch操作真实dom。
遍历算法 react采用的是深度优先遍历,因为广度优先遍历可能导致生命周期错乱。
优化策略 传统的diff算法时间复杂度是n3,react是n,采用的方法是分治,分而治之。
策略一:忽略跨层级比对,提高比对效率,同一层节点比较,如果不存在直接不比较了
策略二:如果组件的class一致,则默认相似结构,否则不相似
策略三:同一层级子节点,通过key来比较
react16新增的fiber
Fiber机制下节点与树分别采用FiberNode 与 FiberTree进行重构
FiberNode采用双链表结构,可以直接找到兄弟节点和子节点(这也是为什么可以随时更新和暂停),FiberTree是通过FiberNode构建的树
Fiber 机制下整个更新过程由current 与 worklnProgress两株树
双缓冲完成,worklnProgress更新完之后,直接改变current的指针,直接抛弃之前的树。
vue3前期也引入了事件切片,后来因为效果不明显删除了
如何解释react的渲染流程?
事务 不可再分 事务通过调度
stack reconciler
react15 渲染的整体策略是递归 并通过事务建立react与虚拟dom的联系并完成递归
fiber reconciler
fiber指一种最轻量化的线程,协调调度
协作式多任务模式,基于循环遍历计算diff
react的渲染异常会造成什么后果?
如何分析和调优性能瓶颈?
FCP
首次绘制内容的耗时
TTI
页面可以交互的时间(骨架屏) 懒加载和异步加载实现,让用户先用起来
核心内容:在react中同步加载
非核心内容:采取异步加载的方式延迟加载
内容中的图片:采用懒加载的方式避免占用网络资源
Page Load
页面完整加载时间
也可以通过异步加载的形式完成
异步加载主要由webpack打包common chunk与异步组件的方式完成
FPS 帧率
主要代表卡顿的情况
在react中引起卡顿的主要原因有长列表和重渲染
静态资源
api请求率
对于静态资源来说能用cdn就用cdn,能大幅提升静态资源的成功率
cdn(静态资源加速)
如何避免重复渲染?
TP99是指满足百分之99的请求需要花的最短时间
长列表 虚拟滚动解决
**重复渲染 **还没有比较完美的解决方案
React Hook的使用有什么限制?
为什么不能在if函数里面调用setState? 可能会造成数组的取值错位,所以循环,嵌套不能使用,当然实际上不是数组,是链表。
useEffect和useLayoutEffect有什么区别?
相同点
他们使用的是同一个签名,在源码中他们调用的是同一个函数。这两者都是用于处理副作用,这些副作用包括改变DOM,设置订阅,操作定时器等
不同点
在源码中,useEffect先调用了mountEffect,再调用mountEffectImpl useLayoutEffect先调用mountEffectImgl再调用mountEffect。
既然内部调用的都是同一个函数,为什么会有这样的区别呢?这就要从hooks原理去理解
hooks设计原理
所有的hooks都导入到dispatcher对象中,在调用hooks时,会调用dispatcher对应的hook函数,所有的hook是会按照顺序存入hook队列中,这样调用就知道当前的hook对应哪个fiber了。
标记为HookLayout的effect会在所有的DOM变更之后同步调用,所以可以使用它来读取DOM布局并同步触发重渲染,计算量较大的耗时任务必然会造成阻塞,所以就需根据实际情况酌情考虑。
react官方给出的答案是一般情况就用useEffect,如果出现了闪烁或者dom操作之后导致的页面渲染问题再使用useLayoutEffect。
react的常用工具库
分类:
由第三方的react-app-rewired 库对create-react-app提供拓展能力