React
什么是concurrent mode
async rendering mode & concurrent rendering
这里的 async rendering 指的是 js 执行 -> DOM更新 层面的异步,也就是某个setState并不会导致DOM直接更新。
Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。
我们在更新 state 时(包括 redux 的更新),一直是采取了一种 类immutable 的操作。如我们改变一个对象属性,我们会采用解构赋值来实现浅拷贝 { …origin, newValue }。这里所要实现的目的就是能够在修改引用类型属性时,引用类型本身同时发生变更(在js中的表现就是引用地址变更,除变更属性外,其他属性浅拷贝,也就是解构赋值),之后外部使用该引用类型若要进行前值对比(shouldUpdate/deps)时则可感知。
setState 是异步的?
https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
因为在react某个执行阶段中收集到的同一个组件的 setState 会被做合并处理:
React may batch multiple setState()
calls into a single update for performance.
我们调用setState时真正的render不是同时发生的,而是render函数产生virtual DOM(或者叫React内部存储DOM信息的一种格式),期间可能会有新的setState或props变化导致这个过程继续计算,计算完成后的虚拟DOM会于之前的对比,最终将需要修改的内容修改到真实DOM上。
React Elements —> Fiber nodes
所以setState的异步指的就是 setState 的调用与视图的真正重绘是异步的。
这个概念会让人产生疑惑,不过也能从中得知一部分React设计者的想法,React设计者希望开发人员进行需要触发视图重绘处理的操作时能显式调用,但最终如何让DOM实现渲染这个过程,则不交给开发者处理,这样React就可以对其进行调度设计,并进行性能优化。
React喜欢将决定权交给开发人员,且往往以一种显式的表达方式(所以 React 似乎很不喜欢proxy这种元编程)。其中还有一个例子就是子节点数组渲染中的 Key:
https://reactjs.org/docs/reconciliation.html#recursing-on-children
因为List头部插入本身就是比尾部插入速度慢的,如果在渲染diff DOM时,还需要查找原节点并判断当前节点是否变化,React表示不能接受,所以它选择让开发者自行传入 key 来实现DOM的标记,完成原节点的快速查找。
所以不传递 key 往往也不会产生错误,但是会降低性能。
Fiber 调度算法
FIber 是如何实现可中断的?
https://blog.ag-grid.com/inside-fiber-an-in-depth-overview-of-the-new-reconciliation-algorithm-in-react/
https://reactjs.org/docs/reconciliation.html
concurrent mode
https://codesandbox.io/s/koyz664q35 // Dan 展示各个模式下的区别
高优先级操作优先处理,低优先级操作后置处理(且会被高优先级打断)。
用户输入反馈,属于高优先级,DOM绘制属于较低优先级。
使 React 在长时间渲染的场景下依旧保持良好的交互性,能优先执行高优先级变更,不会使页面处于卡顿或无响应状态,从而提升应用的用户体验。
屏幕录制2021-02-23 下午5.57.07.mov
performace is intergral to UX
https://www.youtube.com/watch?v=ByBPyMBTzM0&ab_channel=ReactConf
partially render a tree without committing the result
does not block the main thread
update ->同步 视图渲染
render函数返回的结果会拿去做Virtual DOM比较和更新DOM树,这个阶段是能出现可感知耗时的。
concurrent mode 的出现是为了让 React 能更精准地把握 视图数据状态 间的转化,并能做出更多干涉。
concurrent mode 相当于是实现这一切所需要的底层调度策略。
后期 React 会在推出更多的 API 或使用模式,让用户能够在框架的领导下,更方便(?)更规范(?)地写出高性能的视图逻辑。
所以在更细粒度的全局协调下,且不打断主线程。单一组件的连续执行(不可中断)的生命周期将难以为继。
https://twitter.com/sebmarkbage/status/1277770181721198592
Typescript
处理 setInterval 返回类型问题
使用window.setInterval
或
let timer: ReturnType<typeof setTimeout> = setTimeout(() => { ... });
clearTimeout(timer);
如果是 web 环境下混入 node 的类型,可以找到具体混入的位置,不引入该文件即可。但往往比较困难,且大部分情况下无法不引入。