导读:本期聚焦于会飞的猪创作的《一文带你全面了解React DOM操作:常见问题与注意事项有哪些?》,敬请观看详情。直接操作DOM在React中一直是个容易踩坑的话题。本文系统讲解React的虚拟DOM与直接DOM操作之间的关系,分析什么时候需要绕过React去操作真实节点,什么时候应该交给状态驱动。内容包括ref的正确使用方式、受控组件与DOM同步冲突、生命周期中的操作时机、事件委托与阻止默认行为、以及性能优化和常见报错的排查思路,帮助你写出更规范的React组件代码。

React的核心思想是声明式渲染:你描述界面应该是什么样子,React负责把它渲染到真实DOM上。但实际项目中,总有一些场景需要直接操作DOM,比如聚焦输入框、获取元素尺寸、集成第三方库等。如果不理解React的渲染机制就随意操作DOM,很容易出现状态与界面不同步、操作被覆盖、内存泄漏等问题。本文将从原理到实践,全面梳理React中DOM操作的注意事项和常见错误。

一文带你全面了解React DOM操作:常见问题与注意事项有哪些?

一、理解虚拟DOM与真实DOM的关系

React维护一棵虚拟DOM树,每次状态更新后会生成新的虚拟DOM树,再通过diff算法比较新旧两棵树的差异,最后把差异批量应用到真实DOM上。这个过程是异步批处理的,也就是说调用setState之后,真实DOM并不会立刻更新。很多初学者在setState之后立即用原生方法读取DOM属性,拿到的往往是旧值,这是最常见的错误之一。

正确的做法是使用生命周期钩子或钩子函数的依赖机制来等待渲染完成。类组件中可以使用componentDidUpdate,函数组件中可以使用useEffect,它们都在DOM更新之后执行。例如:

import { useEffect, useRef, useState } from 'react';

function Demo() {
  const [count, setCount] = useState(0);
  const boxRef = useRef(null);

  useEffect(() => {
    // useEffect 在渲染完成后执行,此时读取的才是最新 DOM
    console.log(boxRef.current.offsetHeight);
  }, [count]);

  return (
    <div ref={boxRef} onClick={() => setCount(count + 1)}>
      点击了 {count} 次
    </div>
  );
}

需要特别注意的是,在React 18的并发模式下,渲染过程可能被中断和恢复,任何在渲染期间直接修改DOM的做法都会带来不可预测的后果。操作DOM的代码应该集中放在副作用阶段,即useEffectuseLayoutEffect中,而不是渲染函数体内。

二、ref的正确使用方式与常见误区

ref是React提供的访问真实DOM的官方通道。创建ref用useRefReact.createRef,把ref挂到元素上之后,通过ref.current就能拿到对应的DOM节点。这里有几个容易出错的地方需要重点说明。

第一个误区是在渲染期间读写ref.current。渲染阶段应该是纯粹的,读写ref属于副作用,React严格模式下会执行两次渲染来暴露这类问题。第二个误区是忘记判断空值。ref在首次渲染完成前是null,条件渲染的元素在卸载后ref也会变回null,直接调用ref.current.focus()会直接报错,使用前必须判空。

function SearchBox() {
  const inputRef = useRef(null);

  useEffect(() => {
    // 条件渲染的元素可能不存在,必须判空
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, []);

  return <input ref={inputRef} type="text" placeholder="请输入关键词" />;
}

第三个误区是过度使用ref。如果某个效果可以通过状态和样式驱动实现,就不要手动操作DOM。比如显示隐藏元素,用状态控制className即可;只有聚焦、播放视频、读取布局信息、与Canvas交互这类状态驱动做不到的事情,才应该使用ref。另外,回调形式的ref如果写成内联函数,每次渲染都会以null和节点各调用一次,必要时用useCallback缓存回调,或者改用useRef

三、直接修改DOM引发的同步冲突

React默认通过diff来决定是否更新某个DOM节点。如果你绕过React直接修改了DOM的属性或内容,而React后续重新渲染时diff认为该节点没有变化,就不会更新它,你的修改会一直残留;反过来,如果diff认为需要更新,你的修改又会被覆盖。这种不确定性是很多诡异bug的来源。

典型例子是直接用innerHTMLinnerText改写React管理的元素内容。正确的替代方案有三种:一是把内容放进状态里让React渲染;二是把不希望React管理的部分放到dangerouslySetInnerHTML中,虽然它名字里带dangerously,但至少React知道这段内容是自己人改的,不会去diff它;三是把第三方库渲染的目标做成一个空容器,让React完全不去碰它内部的节点。

function RichText({ html }) {
  // React 不会对 dangerouslySetInnerHTML 的内容做 diff
  return <div dangerouslySetInnerHTML={{ __html: html }} />;
}

表单元素是另一个重灾区。如果一个<input>设置了value却没有提供onChange,输入框将无法输入,控制台会给出警告。如果既要受控又要临时改值,应通过修改状态而不是直接改DOM的value属性。同样,stopPropagation与React的合成事件系统交织时也要小心,React把事件委托到根容器上,在原生监听器里阻止冒泡可能拦不住React的处理器,必要时用e.nativeEvent.stopImmediatePropagation()处理。

四、性能与内存方面的注意事项

直接操作DOM还有一个隐性成本:频繁读写布局信息会触发强制重排。比如在循环中反复读取offsetHeight再修改样式,浏览器每次都要重新计算布局,性能会急剧下降。如果确实需要先读后写,应该把读取操作集中在一起,写入操作集中在后面,或者使用requestAnimationFrame把写入推迟到下一帧。

useEffect中绑定原生事件监听或创建定时器时,一定要在清理函数中解绑和销毁,否则组件卸载后监听器仍然引用着已卸载的DOM节点,造成内存泄漏。使用document.querySelector全局查找节点也是反模式,一旦同一个组件在页面上渲染多份,选择器就会命中错误的元素,永远优先使用ref。

function Modal({ onClose }) {
  useEffect(() => {
    const handleKey = (e) => {
      if (e.key === 'Escape') onClose();
    };
    window.addEventListener('keydown', handleKey);

    // 清理函数:组件卸载时移除监听,防止内存泄漏
    return () => window.removeEventListener('keydown', handleKey);
  }, [onClose]);

  return <div className="modal">弹窗内容</div>;
}

最后补充一个工具层面的建议:useLayoutEffectuseEffect的区别在于前者在浏览器绘制前同步执行,适合需要先测量DOM再决定样式的场景(例如提示框定位),后者在绘制后异步执行,绝大多数情况下优先用它,避免阻塞绘制。掌握这些原则后,在React中操作DOM既能解决问题,又不会破坏框架本身的渲染秩序。

React DOM操作虚拟DOMref修改时间:2026-08-31 22:50:40

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。