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

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

什么 Bug?


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

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

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

image.pnghttps://code.juejin.cn/pen/7145527312400777254


import React, { useState } from 'react';
import ReactDom from 'react-dom';
function App() {
  const [isOpen, setIsOpen] = useState(false)
  return (
    <>
      <span>状态: {isOpen ? 'open' : 'closed'}</span>
      <details open={isOpen}>
        <summary
          onClick={() => {
            setIsOpen(!isOpen)
          }}
        >
          Summary
        </summary>
        Details
      </details>
    </>
  )
}
ReactDom.render(<App />, 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 (
    <>
      <span>状态: {isOpen ? 'open' : 'closed'}</span>
      <details
        open={isOpen} 
        onToggle={() => {
          setIsOpen(!isOpen)
        }}>
        <summary>
          Summary
        </summary>
        Details
      </details>
    </>
  )
}

这样似乎正常了。

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

image.pnghttps://code.juejin.cn/pen/7145537955291987976


function App() {
  const [isOpen, setIsOpen] = useState(false)
  return (
    <>
      <span>状态: {isOpen ? 'open' : 'closed'}</span>
      <details
        open={isOpen}
        onToggle={() => {
          setIsOpen(!isOpen)
        }}>
        <summary
        >
          Summary
        </summary>
        Details
        <button onClick={() => setIsOpen(!isOpen)}>切换状态</button>
      </details>
    </>
  )
}

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

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

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


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


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

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

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

Vue 的代码如下:


<template>
  <span>状态: {{isOpen ? 'open' : 'closed'}}</span>
  <details :open="isOpen">
    <summary @click="()=> {isOpen = !isOpen}">
      Summary
    </summary>
    Details
  </details>
</template>
<script>
  import { defineComponent, ref } from 'vue';
export default defineComponent({
  setup() {
    const isOpen = ref(false);
    return {
      isOpen
    };
  },
});
</script>

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

image.pnghttps://code.juejin.cn/pen/7145527732409991200


这个 Bug 到底是谁的锅?


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

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


<!DOCTYPE html>
<html>
  <body>
    <span>状态: closed</span>
    <details>
      <summary>Summary</summary>
      Details
    </details>
    <script>
      const span = document.querySelector('span')
      const details = document.querySelector('details')
      const summary = document.querySelector('summary')
      let isOpen = false
      summary.addEventListener('click', () => {
        if (isOpen) {
          isOpen = false
          span.textContent = '状态: closed'
          details.removeAttribute('open')
          return
        }
        isOpen = true
        span.textContent = '状态: open'
        details.setAttribute('open', '')
      })
    </script>
  </body>
</html>

image.pnghttps://code.juejin.cn/pen/7145540132412588062

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

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



相关文章
|
27天前
|
存储 缓存 JavaScript
如何优化React或Vue应用的性能
需要注意的是,性能优化是一个持续的过程,需要根据具体的应用场景和性能问题进行针对性的优化。同时,不同的项目和团队可能有不同的优化重点和方法,要结合实际情况灵活运用这些优化策略,以达到最佳的性能效果。
107 51
|
26天前
|
监控 前端开发 数据可视化
3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生
@icraft/player-react 是 iCraft Editor 推出的 React 组件库,旨在简化3D数字孪生场景的前端集成。它支持零配置快速接入、自定义插件、丰富的事件和方法、动画控制及实时数据接入,帮助开发者轻松实现3D场景与React项目的无缝融合。
99 8
3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目,实现数字孪生
|
27天前
|
监控 JavaScript 前端开发
如何在实际应用中测试和比较React和Vue的性能?
总之,通过多种方法的综合运用,可以相对客观地比较 React 和 Vue 在实际应用中的性能表现,为项目的选择和优化提供有力的依据。
33 1
|
28天前
|
JavaScript 前端开发 开发者
React和Vue有什么区别?
React 和 Vue 都有各自的优势和特点,开发者可以根据项目的需求、团队的技术背景以及个人的喜好来选择使用。无论是 React 还是 Vue,它们都在不断发展和完善,为前端开发提供了强大的支持。
73 2
|
28天前
|
JavaScript 前端开发 测试技术
React和Vue的性能对比如何?
需要注意的是,性能不仅仅取决于框架本身,还与开发者的代码质量、架构设计以及项目的优化程度等密切相关。因此,在评估性能时,应该综合考虑多个因素,而不是仅仅局限于框架之间的比较。
108 1
|
29天前
|
前端开发 JavaScript 开发者
使用React和Redux构建高效的前端应用
使用React和Redux构建高效的前端应用
33 1
|
1月前
|
JavaScript 前端开发 算法
React 框架和 Vue 框架的区别是什么?
React框架和Vue框架都是目前非常流行的前端JavaScript框架,它们在很多方面存在区别
|
24天前
|
前端开发 JavaScript 算法
探索现代前端框架——React 的性能优化策略
探索现代前端框架——React 的性能优化策略
21 0
|
24天前
|
前端开发 JavaScript API
探索现代前端框架——React 的性能优化策略
探索现代前端框架——React 的性能优化策略
26 0
|
1月前
|
JavaScript 前端开发 开发者
JavaScript框架React vs. Vue:一场性能与易用性的较量
JavaScript框架React vs. Vue:一场性能与易用性的较量
34 0
下一篇
DataWorks