如何在Next.js中实现响应式移动端导航菜单?
解决Next.js响应式移动端导航的状态冲突问题
问题描述
我在Next.js中实现响应式移动端导航时遇到了麻烦,问题出在同时用JavaScript和CSS处理菜单切换状态。参考的React导航教程没考虑Next.js的CSS类名用法,纯React示例靠动态更新CSS,但Next.js里我用useState切换元素,导致没法通过媒体查询和切换按钮精细控制导航。目前用useState布尔值控制菜单显示,想让它只在780px及以下生效:初始设false的话整个菜单(包括切换按钮)都不渲染;设true的话移动端菜单自动展开,而且屏幕尺寸切换时菜单会被完全移除。怎么避免这些问题?或者怎么设置移动端菜单默认隐藏还能保留点击切换功能?
解决方案
核心思路是让CSS负责基于屏幕尺寸的布局控制,JavaScript只处理交互状态,不要用JS条件渲染菜单,而是通过动态添加/移除类名来控制显示,同时监听窗口尺寸变化同步状态。
步骤1:修改Nav组件代码
- 初始
isNavExpanded设为false,确保移动端菜单默认隐藏 - 移除条件渲染逻辑,改为动态绑定类名控制显示
- 添加窗口resize监听,屏幕宽度超过768px时重置导航状态,避免尺寸切换异常
修改后的组件代码:
import { useState, useEffect } from "react"; import Link from "next/link"; import SiteLogo from "../components/CompLogo"; import navStyles from "../styles/Nav.module.css"; const Nav = () => { const [isNavExpanded, setIsNavExpanded] = useState(false); // 监听窗口resize,超过768px时自动收起菜单 useEffect(() => { const handleResize = () => { if (window.innerWidth > 768) { setIsNavExpanded(false); } }; window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []); return ( <nav className={navStyles.nav}> <SiteLogo /> <button className={navStyles.hamburger} onClick={() => setIsNavExpanded(!isNavExpanded)} > <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" > <path fillRule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clipRule="evenodd" /> </svg> </button> <div className={navStyles.navigation}> {/* 用动态类名控制显示,不再依赖条件渲染 */} <ul className={`${navStyles.menu} ${isNavExpanded ? navStyles.expanded : ''}`}> <li className={navStyles.links}> <Link href="/"> <a className={navStyles.anchors}>Home</a> </Link> </li> <li className={navStyles.links}> <Link href="/map"> <a className={navStyles.anchors}>Map Page</a> </Link> </li> <li className={navStyles.links}> <Link href="/about"> <a className={navStyles.anchors}>About</a> </Link> </li> </ul> </div> </nav> ); }; export default Nav;
步骤2:调整CSS代码(Nav.module.css)
重点修改移动端菜单的默认状态,让它默认隐藏,仅在添加expanded类时显示:
/* 保留原有其他CSS,仅修改导航相关部分 */ .nav { background-color: white; color: black; width: 100%; display: flex; align-items: center; position: relative; padding: 0.5rem 0; /* 修复原代码中无效的padding语法 */ } .navigation { margin-left: auto; } .menu { display: flex; padding: 0; } .links { list-style-type: none; margin: 0 1rem; } .links a { text-decoration: none; display: block; width: 100%; } .hamburger { border: 0; height: 80px; width: 80px; padding: 0.5rem; border-radius: 50%; background-color: white; cursor: pointer; transition: background-color 0.2s ease-in-out; position: absolute; top: 50%; right: 5px; transform: translateY(-50%); display: none; } @media screen and (max-width: 768px) { .hamburger { display: block; } /* 移动端默认隐藏菜单 */ .menu { position: absolute; top: 146px; left: 0; flex-direction: column; width: 100%; height: calc(100vh - 147px); background-color: white; border-top: 1px solid black; display: none; } /* 仅添加expanded类时显示菜单 */ .menu.expanded { display: flex; } .links { text-align: center; margin: 0; } .links a { color: black; width: 100%; padding: 1.5rem 0; } .links:hover { background-color: #eee; } }
关键说明
- 放弃JS条件渲染:原代码的
{isNavExpanded && <ul>}会导致屏幕尺寸切换时菜单DOM被完全移除,改为动态类名后,菜单始终存在,仅通过CSS控制显示/隐藏,避免结构突变。 - 职责分离:CSS主导不同屏幕尺寸的默认布局,JS仅处理用户点击的交互状态,彻底解决状态冲突。
- resize监听:屏幕从移动端切换到桌面端时自动收起菜单,保证布局一致性。
- 修复语法错误:修正原CSS中
.nav的无效padding属性。
内容的提问来源于stack exchange,提问作者nizz0k
相关产品推荐
相关产品推荐

