项目全解析
构建、部署与性能优化
就说从 npm run build 到生产服务器,完整走一遍构建流程,再讲性能优化的法子
- 理解 Next.js 静态导出的配置
- 掌握自定义静态服务器的实现
- 就盯着性能优化的关键指标和手段学。
- 了解 PWA 和离线缓存策略
构建流程:next build
Python
# Next.js 构建流程
print("""
1. TypeScript 类型检查(tsc --noEmit)
→ 零报错才能继续,strict 模式全开
2. 路由收集
→ 扫描 app/ 目录,识别所有页面
→ /course/[id] 是动态路由
3. 静态生成(SSG)
→ generateStaticParams() 返回所有课程 ID
→ 每个课程页预渲染为 HTML(318 个页面)
4. 代码分割
→ 每个页面独立 chunk
→ Monaco Editor 是最大的 chunk(~2MB)
5. 样式提取
→ Tailwind 只生成用到的 CSS(通常 < 20KB)
6. 输出到 out/ 目录
→ 纯静态文件,可部署到任何静态服务器
""")next.config 配置
JavaScript
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "export", // 静态导出
images: { unoptimized: true },
trailingSlash: true,
};
/*
* 为什么用 output: "export"?
* 1. 生成纯静态文件,双击 index.html 也能打开
* 2. 可部署到 GitHub Pages、Nginx、任意 CDN
* 3. 自带 server.js 提供更好的静态服务
*
* 代价:不能用 API Routes(但我们用 Pyodide,不需要后端)
*/生产服务器:server.js
JavaScript
// server.js 核心功能
const http = require("http");
const fs = require("fs");
const zlib = require("zlib");
// 1. MIME 类型映射(.wasm, .apk 等)
// 2. gzip/brotli 压缩(减少 70% 传输体积)
// 3. Range 请求支持(大文件断点续传)
// 4. SPA 回退(找不到文件返回 index.html)
// 5. 安全头(X-Content-Type-Options, X-Frame-Options)
// 为什么不用 next start?
// next start 需要运行 Next.js 服务器,但我们是静态导出。
// 自定义 server.js 零依赖(只用 Node 内置模块),
// 支持 APK 下载、WASM 文件、局域网访问(手机扫码学习)。性能优化策略
Python
# 朋友好学性能优化清单
print("""
首屏加载(LCP < 2.5s)
├── 静态预渲染:所有页面 SSG,HTML 直出
├── 字体优化:next/font 自托管,无 FOUT
├── CSS 内联:关键 CSS 在 <head> 中
└── 代码分割:Monaco/Pyodide 懒加载
运行时性能
├── Pyodide 在 Web Worker 中(不阻塞主线程)
├── React 并发渲染
├── CSS 变量主题切换(零 JS 开销)
└── requestAnimationFrame 节流动画
加载策略
├── Pyodide 懒加载(第一次运行代码时才加载)
├── Monaco Editor 动态 import
├── Service Worker 缓存已加载资源
├── gzip/brotli 压缩
└── HTTP 缓存头(immutable, max-age=31536000)
""")🐍懒加载的艺术
Pyodide 是最大的资源,约10MB,但不是每个用户都会运行代码,所以在用户第一次点击「运行」时才加载。加载期间显示进度条。Monaco Editor 也用 next/dynamic 懒加载。就这么简单,把资源加载推迟到用户需要的那一刻。
PWA 与离线缓存
JavaScript
// Service Worker 缓存策略
// install: 预缓存核心资源
// fetch: 缓存优先,网络回退
self.addEventListener("fetch", (e) => {
e.respondWith(
caches.match(e.request).then((cached) => {
if (cached) return cached; // 缓存命中:离线可用
return fetch(e.request).then((response) => {
if (response.ok && e.request.method === "GET") {
const clone = response.clone();
caches.open(CACHE_NAME).then((cache) =>
cache.put(e.request, clone)
);
}
return response;
});
})
);
});
/*
* 缓存策略:
* - HTML/导航:网络优先(确保更新)
* - CSS/JS/字体:缓存优先(文件名带 hash,永久缓存)
* - Pyodide WASM:缓存优先(文件大,不常变)
*/一键启动脚本
Shell
:: start.bat
@echo off
cd /d "%~dp0"
if not exist "node_modules" ( call npm install )
if not exist "out" ( call npm run build )
start http://127.0.0.1:5173
node server.js
pause选择题
朋友好学为什么用 output: 'export' 静态导出?
选择题
Pyodide(约 10MB)为什么要懒加载而不是首屏就加载?
资深工程师加餐
底层原理 · 大厂视角 · 工程经验,点卡片展开
千万别说「没什么难点」。按这条链讲:现象 → 你如何定位(日志、数据、复现)→ 根因 → 有哪些候选方案、各自代价 → 你最终怎么选、放弃了什么 → 上线后的量化收益。它展示的是解决未知问题的方法论,而这正是区分「会写代码」和「能扛事」的关键,也是高级岗面试的核心考点。
挑战任务
判断模块是否该懒加载
简单+50 XP
懒加载的判断标准:体积大、且不是首屏立即需要的资源才推迟加载。写 should_lazy(size_kb, needed_on_first_screen) 这个函数:体积超过500KB,且首屏不需要时返回True,否则返回False。
判断模块是否该懒加载
Python 0%
1 个测试用例