MoreRSS

site iconLiHan | 李寒修改

研究生,中国传媒大学
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

LiHan | 李寒的 RSS 预览

【本文为 AI 生成】Hugo + Pjax 实现无刷新博客体验:从音乐播放中断谈起

2026-03-11 14:23:00

Featured image of post 【本文为 AI 生成】Hugo + Pjax 实现无刷新博客体验:从音乐播放中断谈起

前言

在搭建个人博客的过程中,我一直有一个执念:希望网页底部的音乐播放器能够像网易云音乐那样,在页面切换时永不中断

传统的静态博客(如 Hugo 生成的站点)每一次点击链接,浏览器都会重新加载整个页面 (Full Page Reload)。这意味着:

  1. DOM 树被销毁重建。
  2. 所有 JavaScript 状态丢失。
  3. 音频/视频标签被重置 —— 这就是为什么音乐会停。

为了解决这个问题,我们需要引入 SPA (Single Page Application) 的概念,或者更轻量级的方案 —— Pjax (PushState + Ajax)

核心技术方案:Pjax

Pjax 的工作原理非常直观:

  1. 拦截 <a> 标签的点击事件。
  2. 使用 Ajax 请求新页面的 HTML 内容。
  3. 解析新 HTML,只提取我们需要更新的部分(例如主要内容区 .main-container)。
  4. 使用 history.pushState 修改浏览器的 URL地址栏,使其看起来像正常跳转。
  5. 替换 DOM 中的内容区。

通过这种方式,页脚 (Footer)侧边栏 (Sidebar) 可以保持不变,驻留在其中的音乐播放器自然也就不会中断了。

1. 引入 Pjax

首先在 <head> 中引入 Pjax 库(推荐使用 pjax 库而非老旧的 jquery-pjax):

1
<script src="https://cdn.jsdelivr.net/npm/pjax/pjax.min.js"></script>

2. 定制化配置 (The Tricky Part)

这是最关键的一步。为了保证 Stack 主题的正常渲染,如果你直接替换整个 body,播放器还是会挂掉。我们需要精准打击

我在 layouts/partials/head/custom.html 中进行了如下配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
var pjax = new Pjax({
 selectors: [
 "title",
 ".main-container", // 只替换内容区!
 "body" // 这里的处理很有讲究,见下文
 ],
 switches: {
 "body": function(oldEl, newEl, options) {
 // 我们只更新 body 的 class (用于切换暗色模式或页面特定样式)
 // 绝不替换 body 的 innerHTML,否则页脚脚本会被杀掉
 oldEl.className = newEl.className;
 },
 ".main-container": Pjax.switches.innerHTML, // 标准替换
 "title": Pjax.switches.outerHTML
 }
});

关键点:不仅要通过 CSS 选择器指定更新区域,还要自定义 switch 函数,确保 body 标签只更新属性而不重置内容。

踩坑与填坑

实现 Pjax 只是第一步,真正的挑战在于副作用

坑一:脚本不执行 (Mastodon 动态消失)

现象:跳转到 Timeline 页面,Mastodon 动态加载不出来。 原因:通过 innerHTML 插入的 HTML 片段中如果包含 <script> 标签,浏览器出于安全和规范考虑,通常不会执行它们

解决方案: 我们将初始化代码封装为全局函数,并在 Pjax 完成事件 (pjax:complete) 中手动调用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Check & Init Mastodon Logic
window.initMastodon = function() {
 if (!document.getElementById('mt-container')) return;
 // ... 初始化代码 ...
};

// 监听 Pjax 完成
document.addEventListener('pjax:complete', function () {
 window.initMastodon();
 // 其他需要重载的脚本,如 Google Analytics
 if (typeof gtag === 'function') {
 gtag('config', 'MEASUREMENT_ID', {'page_path': location.pathname});
 }
});

坑二:播放器状态丢失

现象:虽然使用了 Pjax,但用户有时会习惯性按 F5 刷新,或者 Pjax 请求超时回退到普通跳转,这时候音乐还是会断,且进度归零。

解决方案:状态持久化 (State Persistence)。

利用 localStorage 在播放器每秒更新时记录状态:

1
2
3
4
5
6
7
setInterval(() => {
 if (!ap.audio.paused) {
 localStorage.setItem('aplayer_time', ap.audio.currentTime);
 localStorage.setItem('aplayer_index', ap.list.index);
 localStorage.setItem('aplayer_paused', 'false');
 }
}, 1000);

在页面加载时(无论是 Pjax 还是普通加载),尝试恢复状态:

1
2
3
4
5
const savedTime = localStorage.getItem('aplayer_time');
if (savedTime) {
 ap.seek(parseFloat(savedTime));
 if (savedPaused === 'false') ap.play();
}

这里还有一个细节:audio 元素必须在元数据加载后才能 seek,所以需要监听 loadedmetadatacanplay 事件。

总结

通过引入 Pjax 并配合精细的生命周期管理,我们成功在静态博客上实现了类似 SPA 的流畅体验:

  1. 音乐不间断:Footer 区域脱离了页面刷新的生命周期。
  2. 加载极速:只请求部分 HTML,带宽消耗更低。
  3. 体验降级:即使 Pjax 失败,完善的状态恢复机制也能保证用户体验不割裂。

折腾博客的乐趣往往不在于写文章本身,而在于通过解决这些具体的技术问题,窥探现代 Web 开发的冰山一角。

面向外企就业的 LeetCode/NeetCode 记录

2026-03-10 10:24:00

Featured image of post 面向外企就业的 LeetCode/NeetCode 记录

LeetCode 刷题方法

路线图

算法清单

外企基石,必须闭眼手撕:

  • 哈希表
  • 双指针
  • 滑动窗口
  • 二分查找
  • 链表
  • 二叉树(含二叉搜索树 BST)

工程进阶,拉开差距的关键:

  • 回溯算法(排列/组合/子集)
  • 堆/优先队列
  • 栈与队列
  • 图论基础(仅限 BFS / DFS / 拓扑排序)

高级护城河,面试冲刺期再看:

  • 动态规划(仅限 1D DP 和基础背包/最长公共子序列)
  • 字典树 (Trie)
  • 并查集 (Union-Find)
  • 前缀和技巧

题单

设计模式

1. 单例模式 (Singleton) —— C++ 面试必考八股

  • 这是什么:保证一个类只有一个实例,并提供一个全局访问点。
  • 外企考点:不仅会让你手写,还会结合 C++ 问你“线程安全的单例怎么写?”(Meyers Singleton,利用局部静态变量的特性)。
  • 如何融合进你的项目:你的 GPU 监控探针(Agent)运行在 5090 服务器上,它需要读取配置信息(比如本机的 IP、监控频率)。这个 ConfigManager 配置管理器,就必须是一个单例。你在简历上面试时可以说:“为了避免多次读取配置文件造成 I/O 浪费,我用 C++11 的 std::call_once(或局部静态变量)实现了一个线程安全的单例配置中心。”

2. 观察者模式 (Observer) —— 监控系统的灵魂

  • 这是什么:一个对象状态改变,所有依赖它的对象都会收到通知。(也就是发布-订阅机制)。
  • 外企考点:解耦。面试官常问:“如果我加一个新的监控维度,你的代码需要大改吗?”
  • 如何融合进你的项目:你的 5070 Ti 是一直在变化的(温度忽高忽低,显存忽大忽小)。你可以把本地的显卡作为一个 Subject(被观察者),把负责发送网络请求的模块、负责写本地日志的模块作为 Observer(观察者)。显存一旦超过阈值,主动“通知”这些模块报警,而不是让它们写个 while(true) 死循环去轮询。

3. 工厂模式 (Factory) —— 告别满屏的 if-else

  • 这是什么:把创建对象的具体逻辑隐藏起来,提供一个统一的接口。
  • 外企考点:考察你对开闭原则(对扩展开放,对修改封闭)的理解。
  • 如何融合进你的项目:你的调度系统未来不仅能收集 5090 的数据,可能还要收集 AMD 显卡,甚至普通 CPU 的数据。写一个 DeviceFactory,传入 “NVIDIA”,它就吐出一个调用 NVML API 的对象;传入 “CPU”,它就吐出一个读取 /proc/cpuinfo 的对象。

4. 策略模式 (Strategy) —— 调度系统的核心

  • 这是什么:定义一系列算法,把它们一个个封装起来,并且使它们可相互替换。
  • 外企考点:极其高频的设计题。比如“设计一个打车软件的计费模块”。
  • 如何融合进你的项目:你的核心是算力调度。怎么调度?你可以有“轮询策略(Round Robin)”、“最低显存优先策略(Least Loaded)”。把这些策略抽象成接口,运行时动态插拔。这就是顶级的基础设施架构。

刷题记录

NeetCode Roadmap

Contains Duplicate (LeetCode 217)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 最经典的哈希表题。用一个 Set 来记录我们见过的数字,一边遍历一边查这个数字在不在 Set 里。
class Solution:
 def containsDuplicate(self, nums: List[int]) -> bool:
 seen = set() # 创建一个空的哈希集合

 for num in nums:
 if num in seen: # 查字典:这个数字我之前见过吗?
 return True # 见过!直接返回 True
 seen.add(num) # 没见过,把它记在小本本上

 return False # 全都查完了也没重复,返回 False
1
2
3
4
# Pythonic 的一行解法,利用 set() 去重特性,直接比较原数组长度和去重后 Set 的长度。
class Solution:
 def containsDuplicate(self, nums: List[int]) -> bool:
 return len(nums) != len(set(nums))
time/space complexity
  • time complexity: $O(n)$
  • space complexity: $O(n)$
  • 如果你的数组非常大,且重复元素大概率出现在很靠前的位置,解法 1 更优,因为它能“及时止损”。
  • 如果你的数组没有重复元素,或者重复元素在最后面,解法 2 更优,因为底层 C 语言的执行速度会碾压 Python 的 for 循环。
summary:
  1. 时间复杂度 (Time Complexity)
  • $O(1)$ 常数级:终极目标。代码特征:直接通过索引取值 nums[0],或者在字典/集合中查值 if key in my_dict:
  • $O(\log n)$ 对数级:极快。代码特征:二分查找。每次操作砍掉一半(如 while left < right: 配合 mid)。
  • $O(n)$ 线性级:外企最爱的标准解法。代码特征:一层无嵌套的 for 循环,从头到尾扫一遍。
  • $O(n \log n)$:通常是排序的极限。代码特征:代码里调用了 nums.sort(),或者用了归并/快速排序。
  • $O(n^2)$ 平方级:面试危险信号。代码特征:两层嵌套的 for 循环。面试官通常会让你把它优化到 $O(n)$。
  1. 空间复杂度 (Space Complexity)
  • $O(1)$:只新建了几个变量(如双指针 left, right),没有开辟随数据规模增大的新空间。
  • $O(n)$:新建了一个和原数组一样大的字典(哈希表)、集合(Set)或新数组。外企极度喜欢用空间换时间。
  1. Hash (Dict / Set) 用法速查

底层皆为哈希表,查找/插入的时间复杂度均摊为 $O(1)$。

  • dict (字典):存键值对。

  • 初始化:d = {}

  • 增/改:d[key] = value

  • 查:if key in d:

  • set (集合):存无序不重复元素。

  • 初始化:s = set()

  • 增:s.add(value)

  • 查:if value in s:

  1. Pythonic 循环

抛弃 for(int i=0; i<n; i++),只记以下三种:

  • 只取值for num in nums:
  • 只取索引for i in range(len(nums)):
  • 既要索引又要值for i, num in enumerate(nums):
  1. 核心标准库 (绝不重复造轮子)
  • collections.Counter (计数器)
  • 场景:完成词频/元素频率统计。直接替代 count[i] = count[i] + 1 循环。
  • 用法
1
2
from collections import Counter
count = Counter(nums)
  • collections.deque (双端队列)
  • 场景:做 BFS(广度优先搜索)必用。两头增删都是 $O(1)$,绝不用普通 list 做队列。
  • 用法
1
2
3
4
from collections import deque
q = deque([1, 2])
q.append(3) # 尾部进队
q.popleft() # 头部出队
  • heapq (优先队列 / 堆)
  • 场景:解决 “Top K” (前K个最大/最小) 问题。替代 C++ 的 std::priority_queue
  • 用法
1
2
3
4
import heapq
heapq.heapify(nums) # $O(n)$ 原地建堆
heapq.heappush(nums, 2) # 压入元素
val = heapq.heappop(nums) # 弹出最小值

words list

  • integer 整数
  • distinct 不同的
  • duplicate 重复的
  • constraint 约束条件
  • approach 方法
  • time/space complexity 时间/空间复杂度
  • valid 有效的
  • anagram 字谜(回文构筑法)

tips

1. 语言与阵地

  • 语言死绑 Python:追求极简 API 和开发速度,避开 C++ 繁琐的模板代码。
  • 平台死绑美区:只在 LeetCode.com 刷,强迫适应全英文题目与约束条件。

2. 路线与开销

  • 唯一指定路线:只刷 NeetCode Roadmap。打通它 = 自动通关 Blind 75 + NeetCode 150。
  • 绝不打周赛:外企不考竞赛级偏门题,别浪费周末断联日。
  • 零氪金原则:绝不买 $297 NeetCode Premium。只在收到面试通知的前一个月,买 $35 LeetCode 官方会员突击公司专属题库 (Company Tags)。

3. 核心刷题 SOP (15分钟法则)

  • Step 1 (闭卷 15 分钟):拿到题自己想,没思路或写不出立刻停手,绝生死磕。
  • Step 2 (开卷偷师):看 NeetCode 免费视频,学 Python 模板和英文思路 (Think Out Loud)。
  • Step 3 (半闭卷手敲):关掉视频,凭记忆自己敲到 AC。允许随时查 Python API(如字典操作、内置函数),现阶段不要为默写语法浪费时间。

4. 兜底退路与国内大厂补丁 (Pivot 方案)

  • 唯一需要加练的(考前 1 个月突击):外企不考中间件的死记硬背。但如果在 2027 年春招/秋招前决定转面国内互联网业务岗,需额外花 1个月 狂背“国产特色八股文”:MySQL 底层(B+树索引)、Redis(缓存穿透/雪崩)、以及各类高并发锁机制。 仅作为临时补丁,勿在日常备战中分散精力。

5. NeetCode 的正确打开方式

NeetCode 的 Visualize the algorithm step by step 功能非常便于理解算法流程,至少对我来说,可视化地过一遍算法流程,就很清晰了。

链接

2027秋招外企计划

2026-03-06 13:50:00

Featured image of post 2027秋招外企计划

前言

经历了一学期的研究生生活,lihan 变得保守了很多,也不再执着于读博与当教授,外企的技术岗是当前的首选目标了。兼顾当前课业与课题组压力以及身心健康,制定了一个目标明确的学习计划,计划的核心是提升自己的技术能力,兼顾项目开发与实习经历以及英语能力,以健康的身体和积极的心态迎接秋招。

计划 ———— Lihan 的外企核心 SDE 备战「自动机」执行纲领

🎯 终极目标 (The North Star): 2027年秋招/暑期实习斩获一线外企(Microsoft, Amazon, NVIDIA等)核心 Infra/后端 开发岗 Offer,实现 965 工作制、高时薪,拥有绝对的生活主导权。


📅 第一部分:宏观路由与异常接管 (Macro Roadmap)

主线任务由算法与 C++/AI Infra 工程双轮驱动。前端开发仅作为前置 3 天的工具测试,不占主线资源。

📌 前置点火任务:MVP 启动冲刺 (限时 3 天)

  • 任务:借助 AI IDE 快速完成一个基于 Next.js 的 Todo WebApp,仅用于记录本计划的状态流转。
  • 强制约束:限时 3 天。绝不系统性学习官方文档,超时一分钟也必须强制封板,立刻转入算法与 C++ 主线。

🗺️ 两年半推进时间线

  • 阶段一:基建与探路期 (研一下,至 2026 年 8 月)

  • 算法:Python 刷通 LeetCode 经典 150 题(按分类),训练全英文“边写边说”能力。

  • 工程:启动「异构 GPU 算力监控系统」。用 C++ 配合 NVML 写出轻量级数据采集探针,跑通本地与远端 5090 服务器的底层硬件状态拉取。

  • 🛑 异常接管 (期末突击):学期末提前划出 3 周 纯粹用于期末复习。此期间【高能输出】状态冻结,一切为了保住 GPA,考完立刻解冻。

  • 阶段二:并发与重构攻坚期 (研二上,2026.09 - 2027.02)

  • 算法:二刷错题本,定向突击目标外企高频题库。

  • 工程:引入 Python FastAPI 或 Go 重构通信网关,解决跨网络并发与数据持久化。

  • 🛑 异常接管:同样预留 3 周 应对研二上期末考试。

  • 阶段三:狙击与收网期 (研二下,2027.03 - 2027.09)

  • 动作:精修全英文简历,开启 BQ(行为面试)的英文 Mock Interview。海投外企暑期实习。

  • 终局:依靠 3 个月的暑期实习斩获 Return Offer,或携极具深度的 C++ 底层项目降维打击秋招。


⚙️ 第二部分:核心状态机引擎 (State Machine Logic)

你的每一天只会处于以下五种状态之一,绝不允许出现“边学边玩”的模糊中间态。

  • 🟢 状态 A [高能输出]: 全神贯注执行算法刷题与 C++/Python 硬核底层开发。断绝外部通讯。
  • 🔵 状态 B [防御敷衍]: 处理导师横向任务与学校日常课程。大脑降频,能水则水,绝不多花一分精力。
  • 🟡 状态 C [合法狂欢]: 当日设定的【高能输出】任务达标后自动触发。毫无负罪感地打游戏、看剧。
  • 🟣 状态 D [战略停机]: 提前规划好的不插电日(如周日)。去山里徒步摄影,彻底抽离。禁止临时起意的旷工。
  • 🔴 状态 E [兜底模式]: 极度疲惫时触发。完成“最小可行性任务”(如盲打 1 道 LeetCode 简单题)后直接睡觉,保持惯性不断裂。

⏳ 第三部分:每日资源调度表 (Daily Scheduler 8:30-24:00)

针对每周只有四节课的现状,将上课时间直接视为【🔵 防御敷衍】模块。如果在课上,则挂机听讲,大脑后台构思代码或复习计网/OS理论。

时间块 状态机 核心执行逻辑与输入/输出 时长
08:30 - 09:30 系统冷开机 洗漱、早餐。 浏览开源社区或科技资讯,平缓启动大脑。 1.0h
09:30 - 11:30 🟢 高能输出 算法与理论底座 (Python/CS理论)
精力峰值时段。刷 1-2 道 LeetCode,强制英文口述思路。 2.0h
11:30 - 14:00 物理断电 午餐 + 深度午休。 雷打不动睡 40-60 分钟。 2.5h
14:00 - 17:00 🟢 高能输出 AI Infra 底层工程实战 (C++)
操作本地高配机器与远端服务器,死磕内存管理与网络并发。 3.0h
17:00 - 19:00 硬件维护 体能训练与晚餐。 健身房或操场,切换轨道,缓解颈椎压力。 2.0h
19:00 - 21:30 🔵 防御敷衍 导师横向与学校课业
降维处理杂活与作业。到点准时停手。(若此时在上课,则此模块平移至上课时段) 2.5h
21:30 - 23:00 🟢 / 🟡 判定 复盘或触发合法狂欢
若当日高能任务 100% 达成,立刻启动游戏;未达成则做最后冲刺与英文 Bug 复盘。 1.5h
23:00 - 24:00 内存清理 降温与休眠。 洗澡、看闲书、刷 B 站。24:00 强制熄灯休眠。 1.0h

学习记录

招聘信息

以下记录就业相关信息,以备查阅:

  • 2026 米哈游 城市专场 【程序/测试】
    • 日期:2026年03月25日 19:00-20:30(GMT+8)
    • 面向:28届 实习生
    • 地点:西安+西工大长安校区宣讲 启真楼1楼104
    • 岗位:测试
    • 链接:https://mp.weixin.qq.com/s/w9bp4LJTU6QW6lG9kIsHaw
    • 宣讲链接:https://mp.weixin.qq.com/s/Ds46VVli7xdG3VKowFYGKQ
    • 参与:线下面试前问卷+网申简历

TODO Web APP 开发

利用 AI 同时熟悉现代前端开发框架,快速搭建一个 Todo Web APP,作为本计划的状态流转记录工具。

现代前后端WEBAPP开发_方向系统学习记录

2026-02-06 01:00:00

Featured image of post 现代前后端WEBAPP开发_方向系统学习记录

现代前后端 WEBAPP 开发_方向系统学习记录

1. 基础概念

1.1 现代前后端 WEBAPP 思维

  • 前端与后端高度解耦,通过 API/HTTP/Socket 通信
  • 前端使用组件化、模块化框架(React + Next.js + TS)
  • 后端服务微服务化或插件化,每个工具独立容器化
  • 统一开发环境与生产环境,避免“本地可用、服务器报错”

1.2 核心技术理解

技术 作用
Windows 操作系统与 GUI 编辑器环境
WSL2 Linux 核心环境,和服务器一致
nvm Node 版本管理器,保证项目 Node 版本一致
Node.js JS/TS 运行环境
pnpm 高效包管理器,安装依赖与管理脚手架
Next.js + React + TS 现代前端框架,支持组件化与页面路由
Docker 插件化工具容器化,保证隔离和可移植性

2. 学习路线规划

阶段 学习目标 依赖
技术工具 [x] 开发环境搭建:Windows + WSL2 + VS Code Remote-WSL,Docker 容器化思维 WSL2 安装与配置、VS Code Remote-WSL、Docker&&K8s学习笔记
[ ] Node.js + TypeScript 入门:Node 运行机制、nvm 版本管理、npm / pnpm / yarn 使用、TypeScript 基础 Nodejs+TypeScript_技术工具学习记录
[ ] Next.js 现代前端开发:React + Next.js + TS、组件化开发、页面路由与 API 路由、插件化平台思维 Next.js 官方文档、React 官方文档、UI 组件库(Ant Design / Tailwind UI)、Docker 容器化工具
基础知识 了解前后端解耦与现代 WEBAPP 架构 HTTP/HTTPS、RESTful API、GraphQL、微服务与插件化服务思维
掌握模块化与依赖管理概念 CommonJS / ES Module、模块加载机制、包管理器原理
熟悉前端组件化设计与状态管理 React 组件设计模式、状态管理(Zustand / Redux / Context API)
产出学习 能搭建插件化 WEBAPP 平台,整合前端页面与 Docker 插件工具 Node + Next.js + Docker API 调用、前端组件化开发、插件化工具集成
通过小项目练习技术链 小工具插件(音乐播放器、计算器、小游戏、AI Agent)容器化 + 前端调用
建立完整开发与部署流程能力 Windows → WSL2 → nvm → Node → pnpm → Next.js → Docker 插件化平台

3. 技术依赖


4. 相关资料链接


5. 学习过程记录

2026-02-06

  • 理解现代前后端解耦 + 插件化平台思想
  • 搭建 Windows + WSL2 开发环境,VS Code Remote-WSL 测试

2026-02-07

  • 安装 nvm,切换 Node 18,理解 Node 运行机制
  • 熟悉 npm / pnpm / yarn 区别,选择 pnpm 作为包管理器

2026-02-08

  • 使用 pnpm create next-app 初始化 Next.js + TS 项目
  • 理解模块系统:.js / .mjs / CJS / ESM
  • 开始规划插件化工具目录结构

2026-02-09

  • 每个工具独立写 Dockerfile,测试容器启动与端口映射
  • 前端通过 API 路由调用 Docker 插件,实现初步平台功能
  • 整个开发流程在 WSL2 + Docker 环境下稳定运行

6. 技术流程图

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Windows (GUI + 编辑器)
WSL2 (Linux 环境)
nvm (Node 版本管理)
Node.js (JS/TS 运行)
pnpm (包管理与项目脚手架)
Next.js + React + TypeScript (前端页面 + 组件化开发)
Docker (插件化工具容器化,隔离服务)
现代前后端 WEBAPP 工具平台

核心思维:环境统一 → Node 版本稳定 → 高效依赖管理 → 组件化前端 → 容器化服务 → 插件化平台


【Node.js + TypeScript】学习笔记

2026-02-06 01:00:00

Featured image of post 【Node.js + TypeScript】学习笔记

【Node.js + TypeScript】实操笔记

1. 简单介绍

  • Node.js 是基于 V8 引擎的 JavaScript 运行环境,可在服务器端执行 JS/TS
    • nvm 是 Node 版本管理工具,方便切换不同项目的 Node 版本
    • pnpm 是高效的包管理器,替代 npm/yarn,节省磁盘空间和安装时间
  • TypeScript 是 JavaScript 的超集,提供类型系统和编译时检查
  • 常见应用场景:Web 后端服务、CLI 工具、前端构建工具、插件化平台

2. 前置技术依赖

  • 熟悉 JavaScript 基础语法
  • 对 Linux / WSL2 命令行基础了解

3. 环境配置

  • 系统环境:Windows + WSL2(推荐 Linux 内核一致性)
  • Node 版本管理:nvm 安装 Node,切换版本
  • 包管理器:推荐 pnpm,安装与验证:

  • VS Code 配置

    • 安装 Remote-WSL 插件
    • 安装 TS / Node 插件,启用自动类型提示

4. 基础用法

4.1 Node.js 基础

4.1.1 Node.js 安装与基于 nvm 的版本管理

4.1.1.1 Node.js 安装
  • 类似于 python 的 conda,nvm(Node Version Manager)是一个用于管理多个 Node.js 版本的工具。它允许你在同一台机器上安装和切换不同版本的 Node.js,非常适合开发者在不同项目中使用不同版本的 Node.js。

  • 类似于 python 的 pip,pnpm 是一个高效的 JavaScript 包管理器,提供了更快的安装速度和更少的磁盘空间占用。它通过使用符号链接来共享依赖项,从而避免了重复安装相同的包。

Node.js 官网 提供了 Node.js 的安装命令,在选择了 系统环境Node 版本管理器包管理器Node.js 版本后,可以直接复制命令在终端中执行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 以在 WSL(Ubuntu24.04)下安装使用 nvm 进行版本管理并使用 pnpm 管理包的 Node.js 25.x 为例:

# 下载并安装 nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash

# 代替重启 shell
\. "$HOME/.nvm/nvm.sh"

## 若无效报错则重启 shell 继续后面的步骤

# 下载并安装 Node.js:
nvm install 25

# 验证 Node.js 版本:
node -v # Should print "v25.6.1".

# 安装 Corepack:
npm install -g corepack

# 下载并安装 pnpm:
corepack enable pnpm

# 验证 pnpm 版本:
pnpm -v

值得注意的是,nvm 和 pnpm 的安装步骤可能会因操作系统和环境的不同而有所差异,详见[nvm 和 pnpm 的安装注意事项](# 7.1-nvm-和-pnpm-的安装注意事项)。

4.1.1.2 Node.js 常用命令
4.1.1.3 nvm 常用命令
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 列出已安装的 Node.js 版本及别名
nvm ls

# 列出在线可安装的 Node.js 版本
nvm ls-remote

# 安装指定版本的 Node.js
nvm install <version>
## nvm install 25

# node 为最新版本的别名
nvm install node

# 设置别名
nvm alias my_alias v14.4.0

# 使用指定版本的 Node.js
nvm use <version>

# 卸载指定版本的 Node.js
nvm uninstall <version>

# 显示当前使用的 Node.js 版本
nvm current

# 获取 nvm 的安装路径
nvm which <version>

# 在指定版本下运行简单命令
nvm run <version> -- <command>
# 在指定版本下运行复杂命令
nvm exec <version> <command>

4.1.2 基于 pnpm 的包管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14



---

## 5. 实践案例

* **搭建小型 API 服务**

 ```ts
 import express, { Request, Response } from 'express';
 const app = express();
 app.get('/ping', (req: Request, res: Response) => res.send('pong'));
 app.listen(3000, () => console.log('Server running on port 3000'));
  • Docker 容器化运行

    1
    2
    3
    4
    5
    6
    
    FROM node:18-alpine
    WORKDIR /app
    COPY package.json pnpm-lock.yaml ./
    RUN npm install -g pnpm && pnpm install
    COPY . .
    CMD ["npx", "ts-node", "src/index.ts"]
    
  • 实践经验

    • 使用 pnpm 速度快、占用少
    • 在 WSL2 中开发,Docker 与 Linux 环境一致

6. 常见问题与解决办法


7. Tips

7.1 nvm 和 pnpm 的安装注意事项

由于 Node.js 25.x 版本的最新更新,Corepack 在 Node.js 25.x 中已被移除,因此需要手动安装 Corepack 来使用 pnpm 包管理器。

在 Node.js 25.x 中安装 pnpm 的步骤比 Node.js 24.x 多了一步:

``bash

安装 Corepack:

npm install -g corepack

1
2
3
4
5
6
7

---

## 8. 参考资料

- [Node.js 官网](https://nodejs.org/)
- [nvm GitHub 仓库](https://github.com/nvm-sh/nvm)

豫旅260110

2026-01-27 06:28:00

Featured image of post 豫旅260110

前言

毕业聚会是再老生常谈不过的了,故友难聚也早已司空见惯。还记得临近毕业,cwt 总是提起 “这可能是1228最后一次六个人出现了”,毕业后的第一次相聚在cwt lys 的缺席下,由zhj lmx wy lihan 组成的 1228 的四人小分队还是在河南聚了首。

比起订票前长达两个月的犹豫不决与拉拉扯扯,总归是 2/3 个 1228 见了一面。

行程

  • 西安-扶沟 1.10 08:00—>1.10 15:00
    • 高铁 G88 1小时55分钟 西安北站-郑州东站
    • 高铁 G6651 51分钟 郑州东-扶沟南站
  • 【1】扶沟 1.10下午-1.11早

    zhj故居 | 扶沟名胜 | 长城饭店 | 扶沟不正宗河南胡辣汤

  • 扶沟-郑州 1.11 07:10—>1.11 08:10
    • 高铁 G6678 38分钟 扶沟南站-郑州东站
  • 【2】郑州 1.11早-1.11下午

    二七塔 德化街 | 油化厂 | 金融岛 | 郑州菜

  • 郑州-三门峡 1.11 14:00—>1.11 15:20
    • 高铁 G3293 1小时09分钟 郑州东站-三门峡南站
  • 【3】三门峡 1.11下午-1.11晚

    天鹅湖

  • 三门峡-洛阳 1.11 18:00—>1.11 18:50
    • 高铁 G7934 30分钟 三门峡南站-洛阳龙门站
  • 【4】洛阳 1.11晚-1.12晚

    龙门石窟 | 十字街 | 老城 | 随便逛逛

  • 洛阳-西安 1.13 01:00—>1.13 8:00
    • 火车 Z293 4小时29分钟 洛阳站-西安站

推荐

  • 扶沟:
    • 【1】 zhj故居 中国河南省周口市扶沟县城关镇东关小区
    • 【2】 zzj故校 扶沟县丘桐街道红旗小学
    • 【3】 扶沟名胜 大程书院
    • 【4】 扶沟不正宗河南胡辣汤 方俊礼胡辣汤(总店)
    • 【5】 长城饭店 扶沟县长城饭店
  • 郑州:
    • 【5】 二七塔 德化街 小吃 地标
    • 【6】 油化厂 工业遗产
    • 【7】 金融岛 城市地标
    • 【8】 裕丰源豫食雅宴(郑州东站店) 豫菜
  • 三门峡:
    • 【9】 天鹅湖 公园
  • 洛阳:
    • 【10】 龙门石窟 世界文化遗产
    • 【11】 老海家牛肉烩馍(洛邑古城)牛肉烩馍 海碧
    • 【12】 东关大石桥陈记驴肉汤馆(九都东路店)
    • 【13】 莫家水席(老城区万安街) 洛阳水席
    • 【14】 白马寺 中国第一古刹

详细旅途

I. G88 西安->扶沟 1.10 晨

有几个礼拜没有这么早爬起来了,十点多的高铁,八点就得起来赶校车,多亏了我这**的长安校区。没把握好校车发车时间,险些没赶上高铁,最后几分钟总算是上了车,几个小时的高铁我也没补觉,光想着后面的计划了,前晚自动售货机里买的方便面也忘了吃(最后原样带回来当夜宵了),到下车前就啃了几口面包。

扶沟南站看起来挺像乾县站的,近十年新建的高铁站都这个样子吧。

一出站就看到 zhj 来接我。

扶沟南站

扶沟南站-2

1. 扶沟县 1.10下午-1.11早

1.1 红旗小学【2】 zzj 的神话

zzj 是什么时候被 1228 获悉的我肯定是记不得了,但这四年来,zzj 一直是 1228 众多“吉祥物”中最重要一位了(我不想说还有哪几位,cwt 肯定也不想说)。这不单单是因为 zzj 是 zhj 的“软肋”,还因为她确实“听得见,摸不着”————zhj 与 zzj 的N次语音通话里早已让我们成为了网友,甚至鄙人还有幸获得了 zzj 赐名“二狗子”。

到扶沟县的第一站就是 zzj 和 zhj 的母校红旗小学,看到这个我们口嗨了无数次要接 zzj 放学的地方,还是要打个卡的,鄙人就不出镜了,让校友来哈哈。

红旗小学

1.2 东关小区【1】 zhj 的故居

比起红旗小学,东关小区则是 1228 真正的圣地了,我们幻想了无数次,是何等风水宝地得以养育出 zhj ,是何等金屋得以深藏 zhj 的 airplane cup,又是何等雅居得以造就 LOL 和 只狼 双料大神。

原以为和我幼年在西安的旧居一样,应该是个老旧小区的单元房,见到了发现其实更贴近临潼的老家。

最惊喜的还得是一进门就见到了 zhj 的两大护法————奶奶和 zzj。

导游还是同一个嘿嘿。

东关小区

东关小区-2

1.3 大程书院【3】

虽说 cwt 早就讲过“中国一百个最美的地方,其中一百个都在扶沟”,真正意识到扶沟县的文化与历史底蕴还是从打车到 zhj 家里的路上开始。

扶沟县的招牌和路名里常见“丘桐”二字始于春秋,西汉年间始置扶沟县,北宋程颢曾于扶沟任知县,并设立了大程书院。

程门立雪的故事从幼儿园恐怕就学过了,不想原以为的小地方竟和这故事千丝万缕。

还是老熟人当招牌。

大程书院

1.4 桐成广场 地级市里的“苹果县”

在 lmx 完美错过 zzj,导致 lihan 成为 CUC 见过 zzj 阵容的唯二之人后,我们骑着 zhj 和 奶奶 的两台电动车前往扶沟县最繁华的地方————桐成广场。

路上顺便取了快递,wy 一直说给我们准备了礼物,到手捏了两下,排除了 airplane cup 的可能,最后发现是 lihan 1:100 手办,感觉还不如 airplane cup (不是)。

说真的,走在前往这扶沟县最繁华的中心的路上就让我出乎意料了,到了桐成广场后更是震惊了,肯德基、霸王茶姬就不提了,都是正版店(相较起东大村的“米优拌饭”…),走进商场里,从装修到品牌和省会城市里的小购物广场没什么两样,超市更是比肩盒马。看来也不是什么村都和东大村一样,呜呜。

最可恶的是,这儿还有五块钱三串的糖葫芦,这物价比**东大村便宜多了。

1.5 长城饭店【4】豫里尝京味

高铁上就看到这个扶沟必吃榜了哈哈。晚餐人齐了就定在了 zhj 力荐的长城饭店,店员疑似过度推销,要是不拦着点估计还得多点几道菜了。

味道合格,不过价格不是很扶沟,4个人200多,也是半年来 1228 吃饭人最齐的一次了。

长城饭店

1.6 see U later 酒馆和星星的夜游扶沟

可惜 zhj 没叫出妹妹(不是 zzj 那种妹妹!!),四个大老爷们被我拽到河堤看星星,又拉到了酒馆玩 1228 经典游戏。

Unexpectedly,1228 居然在酒馆里人齐了,刚到爬山地点酒店的 cwt 不仅远程参与了整场,还跟我们裸聊了(不是),lys 皮套(其实是中之人)也线下参与了。

lys 皮套

团购订单上标的 40° whisky 被 lmx 当雪碧库库喝地微醺,甚至在我们的忽悠下下了 soul 开始征战哈哈。

酒馆

星星

1.7 扶沟不正宗河南胡辣汤 方俊礼胡辣汤(总店)

离开河南的最后一顿当然得是西安正宗扶沟县河南胡辣汤了,从表情上看,安徽人确实都不喜欢胡辣汤,还记得在北京时候,我带cwt去吃宿舍后面的逍遥镇胡辣汤,从那以后 cwt 见到胡辣汤就骂一次。

饼加胡辣汤,甚至还有豆腐脑和水煎包,我倒是吃得挺好,这不是我们正宗西安菜吗哈哈,下次记得标明出处。

胡辣汤

II. G6678 扶沟->郑州 1.11 早

其实这时就感觉太紧了,确实下次时间得安排充裕点,搞点好玩的重要(当然我不是说女仆咖啡厅:))。

高铁上研究了下,感觉郑州确实没啥玩的,下午就去三门峡看天鹅吧。

2. 郑州 1.11早-1.11下午

2.1 金融岛【7】周末的早上空荡荡

从地图上找了个郑州看起来最装逼的地方,不过周末的十点是真没人起床啊,从地铁到金融岛,连个人影都难看到。

从金融岛看,郑州确实和武汉广州这些地方差距不小,这样的地方在武汉估计地图上根本找不到,烂大街了。不过说实话,景色还是可以的。

金融岛

2.2 油化厂【6】我直接爬爬爬 废墟探险

说真的,这老厂区改造网红景点真是千篇一律啊,北京、武汉、河南,都是这样子,留几个旧厂房,改造成文创园区,开几个咖啡店,网红小吃店,卖卖文创产品,整点不明所以的标语和打卡点……好在都是一样的管理不严,我直接逮住一个高楼爬爬爬。

油化厂

油化厂-2

2.3 二七塔【5】感觉不如女仆咖啡屋

二七塔是郑州的地标,感觉就是翻版的解放碑mini版,没啥乐子,遂前往旁边的郑州百货大楼看女仆咖啡屋,我靠小姐姐真好看吧,可惜怂了没敢进去,不对,是因为赶时间没时间进去,对,是这样的。

我靠,我也想组乐队。

二七塔

组乐队

2.4 裕丰源豫食雅宴【8】豫菜天花板

根据去年这时的粤旅胜利经验,lihan 继续找到了郑州最贵的平民餐厅,感觉是豫菜的天花板了,菜品口味都不错,环境“高雅”,价格也不算太离谱,四个人吃了两百多块钱,和长城饭店比起来感觉性价比很高了。

豫食雅宴

豫食雅宴-2

豫食雅宴-3

豫食雅宴-4

本来说看看郑州东站旁边的蜜雪冰城总店,可惜时间不允许了,下次来河南,女仆咖啡屋和蜜雪冰城总店必须得去!!!!

III. G3293 郑州->三门峡 1.11 下午

三门峡天鹅湖几个月前就听说了,之前在 NWPU 牢坏了去爬山,队友就说三门峡天鹅湖很美,这次也就一直想着要看看。

3. 三门峡 1.11下午-1.11晚

3.1 天鹅湖【9】这辈子的天鹅都看够了

三门峡地处三省交界,翻过秦岭北边就是我们亲爱的西工大(怒)。

仗着自己做了点攻略,跟出租车司机顶嘴去了另一个人少的入口。一下车我就豪气地买了两袋馍豆豆喂天鹅。

景区的多人自行车也升级成扫码共享的了,五十起步确实不便宜,我们直接

讲真天鹅湖真不错吧,可惜还是不太会拍照,设备也不太行。时间算是来对了,就得四点进门,五点到中间映着夕阳。

天鹅湖

天鹅湖-2

天鹅湖-3

天鹅湖-4

天鹅湖-5

天鹅湖-6

IV. G7934 三门峡->洛阳 1.11 晚

这次非常极限,实在是想省点时间,到高铁站时其实已经停止检票了,索性放我们进去了,不然感觉 wy 得打死我QWQ。

习惯了卡极限的我确实得改掉这个坏习惯了。

4. 洛阳 1.11晚-1.13凌晨

4.1 神都夜话 孜然味泡馍还有私人影院的惊天魔盗团

洛阳是 wy 安排的,下了火车想起来还没吃晚饭,wy 把我们带到了老海家牛肉烩馍(洛邑古城)【11】,吃了碗牛肉烩馍+海碧套餐(果然每个城市都有自己的汽水平替)。烩馍看着跟羊肉泡馍没有二般,吃起来味道大不相同,感觉像是烧烤的自然调料撒进了羊肉泡馍。

饭后想着晚上省点,顺便也带lmx zhj试试按摩,为了等到能过夜的点,找了家私人影院看惊天魔盗团。

虽然早有心理准备,不过lmx还是很给力,又是洗漱又是吹头发,给技师急坏了。

牛肉烩馍无图,看看海碧得了:

海碧

4.2 龙门石窟【10】洛阳古城的唯一特色

有“十三朝古都”名号的洛阳看来和西安的文旅风格一般,大街小巷的仿古商铺,卖地摊货和“特色”小吃的古文化街;还有景点的古装 Coser……别说看惯了西安,稍微逛过几趟古城特色的旅游景点就觉得没什么新鲜的了。要说唯一的特色经典的景点,恐怕就是龙门石窟了。

早餐还是洛阳特色:东关大石桥陈记驴肉汤馆(九都东路店)【12】的驴肉汤。闻起来的味道我个人是不太能接受的,食之也一般,驴肉如同品质欠佳的牛肉般,配上的饼子倒显得可口了。

驴肉汤

打车到龙门石窟的集散中心下,原以为就要进去了,不料买了票后还得走一公里多才到检票处,这摆渡车的钱不能给他赚去,遂走着到了门口。这是还没意识到什么,知道后面去了白马寺才恍然大悟,都是后话了。

说起来票价还是值得的,这般石窟雕塑佛像确实在其他地方不曾见过,可惜最大的几尊石窟正在维修,没能看到完整的龙门石窟。

期间聊起文革对文物的破坏,说起来我个人从来都是认为:只要有人还在饿死累死,文物保护这种高成本的文化活动就不应该是第一位的。因而并不是很在意这个问题,砸就砸呗,小孩子摔碎俩玻璃碗又如何。不过同行的朋友还是比较重视的,也许是我没什么这方面的文化艺术细胞吧,对之没什么共情。

龙门石窟

龙门石窟-2

4.3 白马寺 【14】中外合璧的古刹

离开龙门石窟后,感觉时间还早,想说去看看洛阳的另一个古迹白马寺,原想着寺庙都是大同小异,到了还是不虚此行的。

先是一样的集散中心引诱游客坐车,听着 wy lmx 的感叹,忽然意识到国内不少景区都是这样开发的:绞尽脑汁地从游客身上榨钱。先是人为设置距离创造摆渡车的消费,路上和门口又是 PDD 9.9包邮杂牌纪念品的商贩,最后是景区内的餐饮和纪念品店,价格都不便宜。感觉这也是国内旅游业的通病了,商业化下这般演化也是无可厚非的了,只是对我们这种真的来旅游的人来说,确实是折磨。

前半段白马寺确如我所料,对我这种完全的门外汉来说,寺庙的建筑风格和佛像的雕刻风格都没有什么特别的了,wy 是建筑领域大神,倒是和 lmx 聊的津津乐道;倒是后半程看到几个来自泰国、印度的建筑和雕塑,让人耳目一新,之前从未在国内寺庙见过外国建筑雕塑,感觉这也是白马寺的特色了。

白马寺

白马寺-2

4.4 洛阳水席【13】洛阳的特色菜系

总算是在晚上尝到一顿迥异于外地的洛阳菜了。

老城里的莫家水席是个典型的苍蝇馆子,挤在老城区的巷子里真不起眼,以至于一开始刚看了两眼还担心不行准备换一家再看看,被一旁的老太太盛情“拽”了进去。

这次懒得细看攻略,看着菜名即兴点了一通,后厨老两口就开始忙活了。聊起来这老太太的耳朵倒是挺尖,顺着我们话题就进来了。在东大村一直很无聊,以至于偶尔在出租上都会用编纂的身份背景和司机谈天说地,这次还没到我发挥,没想到 zhj 不知什么原因竟也张口就来:“你们哪来的啊”“哈尔滨”,顿时和我 wy 就相视一笑,不聊老太顺着话头没停“那边冰雪挺多吧”,想着 zhj 红温地硬着头皮聊我就开心,哈哈。

菜上了讲真味道不错,又是对我的胃口。洛阳菜道道汤汤水水,难怪叫水席,除了小酥肉在水里泡软不讨喜外,其他烩菜风的几道有笔者母上大人在家做硬菜的感觉了。特别是山楂条的汤,酸甜开胃,也是别处没有的特色。

饱腹之后,zhj 开始肆意口嗨,我想他是忘了老板耳朵挺好,wy 和我对视一眼,忙双双走出店外躲个尴尬。

洛阳水席

洛阳水席-2

洛阳水席-3

洛阳水席-4

Ⅴ. Z293 洛阳->西安 1.13 凌晨

走前一起找了家网吧四排,lmx说这不是第一次正经和我们开黑我倒没印象了,不过 lmx 终归也是和我有几分“共苦”,不然也不至于和我们混迹了。

打了几把战地6,听 lmx *叫还是很有意思的哈哈,随后就是分别了。最后一段在河南的路是和 wy 一路骑车到火车站,路上为拼凑出她女朋友的游戏 id,三个人凑了半天字,其实没啥意思,我倒更喜欢最后在天桥上学 saki酱 大喊“(人間に、なりたいですわ!我好想成为人啊!)”

唉,难得成为人几天,后来的一个月果然又是牢牢的一月,码字这天夜里,在西安家里刚跟 lcy 聊完就业,支持着我“未来会更好”的信念也快被打消完了,脚伤还迟迟不好,真不知道这两年半怎么熬过去啊。

回监狱了。

监狱