文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

React#31的render怎么解决

2024-04-02 19:55

关注

这篇文章将为大家详细讲解有关React#31的render怎么解决,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

React#31的render怎么解决

卡颂日常从事基础架构相关工作。这次接到一个任务:封装一个React组件交给业务方使用。

组件本地开发无误,自测无误,交给业务方接入无误,业务方测试环境验收无误。

然而,当包含该组件的页面小流量上线后,监控平台立刻收到报警:

Minified React error #31 ......

打开监控看板,发现大部分报错来自于一款机型:「Vivo x7」,Android版本5.1。完整报错信息如下:

React#31的render怎么解决

看看时间:周五晚上6点半。呵,还有我解决不了的React问题?半小时搞定,过周末去~

然而......

React#31的render怎么解决

找准问题原因

简单描述下#31号错误信息描述的内容:

React的render函数可接受的返回值类型包括:

其中[]会被处理为React.Fragment

因为该返回值会被编译为React.createElement(或jsx.createElement,视React版本不同而不同)。

而React.createElement的返回值是一个对象(即object类型)。

这里的报错信息是说:你某个组件返回了一个非法值。因为这个值是object类型,但是他不是一个JSX对象。

想要复现这个问题也很简单,比如如下代码:

function App() {   reutrn {}; }

返回值是个object,但非JSX对象。

作为React老司机,是绝对不可能写错返回值类型的。况且,写错的话,本地开发就会报错了。

而且很奇怪,这个问题,为什么只在这款机型复现呢?

初见端倪

现在我们掌握的线索是:

我们需要更多线索!!

 React#31的render怎么解决

虽然是被压缩的线上代码,但好在React提供了必要的错误信息。

这个错误的object包含了如下字段:

found: object with keys {$$typeof, type, key, ref, props, _owner}).

居然包含$$typeof!!!

$$typeof是React源码内部用来判断JSX对象类型的字段。

比如React.Fragment、React.portal、div这三种JSX分别对应三种$$typeof。

换言之,包含$$typeof的object类型,大概率是一个JSX对象。

既然这个报错的object对象就是一个JSX对象,那React为什么还认为他是一个非法的返回值呢?

 React#31的render怎么解决

React狠起来连自己都杀?

深入源码

要解答这个问题,必须深入React源码。

由于我的组件中没有使用Fragment或Portal这样的特性,所以将问题定位在普通React组件对应的$$typeof。

在源码中,这种类型被称为REACT_ELEMENT_TYPE。

var hasSymbol = typeof Symbol === 'function' && Symbol.for; var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7;

可以看到,当宿主环境支持Symbol时,REACT_ELEMENT_TYPE === Symbol.for('react.element')。

不支持时,REACT_ELEMENT_TYPE === 0xeac7

与此同时,REACT_ELEMENT_TYPE这个变量的定义不仅存在于React这个包中,也存在于ReactDOM包中。

Vivo x7的webview原生不支持Symbol。似乎有点儿苗头了!

审查业务方代码后发现,业务方使用core-js实现了Symbol polyfill。

那么设想如下场景:

假如业务方代码打包的顺序是:

React -> core-js -> ReactDOM

那么在运行时,React首先加载,执行到定义REACT_ELEMENT_TYPE变量这行代码时,宿主环境全局不存在Symbol。

所以在React这个包中,REACT_ELEMENT_TYPE === 0xeac7

接着运行core-js,他会在window上挂载Symbol。

接着ReactDOM在运行时,执行到定义REACT_ELEMENT_TYPE变量这行代码时,宿主环境已经存在全局变量Symbol。

所以在ReactDOM中,REACT_ELEMENT_TYPE === Symbol.for('react.element')

而React.createElement方法来自React包,组件的render方法是在ReactDOM包中的reconciler模块调用的,

所以就会发生判断组件类型时,0xeac7 !== Symbol.for('react.element'),让React认为这是个非法的返回值。

在遥远的2016年,就有人就此给React提issue[1]

事实真的如此么?

然而审视业务方代码后发现,依赖打包的顺序是:

core-js -> React -> ReactDOM

按照刚才的推理,React和ReactDOM都能拿到core-js提供的Symbol polyfill。

React#31的render怎么解决

拨云见日

此时早已华灯初上,我为我对React的轻视流下了不争气的泪水。

亏我还是React Contributor,React技术揭秘[2]作者,React 17的源码方法名都能背下来。

嗯?React 17??难道!

React#31的render怎么解决

v16.14之前版本的React中JSX对象会被编译为React.createElement。

此版本之后createElement被从React包中拆分出来,独立在react/jsx-runtime中。

编译工作则由@babel/plugin-transform-react-jsx插件完成。

那么此时,REACT_ELEMENT_TYPE的定义存在于jsx-runtime、React、ReactDOM三个包中。

也就是说,如果编译后包的执行顺序是:

jsx-runtime -> core-js -> React -> ReactDOM

在低端Android机上还会复现这个问题!

这里截取一个网友遇到同样问题后他的截图:

React#31的render怎么解决

可以看到,jsx-runtime被babel编译成第一个依赖,破案!

所以,这实际上是个babel编译后产物顺序造成的bug,已经有人给babel提相关issue[3]

最终我将JSX的编译方式从编译为jsx.createElement降级为React.createElement解决了这个报错。

此时,夜已深。。。

每一片雪花都是那么纯洁,直到他们组成了一场雪崩。

这个bug的各方,React、babel、提供组件的我、业务方代码,单独来看,没有一方有问题。

但是,当一系列巧合合并在一起,就是一个线上bug。

这也显示了线上小流量、报错监控基建的重要性。

关于React#31的render怎么解决就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯