我发现了一个React、Vue等所有前端框架都存在的隐秘Bug?

什么 Bug?

昨天有个朋友请教了我一个问题,她在使用原生的 Details 元素封装一个手风琴组件。但是无论如何都不能按照预期工作。

起初我认为是她水平比较差,代码写的有问题。但是她一再向我保证绝对不是她的问题。所以我就抽出时间帮她看了一下。意外发现这一个框架的隐秘 Bug。

我把这个代码放到了码上掘金上,你可以看一下。

import React, { useState } from 'react';
import ReactDom from 'react-dom';

function App() {const [isOpen, setIsOpen] = useState(false)return (<>状态: {isOpen ? 'open' : 'closed'}
{setIsOpen(!isOpen)}}>SummaryDetails
) } ReactDom.render(, document.getElementById('app'));

我们发现在组件的一开始并没有按照预期去渲染组件。之后的每一次点击,都不会按照预期去渲染。

为什么会这样?

原因在于 details 元素具有自身的状态,React 并不知道。

简单来说,这个问题在于 details 的 open 属性有两个数据来源:React 和浏览器。

更详细的讨论可以看这个 Github issue 。

首先我解释一下当第一次单击按钮时会发生什么:

1.summary 元素的 onClick 事件触发,状态 isOpen 从 false 变为 true。
2.React 重新渲染组件,将 details 元素的 open 属性设置为 true。
3.details 元素的默认行为会切换自身 open 状态,将 open 设置为 false,但 React 并不知道。

所以这就是 details 元素最终没有将 open 属性设置为 true 的原因,而我们的 isOpen 状态依然是 true。

第二次点击:

1.summary 元素的 onClick 处理程序 被触发,切换 isOpen 到 false.
2.React 重新渲染,发现 details 已经关闭,所以它不会去改变它。
3.details 元素的默认行为再次切换它的 open 状态。现在是 false,所以它会把 open 状态改变为 true,而 React 仍然不知道。

在此之后,一切都会打破。

怎么解决?

e.preventDefault

解决思路其实很简单,只要不让浏览器乱动状态就可以了。我们可以使用 e.preventDefault 来禁止浏览器的默认行为。这样就只有 React 能够控制它的状态了。

toggle

除了上面的方法外,还有一种方法是通过 details 的 toggle 事件来处理它。

function App() {const [isOpen, setIsOpen] = useState(false)return (<>状态: {isOpen ? 'open' : 'closed'} {setIsOpen(!isOpen)}}>SummaryDetails)
} 

这样似乎正常了。

但是很快我的朋友又遇到了新的麻烦,她在 details 中有一个按钮,这个按钮可以改变 isOpen 的状态。

function App() {const [isOpen, setIsOpen] = useState(false)return (<>状态: {isOpen ? 'open' : 'closed'} {setIsOpen(!isOpen)}}>SummaryDetails)
} 

当点击这个按钮时,浏览器就抽风了,进入了死循环状态。

我又试着帮她解析了一下这个问题的原因:

1.按钮的 onClick 事件会切换 isOpen,同时会更改 details 的 open 属性。
2.open 属性的变化会触发 onToggle 事件。
3.onToggle 事件会再次切换 isOpen 的状态。同时改变了 details 的 open 属性,这时又回到了第 2 步,所以进入了无限循环状态。

这个 Bug 是 React 框架独有的吗?

虽然朋友解决了这个问题,但是她也向我吐槽 React 难用。

我很好奇这个问题是 React 独有的问题吗?其他类似的框架,比如 Soild、Svelte 和 Vue 它们会有这个问题吗?

于是我尝试了其他所有框架,发现它们都有这个问题。

Vue 的代码如下:



 

我也放到了码上掘金上,你可以看一下。

这个 Bug 到底是谁的锅?

鉴于所有的框架都有这个问题,所以我认为它不应该是框架的问题。

于是我尝试用原生的 JavaScript 来编写这段程序。


状态: closed
SummaryDetails

现在看来,这似乎是 details 这个元素的底层工作原理的问题,和框架无关。

能够完美解决的唯一办法就是通过 e.preventDefault 来禁止掉浏览器默认行为,让 JavaScript 中的变量成为唯一的数据源。

最后

整理了75个JS高频面试题,并给出了答案和解析,基本上可以保证你能应付面试官关于JS的提问。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

你可能感兴趣的:(前端框架,vue.js,react.js)