从做一个播放音乐的小组件讲React的事件触发机制

前言

大家好,这篇文章是React系列的第二篇,之前已经介绍过React的一些基础的东西,包括如何初始化、组件式开发、useState。今天用这篇文章接着介绍,React的基础之一---事件触发机制!我们通过一个制作一个音乐播放的按钮,先讲解原生方式的实现,再进行抛砖引玉,详细地介绍React的实现,以及React的一些重要知识点!

原生方式播放音乐

我们的目的是为了实现一个小按钮,在点击后,能够在网页中播放音乐,当然,用户初始进入到网页时,不会自动播放音乐,这里注意我们必须得注重用户体验,咱做前端的,切记要极致优化用户体验,如果你创建一个网页,用户一点击该网页就自动播放音乐的话,可能会给用户带来"惊吓",所以我们需要做一个按钮,只有在点击后才能播放音乐!

原生方式实现

首先我们创建一个文件夹,名为audio,然后在这个文件夹下创建我们具体实现效果的页面,以及创建一个sounds文件夹,用于存放我们将来要播放的音乐文件snare.wav

引入audio标签

首先我们要介绍播放音乐的标签是<audio>,我们可以在这个标签中设置音乐文件,实现播放对应的音乐。

我们为audio标签指定src属性,即指定具体的音乐文件,这里将src设置为sounds里面的snare.wav。

html 复制代码
 <audio src="./sounds/snare.wav"></audio>  //将audio标签放入body中

此时我们打开页面,发现啥也没看着,这是因为audio这个标签是专门用来播放音乐的,不会在页面上显示,默认也不会播放音乐,这是我们前面提到的,为了用户体验,不会自动播放,也不会显示出来。

但是我们可以打开浏览器的检查,在网络中看到它。

引入button标签

既然audio不能在页面中显示,那么用户如何播放音乐呢?这时候就要写一个button标签,名为播放。专门让用户实现交互的效果。

html 复制代码
<button id="play">播放</button>

接下来我们就要真正的实现效果:用户在点击button后,网页能够播放audio指定的音乐。我们使用原生DOM编程,编写一段JavaScript代码实现交互:

js 复制代码
// 获取页面中的audio元素
const oAudio = document.querySelector('audio');  

//  为播放按钮添加点击事件监听器
document.querySelector('#play').addEventListener('click', () => {
    oAudio.play();  //调用audio元素的play方法
})
  1. 我们通过document.querySelector('audio')获取页面中的第一个audio元素,并将其存储在oAudio变量中。
  2. 接着使用document.querySelector('#play')找到ID为'play'的按钮元素,并为其添加点击事件监听器。
  3. 当按钮被点击时,会执行回调函数,调用oAudio.play()方法来播放音频。
  4. 这样当用户点击按钮时,就能听到audio元素指定的音频内容了。

完整代码展示:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Audio 音乐</title>
</head>
<body>
 <audio src="./sounds/snare.wav"></audio>
 <button id="play">播放</button>

 <script>
    const aAudio = document.querySelector('audio');
    document.querySelector('#play').addEventListener('click', () => {
        aAudio.play();
    })
 </script>
</body>
</html>

原生实现的特点

上面的效果是用原生DOM编程实现的,它有以下特点:

  • 我们在选择元素时必须要使用document的一系列API
  • HTML部分和JS部分是分离的,各自做专门的事情:HTML负责页面结构,JS负责交互
  • 需要显示绑定事件监听器,事件的实现较为繁琐

DOM1 与 DOM2

其实我们的DOM编程有DOM1和DOM2两种,上面介绍的是DOM2,下面来给大家介绍一下DOM1编程实现上面的效果

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Audio 音乐 (DOM1实现)</title>
</head>
<body>
 <!-- DOM1 通常依赖元素的 name 或 id 直接访问 -->
 <audio id="myAudio" src="./sounds/snare.wav"></audio>
 <!-- 内联事件绑定是DOM1的典型特征 -->
 <button id="play" onclick="playAudio()">播放</button>

 <script>
    function playAudio() {
        const audio = document.getElementById('myAudio');
        audio.play(); // 播放逻辑不变
    }
 </script>
</body>
</html>

你会发现,DOM1的编程是没有事件监听addEventListener,它采用的是内联事件(如直接在HTML标签上写onclick等),它的HTML和JS高度耦合。其实这会带来一些问题:

  • 扩展性差:难以动态绑定/解绑事件
  • 全局污染:playAudio()必须暴露在全局作用域

所以现代原生编程使用的是上文的DOM2编程,它增强了DOM操作能力,引入了更强大的API。

不过尽管DOM2比DOM1更强大,但仍然存在一些问题:

  • 手动DOM操作繁琐 :每次更新UI都需要手动选择元素并修改(如innerHTMLappendChild)。
  • 性能问题:频繁的DOM操作会导致浏览器重绘/回流(Reflow & Repaint),影响性能。
  • 状态管理困难:数据变化时,需要手动同步DOM,容易出错。

所以下面介绍的React采用了声明式UI和虚拟DOM解决了这些问题。下面让我们详细介绍React的方式实现!

React方式播放音乐

接下来,介绍用React的方式实现,我们首先初始化一个React项目,名为react-music。如下是项目的结构。

由于逻辑较为简单,下面我们就不用创建子组件,直接在App里写我们的页面了。

创建audio

首先,我们用jsx语法,在App函数的返回值中,先创建一个元素,这是我们待会要实现的音乐元素。同样的指定src为/sounds/snare.wav

js 复制代码
 return (
    <>
      <audio src="/sounds/snare.wav"></audio>
    </>
  )

创建button

接着,我们继续创建一个button元素,用于用户交互,是用户实现播放的按钮

jsx 复制代码
return (
    <>
      <audio  src="/sounds/snare.wav"></audio>
      <button >播放</button> 
    </>

添加事件

接着,我们准备为button按钮添加事件

回想DOM2编程,我们使用DOM的原生API document....实现元素的选择,然后使用事件监听器实现事件的实现。但是React并不是用这种方式,它会直接在该标签上指定事件和触发时的回调函数。

让我们为button添加点击事件的触发的函数,并且声明这个函数的执行内容。

html 复制代码
......
 const playMusic = () => {
    // 函数触发时执行的内容。
  }
  
  return (
    <>
      <audio src="/sounds/snare.wav"></audio>
      <button onClick={playMusic}>播放</button> 
    </> 
  )
......

通过观察,你可能会发现,这种方式和DOM1的方式很类似,都是直接在元素上指定事件,但它们只是形式相似,实则React实现这个很复杂,底层大有天地。

useRef

我们现在需要在playMusic实现具体的内容,选择audio标签并且执行play()方法。那么问题来了?如何选择audio标签呢?

我们将这个任务交给useRef,在React中,通过useRef选择DOM节点,实现操作DOM.

我们首先创建一个audioPlayer的ref 初始设置为null
const audioPlayer = useRef(null)

接着我们绑定该ref到audio标签:
<audio ref={audioPlayer} ...>

实现播放效果

接着我们书写playMusic,实现真正的播放效果!

js 复制代码
  const playMusic = () => {
    audioPlayer.current.play();
  }

在用户点击按钮button之后,按钮会调用audioPlayer当前绑定的audio的play方法,此时就大功告成了!你会发现相对来讲整个过程比DOM1和DOM2都简单多了!

下面给出源代码

js 复制代码
import { useState , useRef } from 'react'
import './App.css'

function App() {
  
  const audioPlayer = useRef(null);
  const playMusic = () => {
    audioPlayer.current.play();
  }
  return (
    <>
      <audio ref={audioPlayer} src="/sounds/snare.wav"></audio>
      <button onClick={playMusic}>播放</button> 
    </> 
  )
}
export default App

React方式特点

通过这个音乐播放器的实现,我们可以总结出 React 事件处理的几个关键特点:

  1. 声明式事件绑定

    • 不同于 DOM2 的 addEventListener,React 直接在 JSX 中通过类似 onClick={handler} 的方式绑定事件
    • 更直观,与 UI 结构紧密结合
  2. 合成事件系统

    • React 使用自己的合成事件(SyntheticEvent)系统,不是原生 DOM 事件
    • 底层实现了事件委托,将所有事件委托到 document 级别处理
    • 提供了跨浏览器的一致事件接口
  3. Ref 机制访问 DOM

    • 使用 useRef 创建引用对象
    • 通过 ref 属性绑定到 DOM 节点
    • 通过 ref.current 访问实际 DOM 节点
    • 比直接使用 document.querySelector 更符合 React 理念
  4. 函数式组件中的实现

    • 事件处理函数就是普通的 JavaScript 函数
    • 可以方便地访问组件内的状态和 props
    • 不需要考虑 this 绑定问题(相比类组件)
  5. 与原生 DOM 操作对比的优势

    • 更简洁:不需要手动选择元素和添加/移除事件监听器
    • 更安全:自动处理事件解绑,减少内存泄漏风险
    • 更高效:利用 React 的虚拟 DOM 和协调算法优化性能
  6. 单向数据流体现

    • 通过事件触发状态变化
    • 状态变化驱动 UI 更新
    • 不直接操作 DOM,而是通过状态管理 UI

这种模式体现了 React 的核心思想:开发者只需声明"UI 应该是什么样子",而不需要关心"如何更新到那个样子"。React 会高效地处理 DOM 操作和事件管理,让开发者可以更专注于业务逻辑。

相关推荐
aiprtem25 分钟前
基于Flutter的web登录设计
前端·flutter
浪裡遊29 分钟前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
why技术36 分钟前
Stack Overflow,轰然倒下!
前端·人工智能·后端
GISer_Jing41 分钟前
0704-0706上海,又聚上了
前端·新浪微博
止观止1 小时前
深入探索 pnpm:高效磁盘利用与灵活的包管理解决方案
前端·pnpm·前端工程化·包管理器
whale fall1 小时前
npm install安装的node_modules是什么
前端·npm·node.js
烛阴1 小时前
简单入门Python装饰器
前端·python
袁煦丞2 小时前
数据库设计神器DrawDB:cpolar内网穿透实验室第595个成功挑战
前端·程序员·远程工作
天天扭码2 小时前
从图片到语音:我是如何用两大模型API打造沉浸式英语学习工具的
前端·人工智能·github
Liudef062 小时前
2048小游戏实现
javascript·css·css3