大家好,悄悄我卡颂 React18正式版已经发布一段时间了,告诉如果你升级到v18,档里的地且仍使用ReactDOM.render创建应用,写错会收到如下报警: 大意是悄悄说:v18使用createRoot而不是render创建应用,如果你仍使用render创建应用,告诉那么应用的档里的地行为将同v17一样。 React团队之所以有底气让大家都升级到v18,写错使用createRoot,悄悄是告诉因为他们作出了承诺: 大意是说:如果你升级到v18,只要不使用「并发特性」(比如useTransition),档里的地React会和之前版本表现一致(更新会同步、写错不可中断)。悄悄 今天这篇文章想说的告诉是:某些情况下,上述说法是档里的地错误的。 例中有a、b两个状态,首次渲染完2秒后会触发a、b更新。 其中触发b更新的源码库方式比较特殊:模拟点击,间接触发b更新: function App() { const [a, setA] = useState(0); const [b, setB] = useState(0); const BtnRef = useRef useEffect(() => { setTimeout(() => { setA(9000); BtnRef.current?.click(); }, 2000); }, []); return ( ref={ BtnRef} onClick={ () => setB(1)}> b: { b} { Array(a).fill(0).map((_, i) => { return })} ); 完整示例地址[1] 现在我们有两种挂载的方式。 v18之前的方式: const rootElement = document.getElementById("root"); // v18之前创建应用的方式 v18提供的方式: const root = ReactDOM.createRoot(rootElement); // v18创建应用的方式 root.render( 为了看清这两者的区别,有两种方式: 调大setA(9000)中的值,使页面渲染更多项。页面渲染时卡顿越明显,渲染顺序的差异越明显。 setTimeout(() => { setA(9000); BtnRef.current?.click(); 在react-dom.development.js的commitRootImpl方法中打断点。 这个方法是React渲染时调用的方法,在这里打断点可以看出页面渲染的顺序。 对于ReactDOM.render创建的应用,触发更新后渲染顺序如下: 首先: 其次: 对于ReactDOM.createRoot创建的应用,触发更新后渲染顺序如下: 首先: 其次: 渲染顺序显然是变了,这和React文档里的说法是相悖的。 背后的网站模板原因是什么呢? 先解释下示例中的b为什么采用「触发onClick事件」的方式间接触发更新: 这是因为:不同方式触发的更新有不同「优先级」,onClick回调中触发的更新是最高优的,即「同步优先级」。 那么问题来了,v18不使用并发特性,所有更新不都该是「同步、不可中断」么? 这话是没错,更新本身是「同步、不可中断」的。但是更新是需要调度的。 在示例中,如果采用ReactDOM.createRoot创建应用,那么触发更新时的优先级如下: setTimeout(() => { // 触发更新,优先级为“默认优先级” setA(9000); // 触发更新,优先级为“同步优先级” BtnRef.current?.click(); 接下来React的亿华云计算执行流程如下: 可见,只要采用ReactDOM.createRoot创建应用,那么「优先级」的影响就会一直存在,与「使用了并发特性」的区别是: 那么采用ReactDOM.render创建的应用执行顺序又是怎么一回事呢? 记不记得一道经典(且毫无意义)的React面试题:React的更新是同步还是异步的? 下面两种情况,a打印的结果是1么? // 情况1 onClick() { this.setState({ a: 1}); console.log(a); } // 情况2 onClick() { setTimeout(() => { this.setState({ a: 1}); console.log(a); }) 其中,情况2中a打印结果是1。 之所以会有这种情况,是React早期实现批处理时的瑕疵造成的,并不是什么有意为之的特性。 当React使用Fiber架构重构后,完全可以规避这个瑕疵。但为了与老版本行为保持一致,刻意实现成这样。 所以,在我们的示例中,这两个更新不会受到「优先级」的影响,但会受到「为了兼容老版本」造成的影响: setTimeout(() => { setA(9000); BtnRef.current?.click(); React的执行流程如下: React作为一款维护了快10年的框架,在经历重大版本更新后要保持框架行为前后一致,实属不易。 更新顺序的变化对一般应用影响不大。 但是,如果你的应用依赖更新后「页面中当前的值」作出后续判断,那么需要注意升级到v18后的这些细微变化。 [1]完整示例地址: https://codesandbox.io/s/strange-cartwright-iq1s2m?file=/src/index.tsx。
上一篇
下一篇