← FC Coder · HomePhase 02 · Lesson 24 · 90 min
Lesson24
Phase Two · Fantasy Builder · Matchday 24 · Winter Camp · Day 1

Kaggle 数据导入
15000 人进场

Today's 3 Jobs · 今天这三件事
  1. 01
    新建 scripts/import-kaggle.ts · 读 CSV 写 JSON
    Node 第一次进场
  2. 02
    pnpm tsx 跑一次 · 15000 人到位
    终端看 parsed N players
  3. 03
    🌟 .slice(0, 100) 性能戏 + 找哈兰德
    DOM 不能塞太多

🌟 寒假集训第 1 节 · 90 分钟。Phase 1 父子手写 22 人 · 今天进货 —— Node 脚本 + 一行命令 · 15000 人到位。Node 是工程的另一半:浏览器之外的赛场 · 跑脚本 · 读硬盘。

Concept · 第 1 个新概念

Node = 另一个赛场

浏览器跑给用户看 · Node 跑给程序员处理文件。

Chalk Board · 两个赛场
import { readFile, writeFile } from 'node:fs/promises';
// 浏览器没有 fs · 这只在 Node 跑
浏览器
用户点开网页 · DOM / React / 不能读硬盘
Node
程序员开终端 · 跑脚本 · 能读 / 写文件
今天
写 Node 脚本 · 把 CSV 变 JSON

`pnpm tsx scripts/import-kaggle.ts` —— 翻译机让 Node 直接跑 TS。

Concept · 第 2 个新概念

CSV → JSON · 字段映射 + 清洗

Kaggle 用 CSV · 我们用 JSON · 中间一段代码桥接。

Chalk Board · 字段映射
// CSV 解析:逗号分隔 · 引号包字段
"哈兰德","ST,LW","曼城" → 3 个字段(不是 4)
Kaggle
short_name, overall, player_positions, ...
我们的
name, rating, position, ...
清洗
位置取第 1 个 · 空俱乐部 → "Free Agent"

详细字段对照见 docs/ethics/kaggle-snapshot.md

Roster · 今天 3 个新工具

Node · pnpm tsx · CSV → JSON

抄着用 · Step 8 看到 parsed N players 是今天的「哇」。

  1. 01
    Node.js
    另一个赛场 —— 跑 JS / TS 但不在浏览器 · 在终端 · 能读硬盘。
    浏览器读不了硬盘是安全设计。Node 给程序员用。
  2. 02
    pnpm tsx file.ts
    翻译机 —— Node 不认 TS · tsx 直接跑(不用先编译)。
    2024 起业内共识 · 比 ts-node 更快。
  3. 03
    CSV → JSON
    清洗 —— 逗号分隔的表格 · 字段映射 + 默认值。
    短名 / 评分 / 位置 / 国家 / 俱乐部。NULL → "Free Agent"。
Half 2 · 在屏幕上(集训 · 90 min)

脚本骨架 · 解析 · 跑一次 · 接到 fantasy

做完一步就点 ✓。Step 8 终端 parsed 那一刻是今天的「哇」。

01脚本骨架 · 读文件
01Min

确认 .local/kaggle/players.csv 已在

爸爸提前 1 周从 Kaggle 下好。Cursor 左树看到(.local 灰色 · gitignore 挡)。
02Min

新建 scripts/import-kaggle.ts

右键 → New Folder → scripts · 里面 New File → import-kaggle.ts。第一行 #!/usr/bin/env tsx(可选 · 仪式感)。
03Min

import readFile / writeFile / resolve · 写 Player type

import { readFile, writeFile } from 'node:fs/promises'; import { resolve } from 'node:path'; type Player = { name, rating, position, nationality, club: string }。
04Min

写 main 骨架 · pnpm tsx 跑一次 · 看 chars 数

const CSV_PATH = process.argv[2] ?? '.local/kaggle/players.csv'; async function main() { const text = await readFile(resolve(process.cwd(), CSV_PATH), 'utf-8'); console.log(`read ${text.length} chars`); } main().catch(...)。终端 pnpm tsx scripts/import-kaggle.ts · 看到 read 18M+ chars。
02CSV 解析 + 字段映射
05Min

抄 parseCsv 函数(50 行 · 处理引号)

课文里的整段抄进去 · 别展开。本质点是脚本跑通 · 不是 parser 实现。Phase 3 用 papaparse 库就免抄。
06Min

替换 main · 用 parseCsv + 字段映射

header = rows[0]; idx = { short_name, overall, player_positions, nationality_name, club_name } 各 indexOf。for (r = 1; ...) 读每行 · 清洗:position split(',')[0] · club 空 → 'Free Agent' · rating Number()。NaN / 缺名字 跳过。
07Min

writeFile 写到 app/data/players-full.json

await writeFile(resolve(process.cwd(), 'app/data/players-full.json'), JSON.stringify(players, null, 2), 'utf-8')。console.log 出 parsed N players + wrote 路径。
03🌟 跑一次 · 15000 人
08Min

🌟 pnpm tsx scripts/import-kaggle.ts

终端跑。应该看到 parsed 15000+ players + wrote app/data/players-full.json。今天的「哇」。
09Min

Cursor 打开 app/data/players-full.json · 看前几行

几 MB 文件 · 打开稍慢。看前几行是不是 [{ name, rating, position, ... }, ...]。注意:全英文。
04性能戏 + 找哈兰德
10Min

lib/players.ts 加 playersFull + playersTop100

import fullData from '@/app/data/players-full.json'; export const playersFull: Player[] = fullData as Player[]; export const playersTop100 = playersFull.slice().sort((a,b) => b.rating - a.rating).slice(0, 100)。
11Min

fantasy/page.tsx 改用 playersTop100

import { playersTop100 as players } from '@/lib/players' 替原来的 import { players } from ...。刷新 /fantasy 看 100 张顶级球员卡。顺畅。
12Min

在 players-full.json 搜 Haaland

Cursor Cmd+F 在 JSON 里搜 Haaland(英文)。E. Haaland · 91 · ST · Norway · Manchester City。
13Min

(可选)临时切到 playersFull 看卡顿

改 import 用 playersFull · 浏览器卡 1-3 秒。看清楚 DOM 不能塞太多。改回 playersTop100。Phase 3+ 虚拟列表才解。
05截图 + 收尾
14Min

📸 截图战利品

终端 parsed 15234 + Cursor 打开 players-full.json 头几行。存 2026-XX-XX-15000人进场.png · Phase 2 第九张。
Half Time · 中场 · 讲给爸爸听

4 题 · 重点:两个赛场

第 1 题答得出 = Node 心智到了。

01Node 和浏览器差在哪?为什么浏览器不能直接读硬盘文件?Hint ↓

两个赛场。浏览器读不了硬盘是安全设计 —— 不然任何网站都能偷你电脑文件。

02CSV 为什么要双引号?给一个例子。Hint ↓

字段里有逗号 · 用引号包起来不被错切。如 'ST,LW' 包成一个字段不被切成两个。

03pnpm tsx 是干什么的?Hint ↓

翻译机 —— 直接跑 TS · 不用先编译成 JS。Node 自己不认 TS · tsx 帮翻译。

04为什么我们不把 players-full.json commit 进仓库?Hint ↓

大 + 衍生品。脚本 + 数据来源记录足够 · 每台机器自跑。仓库干净。

Player Rating · 本课温度计

给「15000 人进场」打个分

说真话。爸爸会看到。

今天难度Difficulty
0
今天开心Fun
0
Final Whistle · 终场哨

工程世界另一半开门 —— 一个命令 · 15000 人到位。

还有 14 步没打勾。Step 8 终端 parsed N players 那一刻今天就过。剩下下次集训日补。