ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Web-Dev-For-Beginners 项目中的 Next.js 记忆翻牌游戏(Memory Game)实战指南

Web-Dev-For-Beginners 项目中的 Next.js 记忆翻牌游戏(Memory Game)实战指南 Web-Dev-For-Beginners 项目中的 Next.js 记忆翻牌游戏Memory Game实战指南【免费下载链接】Web-Dev-For-Beginners24 Lessons, 12 Weeks, Get Started as a Web Developer项目地址: https://gitcode.com/GitHub_Trending/we/Web-Dev-For-Beginners本篇技术指南以仓库memory-game/子项目的官方 README德语翻译版 及 英文原文为核心骨架系统讲解如何在 Web-Dev-For-Beginners 课程仓库中启动、修改、构建并部署一个基于 Next.js 的 React 记忆翻牌游戏Memory Game。读者读完本文后将掌握create-next-app生成的 Next.js 项目结构、开发服务器与构建流程、App Router 与客户端组件写法、next/font字体优化以及 Vercel 部署流程并通过MemoryGame.jsx源码深入理解该记忆游戏的翻牌匹配与胜负判定实现原理。项目定位Web 开发者入门课程中的 React 记忆游戏Web-Dev-For-Beginners 是一个面向 Web 开发初学者的开源课程仓库项目描述为 24 Lessons, 12 Weeks, Get Started as a Web Developer其中memory-game/子项目是一个用 Next.js 搭建、以 React 组件实现核心逻辑的记忆翻牌游戏。它由create-next-app中。该子项目的核心代码仅由三个部分构成结构非常清晰memory-game/app/page.jsApp Router 下的根页面负责渲染MemoryGame组件memory-game/app/layout.js根布局配置页面元信息与全局字体memory-game/components/MemoryGame.jsx记忆游戏的核心 React 客户端组件包含游戏初始化、翻牌、匹配判定、胜负判断等全部逻辑。从源码结构看该游戏通过page.js将交互逻辑收敛在MemoryGame单一组件中是学习「页面与组件分离」「客户端交互状态管理」的理想入门示例。快速启动运行开发服务器依据 memory-game/README.md英文原文与 translations/de/memory-game/README.md德语翻译版启动开发服务器的第一步是进入项目目录并安装依赖然后执行以下任一命令npm run dev # or yarn dev # or pnpm dev # or bun dev由于该项目的 memory-game/package.json 中dev脚本定义为next dev --turbopacknpm 运行时会自动携带 Turbopack 加速参数如需原生使用yarn/pnpm/bun仓库仅保留了与 npm 一致的脚本名包管理器按各自默认行为执行dev脚本即可。启动成功后在浏览器中打开 http://localhost:3000 即可看到记忆游戏界面。按照 README 的说明您可以修改app/page.js来编辑页面保存文件后页面会自动热更新Hot Reload。这意味着page.js的任何改动都会立即反映在浏览器中无需手动刷新或重启服务方便边改边看地学习 Next.js 页面渲染。修改入口页面从app/page.js开始README 明确指出编辑入口为app/page.js。我们来看该文件的实际内容memory-game/app/page.jsimport MemoryGame from /components/MemoryGame import React from react const page () { return ( div MemoryGame / /div ) } export default page几个值得注意的工程细节App Router 文件约定Next.js 的 App Router 约定app/page.js即根路由/对应的页面组件导出默认组件后即完成路由注册这是create-next-app生成项目的默认结构路径别名//components/MemoryGame中的由 memory-game/jsconfig.json 中的paths: { /*: [./*] }配置映射到项目根目录因此/components/MemoryGame等价于./components/MemoryGame避免深层相对路径的繁琐组件即页面页面本身没有业务逻辑只负责把交互组件挂载到页面上这种「薄页面 厚组件」的写法便于组件复用与测试。要实践 README 中的「编辑页面」指引最简单的方式就是修改这里的标题或外层容器例如把div换成带样式类名的容器、调整外层布局保存后即可在http://localhost:3000看到热更新效果。认识根布局与next/font字体优化README 特别提到本项目使用next/font来自动优化并加载 Geist —— Vercel 推出的新字体家族。对应实现位于 memory-game/app/layout.jsimport { Geist, Geist_Mono } from next/font/google; import ./globals.css; const geistSans Geist({ variable: --font-geist-sans, subsets: [latin], }); const geistMono Geist_Mono({ variable: --font-geist-mono, subsets: [latin], }); export const metadata { title: Memory Game, description: A fun and challenging memory game built with Next.js., }; export default function RootLayout({ children }) { return ( html langen body className{${geistSans.variable} ${geistMono.variable} antialiased} {children} /body /html ); }该文件同时承担了三项职责字体优化通过next/font/google引入 Geist 与 Geist_Mono并以 CSS 变量--font-geist-sans、--font-geist-mono的形式挂载到bodynext/font会在构建时自动做字体子集化subset、预加载与哈希处理避免传统font-face带来的额外网络请求与布局偏移全局元信息通过导出的metadata对象设置页面标题Memory Game与描述Next.js 会自动注入对应的title与meta namedescription对 SEO 友好全局样式与主题变量globals.css中memory-game/app/globals.css以import tailwindcss引入 Tailwind CSS v4并通过theme inline把字体变量映射为 Tailwind 的--font-sans/--font-mono同时定义了浅色/深色两种配色prefers-color-scheme: dark下背景切换为#0a0a0a、前景切换为#edededMemoryGame组件中大量使用的bg-gray-100、bg-blue-500、bg-green-500等工具类即来自此全局 Tailwind 配置。提示本项目中的globals.css、page.js、layout.js均位于memory-game/app/目录下根布局layout.js必须存在它是 App Router 渲染所有页面的外壳。深入源码记忆游戏的翻牌匹配与胜负判定虽然 README 是标准脚手架模板但仓库中真正体现该子项目主题的是 memory-game/components/MemoryGame.jsx。为了让文章信息密度高于原文档这里结合源码逐一拆解游戏的核心实现对应行号以当前仓库为准。组件声明与状态设计文件首行use client声明该组件为客户端组件使其可以在浏览器中管理交互状态随后引入了useEffect、useState、useCallback三个 React Hook。游戏共维护六类状态状态初始值作用gridSize2网格边长偶数2~10决定卡片总数array[]洗牌后的卡片数组每项含id与numberflipped[]当前已翻开但未完成匹配的卡片 idselectedPairs[]已成功配对的卡片 id 集合disabledfalse是否锁定点击等待匹配结果时置 truewonfalse是否已全部配对成功error网格尺寸非法时的提示文案洗牌算法Fisher-Yates源码第 6~13 行实现了经典的 Fisher-Yates费雪-耶茨洗牌算法代码注释明确标注「for unbiased randomization」用于无偏随机化function fisherYatesShuffle(array) { const arr array.slice(); for (let i arr.length - 1; i 0; i--) { const j Math.floor(Math.random() * (i 1)); [arr[i], arr[j]] [arr[j], arr[i]]; } return arr; }该算法从数组末尾向前遍历每次随机选取一个未处理位置并交换保证每种排列出现的概率相等无偏array.slice()复制一份避免原地修改破坏原数组引用。相比sort(() Math.random() - 0.5)这类简单做法Fisher-Yates 是更严谨的随机化方案。游戏初始化initializeGame第 36~53 行根据gridSize生成全部卡片const totalCards gridSize * gridSize; const pairCount totalCards / 2; const numbers [...Array(pairCount).keys()].map((n) n 1); const cardNumbers [...numbers, ...numbers]; const shuffledCardNumbers fisherYatesShuffle(cardNumbers); const shuffledCards shuffledCardNumbers.map((number, index) ({ id: index, number, }));逻辑要点gridSize × gridSize得到卡片总数除以 2 得到对数pairCount生成1..pairCount的数字序列并复制一份使每个数字恰好出现两次洗牌后为每张卡片分配唯一id与匹配值number。随后重置flipped、selectedPairs、disabled、won等状态。由于initializeGame被useCallback以gridSize为依赖缓存并通过useEffect在挂载及gridSize变化时自动调用因此修改网格尺寸或点击 Reset 按钮都会触发重新开局。翻牌与匹配判定handleClick第 74~92 行是点击交互的入口当disabled或won为真时直接返回防止连点与胜利后误操作flipped为空记录当前卡片为第一张翻牌flipped已有 1 张先setDisabled(true)锁定界面若点击的不是同一张卡则把第二张加入flipped并调用handleMatch(id)若是同一张卡则直接取消翻开。handleMatch第 59~72 行负责比对两张牌if (array[firstId].number array[secondId].number) { setSelectedPairs([...selectedPairs, firstId, secondId]); setFlipped([]); setDisabled(false); } else { setTimeout(() { setDisabled(false); setFlipped([]); }, 1000); }匹配成功把两张卡片的 id 加入selectedPairs永久保持翻开状态清空flipped并解除锁定匹配失败延迟 1000ms 后翻回清空flipped并解除锁定给玩家留出观察记忆的时间。卡片翻面状态由isFlipped/isSelectedPairs第 94~95 行计算只要卡片在flipped或selectedPairs中即视为翻开其中selectedPairs中的卡片渲染为绿色bg-green-500仅本次翻开的渲染为蓝色bg-blue-500未翻开显示灰色问号?源码第 130~144 行。网格列数通过内联样式gridTemplateColumns: repeat(${gridSize}, minmax(0,1fr))动态生成宽度取min(100%, gridSize * 5.5rem)以保证在大网格下自适应收缩。胜负判定与重置胜负判定第 97~101 行通过useEffect监听selectedPairs与arrayuseEffect(() { if (selectedPairs.length array.length array.length 0) { setWon(true); } }, [selectedPairs, array]);当已配对卡片数等于总卡片数时置won为 true页面顶部渲染绿色加粗的You Won!第 146~150 行。Reset 按钮第 152~157 行直接调用initializeGame实现一键重新开局。整个游戏不依赖任何外部状态管理库仅用 React Hook 完成全部交互闭环非常适合初学者通读。网格尺寸校验handleGridSize第 26~34 行在用户输入网格尺寸时做合法性校验要求2 size 10且size % 2 0偶数合法则更新gridSize并触发重新初始化非法则在界面下方以红色文字提示Please enter a grid size where size is even (e.g., 2, 4, 6, 8, 10)。输入框在 memory-game/components/MemoryGame.jsx 中设置了min2、max10属性与代码校验保持一致。构建、代码检查与生产运行除了dev之外memory-game/package.json 还提供了三个常用脚本scripts: { dev: next dev --turbopack, build: next build --turbopack, start: next start, lint: eslint }npm run build基于 Turbopack 执行生产构建。next/font的字体子集化、Tailwind CSS v4 的按需生成、JavaScript 打包与静态优化都在此阶段完成输出到.next/目录npm run start以生产模式启动服务器需先构建此时访问的是经过优化的生产产物npm run lint执行 ESLint 检查。规则来自 memory-game/eslint.config.mjs它通过FlatCompat继承next/core-web-vitals官方推荐规则并忽略node_modules/**、.next/**、out/**、build/**、next-env.d.ts等目录。依赖方面运行时依赖为react19.1.0、react-dom19.1.0 与next16.2.3开发依赖包含tailwindcss^4、tailwindcss/postcss^4PostCSS 插件由 memory-game/postcss.config.mjs 注册、eslint^9 与eslint-config-next。这些版本信息以仓库实际内容为准升级依赖时需自行验证兼容性。部署到 VercelREADME 的「Deployment auf Vercel」一节指出部署 Next.js 应用最简单的方式是使用由 Next.js 开发者打造的 Vercel 平台。部署前请确认已完成本地验证npm run build通过随后选择任一方式发布Vercel 一键导入访问 Vercel 的 New Project 页面导入本仓库或 memory-game 目录所在 Git 仓库Vercel 会自动识别 Next.js 项目、读取package.json中的build脚本并完成部署命令行部署安装 Vercel CLI 后在项目目录执行vercel按提示关联 Git 仓库与环境变量即可。默认部署由 Vercel 接管构建与静态资源托管无需自行配置服务器。更多细节可参考 Next.js 官方部署文档README 原文中给出的链接。注意create-next-app脚手架生成的 README 属于通用模板README 提到的字体、脚本、路由结构在本仓库memory-game/子项目中均已由 memory-game/app/layout.js、memory-game/app/page.js、memory-game/components/MemoryGame.jsx 等文件落实读者可以对照源码逐一验证。常见问题速查打开http://localhost:3000没有反应确认npm run dev已在memory-game/目录下运行且端口未被占用Turbopack 首次编译可能稍慢稍候刷新即可。修改了page.js不生效App Router 下app/page.js即根页面保存后应自动热更新若未生效检查是否改错了文件如误改components/MemoryGame.jsx但未刷新浏览器。输入网格尺寸报错网格边长必须是 2~10 之间的偶数2、4、6、8、10否则界面会显示红色错误提示且不会重新开局。/components/MemoryGame解析失败确认 memory-game/jsconfig.json 中paths配置了/*: [./*]该别名同时服务于 VS Code 智能提示与打包解析。npm run build失败优先运行npm run lint检查代码规范问题并确认node_modules依赖与package.json中记录的版本一致。至此您已经掌握了从启动开发服务器、编辑页面、理解next/font与 App Router 结构到深入MemoryGame.jsx源码理解翻牌匹配逻辑再到生产构建与 Vercel 部署的完整链路可以直接在这个入门课程仓库中动手实践并扩展自己的记忆游戏功能。【免费下载链接】Web-Dev-For-Beginners24 Lessons, 12 Weeks, Get Started as a Web Developer项目地址: https://gitcode.com/GitHub_Trending/we/Web-Dev-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表