Appearance
第 27 章:预览服务器与静态部署
经过 vite build 的洗礼,我们的项目代码已经被打包、优化,并静静地躺在 dist 目录中。这些由 JS、CSS、HTML 和静态资源组成的文件,就是我们即将交付给最终用户的“成品”。
但在将它部署到真实的服务器之前,我们如何能确保它在生产环境中的行为与预期一致?这就是 vite preview 命令存在的意义。
理论:预览(Preview) vs. 开发(Dev)
vite preview 命令启动一个简单的本地静态 Web 服务器,将 dist 目录作为根目录。它让你可以在本地环境中,模拟最终用户访问你网站时的真实情况。这与 vite dev 有着本质的区别:
vite dev(开发服务器):- 目标:提供极致的开发体验。
- 机制:动态地、按需地编译和提供文件,不进行打包。利用浏览器原生 ESM 支持,并附带 HMR 功能。
- 服务内容:直接服务于你的源代码(
src目录)。
vite preview(预览服务器):- 目标:验证生产构建产物。
- 机制:一个纯粹的静态文件服务器,它不会进行任何代码转换或打包。它只是忠实地提供
dist目录下的文件。 - 服务内容:服务于构建后的产物(
dist目录)。
使用 preview 服务器的主要目的是,在部署前捕获那些只有在生产构建后才会出现的问题,例如:
- 路径问题:构建后的资源路径(如图片、API 请求)是否正确?
- 代码压缩问题:代码压缩(Minify)是否意外地破坏了某些逻辑?
- Tree-shaking 问题:是否有不应被移除的代码被错误地清除了?
Vite 源码剖析:preview 命令的简洁实现
Vite 的预览服务器实现非常轻量,其核心逻辑位于 packages/vite/src/node/preview.ts 的 preview 函数中。
typescript
// packages/vite/src/node/preview.ts
export async function preview(inlineConfig: InlineConfig = {}): Promise<PreviewServer> {
// 1. 解析配置,但这次是为预览服务器
const config = await resolveConfig(inlineConfig, 'serve', 'production')
// 2. 创建一个 Connect 实例 (类似于 Koa 或 Express)
const app = connect()
// 3. 使用 sirv 作为静态文件服务中间件
// sirv 是一个非常快速、轻量的静态资源服务库
app.use(
config.base,
sirv(config.build.outDir, {
etag: true,
single: true, // 对于单页应用 (SPA) 很重要
}),
)
// 4. 创建并启动一个 Node.js HTTP 服务器
const server = await createServer(app)
server.listen(config.preview.port)
// ...
return server
}从源码中我们可以看到,preview 命令的实现非常直白:
- 它同样会解析配置,以确定诸如
base路径、输出目录outDir和预览端口port等信息。 - 它使用了一个轻量的 Node.js 服务框架
connect。 - 核心是
sirv这个库。Vite 用它来创建一个静态文件服务中间件,指向dist目录 (config.build.outDir)。single: true这个选项对于单页应用(SPA)至关重要。它意味着当服务器收到一个无法匹配任何静态文件的请求时(例如,用户直接访问/user/profile),它不会返回 404,而是会返回index.html。这样,前端路由库就能接管并正确地渲染页面。
- 最后,它创建一个标准的 Node.js HTTP 服务器,承载
connect应用,并在指定的端口上进行监听。
mini-vite 实践:实现 preview 命令
让我们为 mini-vite 添加一个 preview 命令。我们将使用 Node.js 内置的 http 模块和一个社区流行的静态服务库 sirv 来实现。
首先,安装 sirv:
bash
npm install sirv -D然后,创建一个 preview.js 文件:
javascript
// preview.js
import http from 'http';
import sirv from 'sirv';
import path from 'path';
async function runPreview() {
const port = 5173; // 模拟 Vite 的默认预览端口
const distDir = path.resolve(process.cwd(), 'dist');
console.log(`Starting mini-vite preview server on port ${port}...`);
// 1. 创建一个 sirv 中间件
// 它将服务于 'dist' 目录
const serve = sirv(distDir, {
single: true, // 开启 SPA 模式
dev: false, // 确保在生产模式下运行
etag: true, // 开启 ETag
});
// 2. 创建 HTTP 服务器
const server = http.createServer((req, res) => {
// 将请求交给 sirv 处理
serve(req, res, () => {
// 如果 sirv 没有处理该请求 (例如,找不到文件且未开启 single 模式)
res.statusCode = 404;
res.end('Not Found');
});
});
// 3. 监听端口
server.listen(port, () => {
console.log(`Preview server is running at http://localhost:${port}`);
});
}
runPreview();这个 preview.js 脚本做了什么?
- 它引入了
http和sirv。 - 它创建了一个
sirv实例,配置它来服务dist目录,并开启了single: true以支持 SPA。 - 它创建了一个基础的 Node.js
http.Server。在请求处理函数中,它将req和res对象直接传递给sirv中间件。 - 服务器在
5173端口启动,并打印出一个访问链接。
现在,更新你的 package.json,加入 preview 脚本:
json
"scripts": {
"dev": "node server.js",
"build": "node build.js",
"preview": "node preview.js"
}现在,你可以体验一个完整的开发->构建->预览流程了:
npm run dev:在开发模式下进行编码和调试。npm run build:将你的应用打包到dist目录。npm run preview:在本地启动一个静态服务器,预览最终的生产产物。
至此,mini-vite 已经拥有了从开发到构建再到预览的完整工具链,真正成为了一个微缩版的现代前端构建工具。