本文记录了这个英雄联盟海克斯大乱斗攻略网站的完整开发思路、代码结构,以及如何把它部署到自己的云服务器上。适合有一点前端基础、想了解 Next.js 全栈开发流程的同学阅读。
一、项目是什么
这是一个英雄联盟「海克斯大乱斗」模式的攻略网站,主要功能有三个页面:
- 英雄图鉴:展示所有英雄,支持搜索和职业筛选,点击进入详情
- 英雄详情:展示英雄技能、推荐海克斯强化
- 海克斯强化:所有强化按强度(S/A/B/C)和稀有度筛选
- 出装推荐:热门英雄的推荐出装路线和胜率
技术栈:
- Next.js 16(React 19)
- TypeScript
- Tailwind CSS v4
- Riot Data Dragon API(拳头官方免费数据接口)
- pnpm 包管理器
二、项目结构一览
python
lol-web/
├── src/
│ ├── app/ # Next.js App Router 页面目录
│ │ ├── layout.tsx # 全局布局(导航栏)
│ │ ├── page.tsx # 首页:英雄图鉴
│ │ ├── globals.css # 全局样式(主题色变量)
│ │ ├── augments/
│ │ │ └── page.tsx # 海克斯强化页
│ │ ├── builds/
│ │ │ └── page.tsx # 出装推荐页
│ │ └── champions/
│ │ └── [id]/
│ │ └── page.tsx # 英雄详情页(动态路由)
│ ├── components/
│ │ └── ChampionGrid.tsx # 英雄网格组件(含搜索/筛选)
│ └── lib/
│ ├── ddragon.ts # Riot Data Dragon API 封装
│ └── augments.ts # 海克斯强化静态数据
├── package.json
├── pnpm-lock.yaml
├── postcss.config.mjs
└── tsconfig.json
三、核心概念:Next.js App Router
Next.js 13 之后引入了 App Router,和以前的 Pages Router 最大的区别是:
src/app/目录下每个文件夹代表一个路由- 文件夹里的
page.tsx就是这个路由的页面 - 默认所有组件都是服务端组件 (Server Component),可以直接
async/await请求数据 - 需要用到
useState、onClick等浏览器交互的组件,顶部加"use client"声明
这个项目里:
page.tsx(首页)、builds/page.tsx、champions/[id]/page.tsx都是服务端组件,直接在服务器上请求 Riot APIChampionGrid.tsx、augments/page.tsx是客户端组件,因为需要搜索框输入和按钮筛选的交互状态
四、数据来源:Riot Data Dragon
所有英雄数据来自拳头官方提供的免费静态资源接口,叫 Data Dragon,不需要任何 API Key。
封装在 src/lib/ddragon.ts:
ts
const DDRAGON_BASE = "https://ddragon.leagueoflegends.com";
// 获取最新版本号(如 "15.8.1")
export async function getLatestVersion(): Promise<string> {
const res = await fetch(`${DDRAGON_BASE}/api/versions.json`, {
next: { revalidate: 86400 }, // Next.js 缓存,每天刷新一次
});
const versions = await res.json();
return versions[0]; // 数组第一个就是最新版本
}
// 获取所有英雄列表(中文)
export async function getChampions() {
const version = await getLatestVersion();
const res = await fetch(
`${DDRAGON_BASE}/cdn/${version}/data/zh_CN/champion.json`,
{ next: { revalidate: 86400 } }
);
const data = await res.json();
return data.data; // 返回一个以英雄ID为key的对象
}
// 获取单个英雄详情(含技能、皮肤等)
export async function getChampionDetail(id: string) {
const version = await getLatestVersion();
const res = await fetch(
`${DDRAGON_BASE}/cdn/${version}/data/zh_CN/champion/${id}.json`,
{ next: { revalidate: 86400 } }
);
const data = await res.json();
return data.data[id];
}
next: { revalidate: 86400 } 是 Next.js 的**增量静态再生(ISR)**功能,意思是这个请求的结果会被缓存 24 小时,不会每次访问都重新请求 Riot 服务器,既快又省流量。
英雄图片的 URL 规则是:
ruby
https://ddragon.leagueoflegends.com/cdn/{版本号}/img/champion/{图片文件名}
五、页面逐一解析
1. 全局布局 layout.tsx
这是整个网站的外壳,所有页面都会套在这里面。包含:
- 顶部固定导航栏(
sticky top-0) - 三个导航链接:英雄 / 海克斯强化 / 出装推荐
{children}是各页面内容的插槽
主题色用了英雄联盟风格的金色 #c89b3c 和深色背景 #0a0e1a。
2. 首页 page.tsx + ChampionGrid.tsx
首页是服务端组件,启动时并行请求英雄列表和版本号:
ts
const [champions, version] = await Promise.all([
getChampions(),
getLatestVersion(),
]);
用 Promise.all 并行请求,比串行快一倍。
然后把数据传给客户端组件 ChampionGrid,由它负责搜索和筛选的交互逻辑。
ChampionGrid 里用 useState 管理搜索词和当前选中的职业标签,用 .filter() 实时过滤英雄列表,完全在前端完成,不需要再请求服务器。
3. 英雄详情页 champions/[id]/page.tsx
[id] 是 Next.js 的动态路由 ,方括号表示这是一个变量。访问 /champions/Jinx 时,id 就是 "Jinx"。
页面展示:
- 英雄头像、名字、称号、职业标签
- 被动技能 + Q/W/E/R 四个主动技能(图标 + 名称)
- 推荐海克斯强化(从静态数据里取 S/A 级强化)
如果英雄 ID 不存在,调用 Next.js 内置的 notFound() 自动跳转 404 页面。
4. 海克斯强化页 augments/page.tsx
纯客户端组件,数据来自本地静态文件 src/lib/augments.ts(手动维护的强化数据)。
支持两个维度的筛选:
- 强度等级:S / A / B / C
- 稀有度:棱彩 / 金色 / 银色
每个强化卡片根据稀有度显示不同的背景色(紫色/黄色/灰色)。
5. 出装推荐页 builds/page.tsx
服务端组件,出装数据是硬编码在文件里的静态数组(模拟高胜率数据)。
同时请求英雄列表来获取英雄头像,展示每个英雄的推荐出装顺序和推荐强化组合。
六、样式方案:Tailwind CSS v4
项目用的是 Tailwind CSS v4,和 v3 的主要区别是配置方式变了:
- 不再需要
tailwind.config.js - 直接在
globals.css里用@import "tailwindcss"引入 - CSS 变量直接在
:root里定义
css
/* globals.css */
@import "tailwindcss";
:root {
--gold: #c89b3c;
--gold-light: #f0e6d3;
--dark-bg: #0a0e1a;
--dark-card: #111827;
--dark-border: #1e2a3a;
}
body {
background-color: var(--dark-bg);
color: var(--gold-light);
}
在组件里直接用 Tailwind 工具类,颜色用方括号语法写自定义值,比如 text-[#c89b3c]、border-[#1e2a3a]。
七、部署到云服务器
前提条件
- 一台云服务器(阿里云/腾讯云/任意 VPS),系统推荐 Ubuntu 22.04
- 服务器已安装 Node.js(推荐 v20+)和 pnpm
- 本地已把项目打包成
lol-web.tar.gz
第一步:本地打包项目
在本地项目根目录执行:
bash
# 先构建生产版本,确保没有报错
pnpm build
# 打包整个项目(排除 node_modules 和 .next 缓存)
tar -czf lol-web.tar.gz \
--exclude=node_modules \
--exclude=.next \
--exclude=.git \
.
第二步:上传到服务器
bash
# 用 scp 上传(替换成你自己的服务器 IP 和用户名)
scp lol-web.tar.gz root@你的服务器IP:/home/www/
第三步:服务器上解压并安装依赖
SSH 登录服务器后:
bash
# 进入目标目录
cd /home/www
# 创建项目目录并解压
mkdir lol-web && tar -xzf lol-web.tar.gz -C lol-web
cd lol-web
# 安装依赖(如果服务器没有 pnpm,先装:npm i -g pnpm)
pnpm install
# 构建生产版本
pnpm build
第四步:用 PM2 保持进程常驻
直接 pnpm start 的问题是关掉终端进程就停了。用 PM2 可以让 Node 进程在后台持续运行,并在服务器重启后自动恢复。
bash
# 安装 PM2
npm i -g pm2
# 启动项目(端口默认 3000)
pm2 start pnpm --name "lol-web" -- start
# 设置开机自启
pm2 startup
pm2 save
常用 PM2 命令:
bash
pm2 list # 查看所有进程状态
pm2 logs lol-web # 查看日志
pm2 restart lol-web # 重启
pm2 stop lol-web # 停止
第五步:配置 Nginx 反向代理(可选但推荐)
Next.js 默认跑在 3000 端口,用 Nginx 做反向代理可以:
- 用 80/443 标准端口访问(不用在 URL 里加
:3000) - 配置 HTTPS(SSL 证书)
- 处理静态资源缓存
安装 Nginx:
bash
apt update && apt install nginx -y
创建配置文件 /etc/nginx/sites-available/lol-web:
nginx
server {
listen 80;
server_name 你的域名或IP;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
启用配置:
bash
ln -s /etc/nginx/sites-available/lol-web /etc/nginx/sites-enabled/
nginx -t # 测试配置是否正确
systemctl reload nginx
第六步:配置 HTTPS(如果有域名)
用 Certbot 免费申请 Let's Encrypt 证书:
bash
apt install certbot python3-certbot-nginx -y
certbot --nginx -d 你的域名
按提示操作,Certbot 会自动修改 Nginx 配置并配好 HTTPS,证书每 90 天自动续期。
八、更新部署流程
以后每次改了代码,重新部署的步骤:
bash
# 本地
pnpm build
tar -czf lol-web.tar.gz --exclude=node_modules --exclude=.next --exclude=.git .
scp lol-web.tar.gz root@服务器IP:/home/www/
# 服务器
cd /home/www
rm -rf lol-web && mkdir lol-web
tar -xzf lol-web.tar.gz -C lol-web
cd lol-web
pnpm install
pnpm build
pm2 restart lol-web
九、整体架构总结
markdown
用户浏览器
│
▼
Nginx(80/443端口)
│ 反向代理
▼
Next.js 服务(3000端口,PM2守护)
│
├── 服务端渲染页面(SSR/ISR)
│ │
│ ▼
│ Riot Data Dragon API
│ (英雄数据、图片资源)
│
└── 客户端交互组件
(搜索、筛选,纯前端)
整个项目没有自己的数据库,英雄数据全部来自 Riot 官方 API,强化和出装数据是本地静态维护的。Next.js 的 ISR 缓存机制保证了性能,不会每次请求都打到 Riot 服务器。