React项目useEffect初始化AOS 首次加载不生效需刷新问题
问题产生原因
- React 18 开发环境默认开启的严格模式双调用机制是核心诱因:组件首次挂载时
useEffect执行触发AOS初始化,紧接着严格模式会模拟组件卸载、再重新挂载的流程,这个过程会打乱AOS内部维护的DOM节点监听、元素显隐状态标记逻辑,导致带data-aos属性的元素被错误设置为opacity:0,且没有绑定滚动触发逻辑,会一直保持隐藏状态。 useEffect的执行时机早于浏览器完成页面布局计算的节点,代码中在AOS.init()后同步调用AOS.refresh()时,浏览器还没算出DOM元素的实际位置、尺寸,AOS无法正确判定元素是否处于视口内,不会触发初始可见区域元素的入场动画。- 当前使用的
aos@3.0.0-beta.6版本本身没有对React 18的并发渲染做适配,初始化后不会自动感知React首次渲染完成的节点,只有当热更新触发DOM变动时,AOS内置的变动监听才会重新扫描节点、计算位置,动画才会正常加载。
修复方案
- 第一步:把AOS初始化逻辑上移到应用根组件(比如
App.jsx/main.jsx),保证全局只初始化一次,不要在单个业务组件里重复调用AOS.init(),避免事件监听重复绑定、状态错乱。根组件初始化参考代码:
import { useEffect } from 'react' import AOS from 'aos'; import 'aos/dist/aos.css'; import Portfolio from './Portfolio'; // 引入对应业务组件 function App() { useEffect(() => { // 加判断防止严格模式下重复初始化 if (AOS.initiated) return AOS.init({ duration: 1000, offset: 120, // 动画触发的提前偏移量,可按需调整 once: false // 配置为false则滚动出视口再回来会重复触发动画,按需调整 }) // 等浏览器完成布局绘制后再触发硬刷新,确保元素位置计算准确 requestAnimationFrame(() => { AOS.refreshHard() }) }, []) return <Portfolio /> }
- 第二步:调整业务组件的逻辑,移除组件内重复的AOS初始化代码。如果组件内有异步加载的内容(比如接口请求返回后才渲染的列表、文本),等内容渲染完成后再调用一次AOS刷新即可,参考代码:
import { useEffect } from 'react' import AOS from 'aos'; // 注意这里不需要重复引入aos.css,全局引入一次即可 export default function Portfolio() { // 同步渲染的静态内容不需要加这段useEffect // 只有异步加载内容时,才需要在数据加载完成后触发refresh // 举个例子:如果组件内content是接口返回的,就把content放到依赖数组里 // const [content, setContent] = useState(null) // useEffect(() => { // fetchData().then(res => setContent(res.data)) // }, []) useEffect(() => { requestAnimationFrame(() => { AOS.refresh() }) }, [/* 这里放异步数据的依赖项,比如content */]) return( <> <span className="bumper">Projects </span> <div data-aos="fade-in"> <h3>Hello!</h3> </div> </> ) }
- 临时验证方案:如果本地调试时仍有异常,可以先检查应用入口是否被
StrictMode包裹,暂时移除严格模式包裹可以快速验证问题是否由双调用机制导致,但不建议长期关闭严格模式,用上述初始化防重的方案更稳妥。
注意:生产环境下React不会触发严格模式的双调用逻辑,只要保证AOS全局只初始化一次、在布局完成后调用refresh,就不会出现首次加载动画不生效的问题。
内容的提问来源于stack exchange,提问作者Marko
相关产品推荐
相关产品推荐

