我发现了一个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 中的变量成为唯一的数据源。



相关文章
|
6天前
|
JavaScript 前端开发 小程序
一小时入门Vue.js前端开发
本文是作者关于Vue.js前端开发的快速入门教程,包括结果展示、参考链接、注意事项以及常见问题的解决方法。文章提供了Vue.js的基础使用介绍,如何安装和使用cnpm,以及如何解决命令行中遇到的一些常见问题。
一小时入门Vue.js前端开发
|
9天前
|
前端开发 JavaScript API
React、Vue.js 和 Angular前端三大框架对比与选择
前端框架是用于构建用户界面的工具和库,它提供组件化结构、数据绑定、路由管理和状态管理等功能,帮助开发者高效地创建和维护 web 应用的前端部分。常见的前端框架如 React、Vue.js 和 Angular,能够提高开发效率并促进团队协作。
25 4
|
11天前
|
前端开发 JavaScript 开发者
React 和 Vue.js 框架的区别是什么?
React 和 Vue.js 框架的区别是什么?
|
13天前
|
JavaScript 前端开发 开发者
基于Vue.js的前端框架有哪些?
Vue.js 是一款流行的前端 JavaScript 框架,用于构建单页面应用(SPA)。除了 Vue.js 本身,还有许多基于 Vue.js 的前端框架和 UI 库,它们提供了更多的功能和组件,以便开发者能够快速构建应用程序。
19 6
|
22天前
|
前端开发 JavaScript 开发者
Express.js与前端框架的集成:React、Vue和Angular的示例与技巧
本文介绍了如何将简洁灵活的Node.js后端框架Express.js与三大流行前端框架——React、Vue及Angular进行集成,以提升开发效率与代码可维护性。文中提供了详细的示例代码和实用技巧,展示了如何利用Express.js处理路由和静态文件服务,同时在React、Vue和Angular中构建用户界面,帮助开发者快速掌握前后端分离的开发方法,实现高效、灵活的Web应用构建。
36 3
|
21天前
|
JavaScript 前端开发 API
如何在前端开发中有效管理状态:React vs. Vue
在现代前端开发中,状态管理是一个关键因素,它直接影响到应用的性能和可维护性。React 和 Vue 是当前最流行的前端框架,它们在状态管理方面各有优势和劣势。本文将深入探讨 React 和 Vue 在状态管理中的不同实现,分析它们的优缺点,并提供实际应用中的最佳实践,以帮助开发者选择最适合他们项目的解决方案。
|
8天前
|
JavaScript 前端开发 开发者
深入浅出 Vue.js:构建响应式前端应用
Vue.js 是一个流行的前端框架,以其简洁、高效和易学著称。它采用响应式和组件化设计,简化了交互式用户界面的构建。本文详细介绍 Vue.js 的核心概念、基本用法及如何构建响应式前端应用,包括实例、模板、响应式数据和组件等关键要素,并介绍了项目结构、Vue CLI、路由管理和状态管理等内容,帮助开发者高效地开发现代化前端应用。
|
2月前
|
前端开发 Java Spring
Spring与Angular/React/Vue:当后端大佬遇上前端三杰,会擦出怎样的火花?一场技术的盛宴,你准备好了吗?
【8月更文挑战第31天】Spring框架与Angular、React、Vue等前端框架的集成是现代Web应用开发的核心。通过RESTful API、WebSocket及GraphQL等方式,Spring能与前端框架高效互动,提供快速且功能丰富的应用。RESTful API简单有效,适用于基本数据交互;WebSocket支持实时通信,适合聊天应用和数据监控;GraphQL则提供更精确的数据查询能力。开发者可根据需求选择合适的集成方式,提升用户体验和应用功能。
70 0
|
2月前
|
Android开发 iOS开发 C#
Xamarin:用C#打造跨平台移动应用的终极利器——从零开始构建你的第一个iOS与Android通用App,体验前所未有的高效与便捷开发之旅
【8月更文挑战第31天】Xamarin 是一个强大的框架,允许开发者使用单一的 C# 代码库构建高性能的原生移动应用,支持 iOS、Android 和 Windows 平台。作为微软的一部分,Xamarin 充分利用了 .NET 框架的强大功能,提供了丰富的 API 和工具集,简化了跨平台移动应用开发。本文通过一个简单的示例应用介绍了如何使用 Xamarin.Forms 快速创建跨平台应用,包括设置开发环境、定义用户界面和实现按钮点击事件处理逻辑。这个示例展示了 Xamarin.Forms 的基本功能,帮助开发者提高开发效率并实现一致的用户体验。
78 0
|
2月前
|
开发者 Android开发 iOS开发
Xamarin开发者的神器!揭秘你绝不能错过的插件和工具,让你的开发效率飞跃式提升
【8月更文挑战第31天】Xamarin.Forms 是一个强大的框架,让开发者通过单一共享代码库构建跨平台移动应用,支持 iOS、Android 和 Windows。使用 C# 和 XAML,它简化了多平台开发流程,保持一致的用户体验。本指南通过创建一个简单的 “HelloXamarin” 应用介绍 Xamarin.Forms 的基本功能和工作原理。首先配置 Visual Studio 开发环境,然后创建并运行一个包含标题、按钮和消息标签的示例应用,展示如何定义界面布局及处理按钮点击事件。这帮助开发者快速入门 Xamarin.Forms,提高跨平台应用开发效率。
33 0
下一篇
无影云桌面