大家好,究惑电动玩具WRITE AS相信很多的网友都不是很明白,包括React 17 要来了,非常特别的一版也是一样,不过没有关系,接下来就来为大家分享关于究惑电动玩具WRITE AS和React 17 要来了,非常特别的一版的一些知识点,大家可以关注收藏,免得下次来找不到哦,下面我们开始吧!
写在前面React最近发布了v17.0.0-rc.0,距上一个大版本v16.0(发布于2017/9/27)已经过去近3年了
与新特性云集的React16及先前的大版本相比,React17显得格外特殊——没有新特性:
Reactv17.0ReleaseCandidate:NoNewFeatures
不仅如此,还带上来了7个breakingchange……
一.真没有新特性?React官方对v17的定位是一版技术改造,主要目标是降低后续版本的升级成本:
ThisreleaseisprimarilyfocusedonmakingiteasiertoupgradeReactitself.
因此v17只是一个铺垫,并不想发布重大的新特性,而是为了v18、v19……等后续版本能够更平滑、更快速地升上来:
WhenReact18andthenextfutureversionscomeout,youwillnowhavemoreoptions.
但其中有些改造不得不打破向后兼容,于是提出了v17这个大版本变更,顺便搭车卸掉两年多积攒的一些历史包袱
二.渐进式升级成为了可能在v17之前,不同版本的React无法混用(事件系统会出问题),所以,开发者要么沿用旧版本,要么花大力气整个升级到新版本,甚至一些常年没有需求的长尾模块也要整体适配、回归测试。考虑到开发者的升级适配成本,React维护团队同样束手束脚,废弃API不敢轻易下掉,要么长时间、甚至无休止地维护下去,要么选择放弃那些老旧的应用
而React17提供了一个新的选项——渐进式升级,允许React多版本并存,对大型前端应用十分友好,比如弹窗组件、部分路由下的长尾页面可以先不升级,一块一块地平滑过渡到新版本(参考官方Demo)
P.S.注意,(按需)加载多个版本的React存在着不小的性能开销,同样应该慎重考虑
多版本并存与微前端架构多版本并存、新旧混用的支持让微前端架构所期望的渐进式重构成为了可能:
渐进地升级、更新甚至重写部分前端功能成为了可能
与React支持多版本并存、渐进地完成版本升级相比,微前端更在意的是允许不同技术栈并存,平滑地过渡到升级后的架构,解决的是一个更宽的问题
另一方面,当React技术栈下多版本混用难题不复存在时,也有必要对微前端进行反思:
一些问题是不是由技术栈自身来解决更为合适?多技术栈并存是常态还是短期过渡?对于短期过渡,是否存在更轻量的解决方案?关于微前端在解决什么问题的更多思考,见Whymicro-frontends?
三.7个Breakingchange事件委托不再挂到document上之前多版本并存的主要问题在于React事件系统默认的委托机制,出于性能考虑,React只会给document挂上事件监听,DOM事件触发后冒泡到document,React找到对应的组件,造一个React事件(SyntheticEvent)出来,并按组件树模拟一遍事件冒泡(此时原生DOM事件早已冒出document了):
[captionid="attachment_2263"align="alignnone"width="625"]
react16delegation[/caption]
因此,不同版本的React组件嵌套使用时,e.stopPropagation()无法正常工作(两个不同版本的事件系统是独立的,都到document已经太晚了):
Ifanestedtreehasstoppedpropagationofanevent,theoutertreewouldstillreceiveit.
P.S.实际上,Atom在早些年就遇到了这个问题
为了解决这个问题,React17不再往document上挂事件委托,而是挂到DOM容器上:
[captionid="attachment_2264"align="alignnone"width="625"]
react17delegation[/caption]
例如:
constrootNode=document.getElementById('root');n//以为render为例nReactDOM.render(<App/>,rootNode);n//Portals也一样n//ReactDOM.createPortal(<App/>,rootNode)n//React16事件委托(挂到document上)ndocument.addEventListener()n//React17事件委托(挂到DOMcontainer上)nrootNode.addEventListener()n
另一方面,将事件系统从document缩回来,也让React更容易与其它技术栈共存(至少在事件机制上少了一些差异)
向浏览器原生事件靠拢此外,React事件系统还做了一些小的改动,使之更加贴近浏览器原生事件:
onScroll不再冒泡onFocus/onBlur直接采用原生focusin/focusout事件捕获阶段的事件监听直接采用原生DOM事件监听机制注意,onFocus/onBlur的下层实现方案切换并不影响冒泡,也就是说,React里的onFocus仍然会冒泡(并且不打算改,认为这个特性很有用)
DOM事件复用池被废弃之前出于性能考虑,为了复用SyntheticEvent,维护了一个事件池,导致React事件只在传播过程中可用,之后会立即被回收释放,例如:
<buttononClick={(e)=>{nconsole.log(e.target.nodeName);n//输出BUTTONn//e.persist();nsetTimeout(()=>{n//报错UncaughtTypeError:Cannotreadproperty'nodeName'ofnullnconsole.log(e.target.nodeName);n});n}}>nClickMe!n</button>n
传播过程之外的事件对象上的所有状态会被置为null,除非手动e.persist()(或者直接做值缓存)
React17去掉了事件复用机制,因为在现代浏览器下这种性能优化没有意义,反而给开发者带来了困扰
EffectHook清理操作改为异步执行useEffect本身是异步执行的,但其清理工作却是同步执行的(就像Class组件的componentWillUnmount同步执行一样),可能会拖慢切Tab之类的场景,因此React17改为异步执行清理工作:
useEffect(()=>{n//Thisistheeffectitself.nreturn()=>{n//以前同步执行,React17之后改为异步执行n//Thisisitscleanup.n};n});n
同时还纠正了清理函数的执行顺序,按组件树上的顺序来执行(之前并不严格保证顺序)
P.S.对于某些需要同步清理的特殊场景,可换用LayoutEffectHook
render返回undefined报错React里render返回undefined会报错:
functionButton(){nreturn;//Error:Nothingwasreturnedfromrendern}n
初衷是为了把忘写return的常见错误提示出来:
functionButton(){n//Weforgottowritereturn,sothiscomponentreturnsundefined.n//Reactsurfacesthisasanerrorinsteadofignoringit.n<button/>;n}n
在后来的迭代中却没对forwardRef、memo加以检查,在React17补上了。之后无论类组件、函数式组件,还是forwardRef、memo等期望返回React组件的地方都会检查undefined
P.S.空组件可返回null,不会引发报错
报错信息透出组件“调用栈”React16起,遇到Error能够透出组件的“调用栈”,辅助定位问题,但比起JavaScript的错误栈还有不小的差距,体现在:
缺少源码位置(文件名、行列号等),Console里无法点击跳转到到出错的地方无法在生产环境中使用(displayName被压坏了)React17采用了一种新的组件栈生成机制,能够达到媲美JavaScript原生错误栈的效果(跳转到源码),并且同样适用于生产环境,大致思路是在Error发生时重建组件栈,在每个组件内部引发一个临时错误(对每个组件类型做一次),再从error.stack提取出关键信息构造组件栈:
varprefix;n//构造div等内置组件的“调用栈”nfunctiondescribeBuiltInComponentFrame(name,source,ownerFn){nif(prefix===undefined){n//ExtracttheVMspecificprefixusedbyeachline.ntry{nthrowError();n}catch(x){nvarmatch=x.stack.trim().match(/\n(*(at)?)/);nprefix=match&&match[1]||'';n}n}//Weusetheprefixtoensureourstackslineupwithnativestackframes.nnreturn'\n'+prefix+name;n}n//以及describeNativeComponentFrame用来构造Class、函数式组件的“调用栈”n//...太长,不贴了,有兴趣看源码n
因为组件栈是直接从JavaScript原生错误栈生成的,所以能够点击跳回源码、在生产环境也能按sourcemap还原回来
P.S.重建组件栈的过程中会重新执行render,以及Class组件的构造函数,这部分属于Breakingchange
P.S.关于重建组件栈的更多信息,见BuildComponentStacksfromNativeStackFrames、以及react/packages/shared/ReactComponentStackFrame.js
部分暴露出来的私有API被删除React17删除了一些私有API,大多是当初暴露给ReactNativeforWeb使用的,目前ReactNativeforWeb新版本已经不再依赖这些API
另外,修改事件系统时还顺手删除了ReactTestUtils.SimulateNative工具方法,因为其行为与语义不符,建议换用ReactTestingLibrary
四.总结总之,React17是一个铺垫,这个版本的核心目标是让React能够渐进地升级,因此最大的变化是允许多版本混用,为将来新特性的平稳落地做好准备
We’vepostponedotherchangesuntilafterReact17.Thegoalofthisreleaseistoenablegradualupgrades.
参考资料Reactv17.0ReleaseCandidate:NoNewFeatures有所得、有所惑,真好关注「前端向后」微信公众号,你将收获一系列「用心原创」的高质量技术文章,主题包括但不限于前端、Node.js以及服务端技术
本文首发于ayqy.net,原文链接:http://www.ayqy.net/blog/react-17/
好了,文章到此结束,希望可以帮助到大家。
本文由欣欣吧手游攻略栏目发布,感谢您对欣欣吧的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人站长或者朋友圈,但转载请说明文章出处“究惑电动玩具WRITE AS”