06 - 开发环境实战
工具链落位是 WSL 实战的第一课:Node、Python、Go、Rust、Java、C++ 各有各的安装哲学;数据库放 Linux 侧才有性能;代码必须放
~/而不是/mnt/c——这一条铁律背后是 9p 协议与 inotify 的双重陷阱。本章逐个给出可复制的安装命令,并串起 Windows Terminal 集成与前后端混编联调的完整场景。
6.1 语言工具链逐个落位
Node.js:nvm + corepack
不要用 apt install nodejs,版本太旧且升级痛苦。nvm 是社区事实标准:
# 安装 nvm(以官方脚本为准,版本号可能更新)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
# 重载 shell 后
source ~/.bashrc
nvm install --lts # 装 LTS 版本并设为默认
nvm install 20 # 多版本并存
nvm use 20 # 切换
node -v && npm -v包管理器用 corepack 启用 pnpm(Node 16.13+ 内置):
corepack enable
corepack prepare pnpm@latest --activate
pnpm -v项目里若有 "packageManager": "pnpm@9.x.x" 字段,corepack 会自动锁定版本,团队协作不再吵架。
Python:pyenv + venv
pyenv 从源码编译 Python,先补齐编译依赖:
sudo apt update
sudo apt install -y build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \
libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev \
libffi-dev liblzma-dev git
curl https://pyenv.run | bash按脚本提示把以下内容追加到 ~/.bashrc:
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"日常流:
pyenv install 3.12.5
pyenv global 3.12.5 # 全局默认
pyenv local 3.11.9 # 项目目录内固定版本(写入 .python-version)
python -m venv .venv # 项目虚拟环境
source .venv/bin/activate
pip install requests
deactivate # 退出虚拟环境数据科学向可以选 miniconda 替代 pyenv+venv:
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh -b -p ~/miniconda3
~/miniconda3/bin/conda init bash
conda create -n ds python=3.12 numpy pandas matplotlib jupyterlab
conda activate dsGo:官网 tar 包直装
apt 的 golang 版本通常落后一到两个大版本,直接从官网装:
wget https://go.dev/dl/go1.23.0.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.23.0.linux-amd64.tar.gz
rm go1.23.0.linux-amd64.tar.gzPATH 追加到 ~/.bashrc:
export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin这里正是 PATH 机制 的日常应用:/usr/local/go/bin 提供 go 命令本体,~/go/bin 收纳 go install 装出的二进制工具。
验证与初始化项目:
go version
mkdir ~/hello && cd ~/hello
go mod init example.com/hello
go run .Rust:rustup 标准路径
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
rustc --version && cargo --versionrustup 默认把工具链放在 ~/.rustup,可执行文件放 ~/.cargo/bin 并自动注入 PATH。升级一条命令:
rustup update再次呼应 PATH 机制:~/.cargo/env 做的事情就是向 PATH 追加一个目录,理解了 PATH 就能看懂几乎所有工具链安装脚本。
Java:SDKMAN 多版本管理
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk list java # 浏览可用发行版与版本
sdk install java 21.0.4-tem # Eclipse Temurin JDK 21
sdk install java 17.0.12-tem # 再装一个 LTS 并存
sdk use java 17.0.12-tem # 当前 shell 切换
sdk default java 21.0.4-tem # 设为默认
java -versionSpring Boot 项目跑通要点:
# 安装 Maven 或 Gradle 也交给 sdkman
sdk install maven
git clone https://github.com/spring-guides/gs-rest-service.git
cd gs-rest-service/complete
./mvnw spring-boot:run
# Windows 浏览器打开 http://localhost:8080/greeting —— localhost 直通生效要点清单:JDK 用 sdkman 管理保证版本可控;./mvnw wrapper 锁定构建工具版本;默认端口 8080 可从 Windows 直接访问。更系统的 Java 内容见 Java 教程。
C/C++:build-essential 全家桶
sudo apt install -y build-essential gdb cmake clang-format
gcc --version && g++ --version && cmake --versionVS Code 侧配合调试(扩展必须装在 WSL 侧,见 05 章):
{
"version": "0.2.0",
"configurations": [
{
"name": "g++ build & debug",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/app",
"cwd": "${workspaceFolder}",
"MIMode": "gdb",
"preLaunchTask": "build"
}
]
}配合 CMake Tools 扩展则更省心:选择 kit → Configure → F5 一路点下去,断点落在 Linux 的 gdb 里。
6.2 数据库开发环境
MySQL / PostgreSQL 装在 WSL 内
# PostgreSQL
sudo apt install -y postgresql postgresql-contrib
sudo service postgresql start # 未启用 systemd 时
sudo -u postgres psql # 进入交互
# CREATE DATABASE devdb;
# CREATE USER dev WITH PASSWORD 'dev123';
# GRANT ALL PRIVILEGES ON DATABASE devdb TO dev;
# MySQL
sudo apt install -y mysql-server
sudo service mysql start
sudo mysql_secure_installation数据文件必须留在 Linux 侧。apt 安装的默认 datadir 在 /var/lib/postgresql 与 /var/lib/mysql,都在 ext4 上,无需动它。若手动改过配置指向 /mnt/c,数据库会慢到怀疑人生且可能锁损坏。
/mnt/wsl/ 共享挂载点说明
WSL2 提供了一个发行版间的共享挂载树:
mount | grep /mnt/wsl
ls /mnt/wsl/多个发行版通过它共享某些内核级挂载(如 GPU 驱动设备)。它是 WSL 内部机制,不是给用户当”跨发行版 U 盘”用的,日常开发不必依赖;跨发行版共享文件走 ~/ 加 tar 打包或 git 更可靠。
DBeaver 从 Windows 连接
两条路:
- localhost 直通(推荐,WSL2 默认支持):DBeaver 新建连接,Host 填
localhost,端口填 5432/3306 即可。localhost 直通让 Windows 到 WSL 的 TCP 无需任何配置。 - portproxy:仅当直通失效(老版本 Windows 或 mirrored 网络特殊配置)时启用:
netsh interface portproxy add v4tov4 listenport=5432 listenaddress=127.0.0.1 `
connectport=5432 connectaddress=(wsl hostname -I).Trim()注意 PostgreSQL 还要允许监听非本地连接时改 listen_addresses = '*' 并配 pg_hba.conf;只用 localhost 直通时保持默认最安全。
Redis 一条龙
sudo apt install -y redis-server
sudo service redis-server start
redis-cli ping # PONG
redis-cli set hello wsl
redis-cli get hello
# 持久化按需开启(AOF)
# sudo sed -i 's/^appendonly no/appendonly yes/' /etc/redis/redis.confRedis 单线程内存库,即使放默认位置也几乎无 IO 瓶颈,但持久化文件(dump.rdb)同样应留在 ext4。
6.3 项目位置铁律:代码放 ~/ 不放 /mnt/c
为什么
WSL 访问 /mnt/c 走 9p 文件协议,每次系统调用都要跨越 Linux 内核到 Windows NTFS 的边界。后果有两个:
- IO 慢 5-10 倍:
npm install、git status、编译全量读写都显著变慢 - inotify 失效:9p 不支持完整的文件变更通知,vite/webpack 的 hot reload 监听不到文件变化,页面不自动刷新
实测参考对比(同机器、同一 vite 项目冷启动 + 全量构建):
| 操作 | ~/project (ext4) | /mnt/c/project (9p) | 差距 |
|---|---|---|---|
| npm install | 18 s | 96 s | 约 5 倍 |
| git status | 0.15 s | 1.8 s | 约 12 倍 |
| vite 冷启动 | 1.2 s | 无法热更新 | 功能性故障 |
| tsc 全量编译 | 22 s | 130 s | 约 6 倍 |
数字随硬件浮动,量级结论稳定:代码在 Linux 侧,Windows 只当显示器和键盘。
正确姿势
mkdir -p ~/work && cd ~/work
git clone https://github.com/your/repo.git
code .需要用 Windows 图形工具(如 Photoshop 改设计稿)处理这些文件时,走 \\wsl$\Ubuntu\home\you\work\ 网络路径访问,方向反过来了:Linux 进程读 Linux 文件快,Windows 进程经 9p 反向访问也可接受(低频操作无妨)。
反向同理:Windows 侧的 IDE 项目(如 Visual Studio 大型工程)就让它待在 NTFS,别搬进 WSL。
6.4 浏览器访问 WSL dev server
localhost 直通演示:
cd ~/work && pnpm create vite demo --template react
cd demo && pnpm install && pnpm dev
# VITE ready on http://localhost:5173/此刻在 Windows 浏览器地址栏输入 http://localhost:5173 直接打开。流量链路:
flowchart LR B["Windows 浏览器"] -->|"localhost:5173"| H["Windows localhost 直通层"] H -->|"自动转发"| V["WSL 内 vite :5173"] V -->|"热更新 WebSocket"| B
Node/Spring Boot/Flask/Django 同理,只要服务绑定在 0.0.0.0 或 localhost 上均可。唯一例外是绑定到 WSL eth0 具体 IP 的服务,此时用该 IP 访问而非 localhost。
防火墙拦不住 localhost 直通;但局域网其他设备想访问你的 dev server,仍需 portproxy + 防火墙规则,见 05 章。
6.5 API 密钥管理
.env 文件实践
密钥绝不进 git。项目根建 .env,同时提供 .env.example(不含真值)进仓库:
# .env(已在 .gitignore 中)
DATABASE_URL=postgres://dev:dev123@localhost:5432/devdb
OPENAI_API_KEY=sk-xxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxNode 侧 dotenv,Python 侧 python-dotenv 自动加载。.env 实践 的核心原则:环境变量是进程级配置,文件只是它的载体,载体的权限和作用域要收窄:
chmod 600 .env # 仅属主可读写direnv:目录级环境自动加载
比 .env 更进一步,进入目录自动 source、离开自动卸载:
sudo apt install -y direnv
echo 'eval "$(direnv hook bash)"' >> ~/.bashrc
exec $SHELL
cd ~/work/demo
echo 'export OPENAI_API_KEY=sk-xxx' > .envrc
direnv allow
# 之后 cd 进来自动带上变量,cd 离开自动清除好处:变量不落到任何全局 shell 配置里,不同项目互不污染,密钥泄漏面最小化。.envrc 同样要进 .gitignore。
6.6 Windows Terminal 集成
Profile 自动生成
安装新版 WSL 发行版后,Windows Terminal 会自动为其生成 profile(名字如 Ubuntu-22.04),下拉菜单直接可选。若没有,设置 → 添加新配置文件 → 新空配置文件 → 命令行填 wsl.exe -d Ubuntu-22.04。
Nerd Font 装在 Windows 侧
终端渲染发生在 Windows,字体也必须装在 Windows:下载 Nerd Font(如 JetBrainsMono Nerd Font),右键 ttf → 为所有用户安装,然后在 Terminal profile → 外观 → 字体选择它。starship、powerlevel10k 等 prompt 的图标才能正常显示。WSL 内不需要装字体。
默认启动目录设为 \wsl$
profile → 常规 → 启动目录填:
\\wsl$\Ubuntu-22.04\home\yourname\work
或更简单地在 profile 命令行后加参数:
{
"name": "Ubuntu Dev",
"commandline": "wsl.exe -d Ubuntu-22.04 --cd ~",
"font": { "face": "JetBrainsMono Nerd Font" },
"startingDirectory": "//wsl$/Ubuntu-22.04/home/yourname/work"
}打开即落在 Linux 家目录,省掉每次 cd。
6.7 全栈混编联调场景
典型组合:前端 vite 跑在 WSL(享受 ext4 性能与热更新),后端 Spring Boot 跑在 Windows IDEA(公司统一 IDE、连 Windows 上的内网认证)。
flowchart LR subgraph WSL["WSL2"] FE["vite dev server :5173"] end subgraph WIN["Windows"] BE["Spring Boot :8080<br/>IDEA 运行"] BR["浏览器"] end BR -->|"http://localhost:5173"| FE FE -->|"代理 /api → localhost:8080"| BE BE -->|"返回 JSON"| FE
互通方案要点:
- 端口双向可达:localhost 直通对 Windows→WSL 生效;WSL→Windows 方向,WSL2 中 Windows 主机网关 IP 可由
/etc/resolv.conf的 nameserver 获得,或直接用host.docker.internal风格的名称(mirrored 模式下直接 localhost 双通)
# WSL 内拿到 Windows 主机 IP(NAT 模式)
WIN_IP=$(grep nameserver /etc/resolv.conf | awk '{print $2}')
curl http://$WIN_IP:8080/api/health- vite 代理配置:
// vite.config.ts
export default defineConfig({
server: {
proxy: {
"/api": {
target: `http://${process.env.WIN_IP ?? "localhost"}:8080`,
changeOrigin: true,
},
},
},
});- CORS 免烦恼:走 vite 代理后浏览器看到的是同源请求,后端无需单独开 CORS。
- 数据库归属:PostgreSQL 建议放 WSL,Windows 后端用
localhost:5432直连即可,方向仍是直通支持的。
Java 生态的系统学习见 Java 教程。
6.8 性能预期管理
WSL2 不是裸机,但差距远小于直觉。社区大量基准测试的共识区间:
| 维度 | WSL2 相对裸机 | 说明 |
|---|---|---|
| CPU 计算/编译 | 慢 5-15% | Hyper-V 虚拟化开销,正常范围 |
| 磁盘 IO(ext4 内) | 慢 5-10% | virtio 接近原生 |
| 网络 | 视模式而定 | NAT 有轻微开销,mirrored 接近原生 |
| 内存 | 有静态底噪 | VM 常驻,可用 .wslconfig 控制 |
编译速度慢 5-15% 属于预期之内,不值得为此焦虑;真正的性能悬崖只出现在一种情况——代码放错位置(/mnt/c),那是以倍数计的损失,回到 6.3 节的铁律即可解决。
6.9 小结
- 语言工具链各归其位:nvm 管 Node、pyenv/miniconda 管 Python、官网 tar 管 Go、rustup 管 Rust、sdkman 管 Java、build-essential 管 C++
- 数据库装 WSL 内、数据留 ext4;DBeaver 用 localhost 直通零配置连接
- 铁律:代码必须在
~/,9p 协议让 /mnt/c 上的项目 IO 慢 5-10 倍且热更新失灵 - localhost 直通使浏览器无缝访问 dev server;混编开发靠 vite 代理打通前后端
- 密钥用 .env + chmod 600 或 direnv 目录级管理;字体装 Windows 侧、启动目录设 \wsl$
- 编译比裸机慢 5-15% 是虚拟化的正常代价,别把放错位置的锅甩给 WSL 本身
下一篇 07-WSL与虚拟机协作 讨论 WSL2、VirtualBox、VMware 与双系统的共存与抉择。