react-fiber
js运行在浏览器的渲染主线程中,如果js执行的时间较长,那么页面渲染就需要等待js执行完毕,那样会导致页面长时间不更新,用户会感觉到卡顿。
react16主要做了以下工作:
- 为每个任务增加了优先级,优先级高的任务可以中断低优先级的任务,执行完毕后,再重新执行优先级低的任务;
- 增加了异步任务,调用
requestIdleCallbackapi,等待浏览器空闲时再执行 - dom diff变成了链表,一个dom对应两个fiber,一个链表对应两个队列,这都是为找到被中断的任务,重新执行
fiber把渲染更新过程拆分成多个子任务,每次只做一部分,做完看是否还有剩余时间,如果有,继续执行下一个任务;如果没有,挂起当前任务,将时间控制权交给主线程,当主线程不忙的时候继续执行。即可以中断与恢复,恢复后也可以复用之前的中间状态,并给不同的任务赋予不同的优先级,其中每个任务更新单元为React Element对应的Fiber节点。
首先React中任务切割成多个子任务,分批完成。在完成一部分子任务后,将控制权交给浏览器,让浏览器有时间进行页面的渲染。等浏览器忙完后,如果有空闲时间,再继续之前React没有完成的任务,是一种合作式调度。
该实现过程是基于Fiber节点实现,作为静态的数据结构来说,每个Fiber节点对应一个React element,保存了该组件的类型、对应的DOM节点等信息。
作为独立的工作单元来说,每个Fiber节点保存了本次更新该组件改变的状态、要执行的工作。每个Fiber节点有个对应的React element,多个Fiber节点根据如下三个属性构建一棵树。
// 指向父级Fiber节点
this.return = null
// 指向子Fiber节点
this.child = null
// 指向右边第一个兄弟Fiber节点
this.sibling = null
如何实现react-router
手写React-Router源码 · 前端进阶 (dennisgo.cn)
大文件上传与下载
10.大文件的切片上传和断点续传_哔哩哔哩_bilibili
如何实现useMemo、useCallback
function useMemo(nextCreate, deps) {
const hook = updateWorkInProgressHook()
const nextDeps = deps === undefined ? null : deps
const prevState = hook.memoizedState
if(prevState !== null) {
if(nextDeps !== null) {
const prevDeps = prevState[1]
if(areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0]
}
}
}
const nextValue = nextCrate()
hook.memoizedState = [nextValue, nextDeps]
return nextValue
}
function useCallback(callback, deps) {
const hook = updateWorkInProgressHook()
const nextDeps = deps === undefined ? null : deps
const prevState = hook.memoizedState
if(prevState !== null) {
if(nextDeps !== null) {
const prevDeps = prevState[1]
if(areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0]
}
}
}
hook.memoizedState = [callback, nextDeps]
return callback
}
react生命周期函数
react分为类组件和函数组件,类组件中有生命周期函数,而函数组件中用hooks来模拟生命周期函数
- 类组件
// constructor 在类组件创建实例时调用,而且初始化的时候执行一次。
constructor(){}
// getDerivedStateFromProps 用于初始化和更新阶段,接受父组件的props数据,可以对props进行格式化、过滤等操作,返回值将作为新的State合并到State中,供给视图渲染层消费。返回值与state合并完,可以作为shouldComponentUpdate第二个参数newState。只要组件更新,getDerivedStateFromProps就会执行。是为了取代componentWillMount和componentWillReceiveProps,该生命周期函数内部无法访问到this。
getDerivedStateFromProps(nextProps, prevState)
// componentWillMount 在react18版本中成为了UNSAFE__componentWillMount,在渲染之前执行。
componentWillMount(){}
// componentWillReceiveProps 在react18版本中成为了UNSAFE__componentWillReceiveProps,执行时机在更新组件阶段,该生命周期执行驱动是因为父组件更新带来的props修改,但是只要父组件触发渲染函数,调用React.crateElement方法,props就会被重新创建,生命周期componentWillReceiveProps就执行了。这就解释了即使props没变,该生命周期函数也会执行
componentWillReceiveProps(newProps){}
// componentWillUpdate 意味着在更新之前,此时的DOM还没有更新。 在这里可以做一些获取DOM的操作。比如在一次更新中,保存了DOM之前的信息(记录上一次位置)。
componentWillUpdate()
// getSnapshotbeforeUpdate 意为获取更新前的快照,可以进一步理解为获取更新前DOM的状态。此时是获取DOM信息的最佳时期,getSnapshotBeforeUpdate将返回一个值作为snapshot(快照),传递给componentDidUpdate作为第三个参数。
getSnapshotbeforeUpdate(prevProps, prevState){}
// componentDidUpdate 执行时机是DOM更新之后,可以直接获取DOM最新状态。这个函数里如果想要使用setState,一定要加以限制,否则会引起无限循环。
componentDidUpdate(prevProps, prevState, snapshot){}
// componentDidMount 执行时机和componentDidUpdate一样。一个是初始化,一个是组件更新。此时DOM已经创建完毕,即然DOM已经创建挂在,就可以做一些基于DOM的操作。
componentDidMount(){}
// shouldComponentUpdate 这个生命周期一般用于性能优化,如果返回true,组件会重新渲染,否则组件不会重新渲染。需要关注的是第二个参数newState,如果有getDerivedStateFromProps生命周期,getDerivedStateFromProps的返回值将合并到newState,供shouldComponentUpdate使用。
shouldComponentUpdate(newProps, newState, nextContext){}
// componentWillUnmount 是组件销毁阶段唯一执行的生命周期,主要做一些收尾工作,比如清除一些可能造成内存泄露的定时器、延时器或者一些事件监听器。
componentWillUnmount(){}
- 函数组件
// useInsertionEffect 主要是解决CSS-in-JS在渲染中注入样式的性能问题。执行时机在useLayoutEffect之前,执行时机在DOM更新之前,这时使用CSS-in-js避免了浏览器再次出现重绘和回流的可能,提高了性能
useInsertionEffect
// useLayoutEffect 采用了同步执行,会阻塞页面的渲染。执行时机是在DOM更新之后,浏览器绘制之前,这样可以方便修改DOM,获取DOM信息,这样浏览器只会绘制一次。在执行时机上,componentDidMount/componentDidUpdate和useLayoutEffect更加类似。
useLayoutEffect
// useEffect 是异步调用,不会阻塞页面渲染。执行时机是在视图绘制完毕后。
useEffect
setState是同步还是异步的
首先react18之后,setState是异步的。
- 在react18之前,setState在合成事件和钩子函数中是异步的,在原生事件和延时器、计时器中都是同步的。
- setState在异步中对同一个值进行多次setState,setState的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时setState多个不同的值,在更新时会对其进行合并批量更新,以达到性能优化的目的。
- 正常调用setState的话会形成异步化,但是通过setState接受一个函数作为参数,函数的首个参数就是上一次的state,以达到同步化。
BFC
BFC(Block Formatting Context)
- 开启BFC的区域,是一块独立的渲染区域
- 隔绝了内部与外部的联系,内部渲染不会影响到外部
- 不同的BFC区域,渲染时也互不干扰 开启BFC能解决什么问题?
- 子元素不会再产生margin塌陷问题
- 就算子元素浮动,自身高度也不会塌陷
- 自己不会被其他浮动元素所覆盖
如何开启BFC?
- 根元素(HTML)
- 设置float属性
- 设置position属性(属性值需要时absolute或fixed)
- 设置overflow属性(属性值不为visible即可)
- 设置行内块元素(inline-block)
- 设置display为flow-root的元素
- 伸缩项目(flex盒子内的item),即其父元素为
display: flex; - 多列容器(设置column-count)
- 表格元素(table thead tbody tfoot tr th td caption)
- column-span为all 的元素(表格第一行横跨所有列)
jwt
jwt的原理是,服务器认证以后,生成一个JSON对象, 发回给用户,就像下面这样:
{
"姓名": "张三",
"角色": "管理员",
"到期时间": "2018年7月1日0点0分"
}
以后,用户与服务端通信的时候,都要发回这个JSON对象。服务器完全只靠这样对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。服务器就不保存任何session数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。
时机的jwt由以下三部分组成,中间用.隔开
- Header(头部)
- Payload(负载)
- Signature(签名)
写成一行就是
Header.Payload.Signature总的来说,jwt就是一开始服务器将用户的身份信息通过base64编码以及使用只有服务器知道的密钥加密生成的签名一起返回给客户端,然后客户端再次请求服务器的时候,服务器拿到身份信息再次生成签名,并与之前的签名对比要验证用户身份。
如何解决js数字精度丢失的问题
- 转成整数计算
- Math.js
- BigDecimal.js