vite本地运行-启动本地服务
创建调试项目
我们先创建一个最简单的项目,来看看vite是如何启动本地服务的。
- 在
playground下面新建个文件夹vite-test1 - 在
playground/vite-test1新建index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="module" src="./main.js"></script>
</body>
</html>
- 在
playground/vite-test1新建main.js
console.log('hello world')
- 使用
pnpm init新建package.json文件
{
"name": "vite-test1",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.8.1",
"devDependencies": {
"vite": "workspace:*"
}
}
- 在
.vscode/launch.json添加调试指令
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug: pnpm run dev vite-test1",
"skipFiles": ["<node_internals>/**"],
"program": "${workspaceFolder}/packages/vite/bin/vite.js",
"args": ["--debug"],
"cwd": "${workspaceFolder}/playground/vite-test1",
"console": "integratedTerminal",
"outFiles": ["${workspaceFolder}/packages/vite/dist/**/*.js"],
"sourceMaps": true,
"resolveSourceMapLocations": [
"${workspaceFolder}/packages/vite/**",
"!**/node_modules/**"
]
},
]
}
在Run and Debug面板,找到指令,点击开始按钮,或者F5开始调试
源码分析
1. 执行 "dev": "vite"执行时,先找到packages/vite/src/node/cli.ts文件,执行里面的action,
删除一些干扰代码,实际上就剩三步
- 创建服务
- 监听端口
- 控制台打印日志
cli
.command('[root]', 'start dev server')
.action(async (root: string, options: ServerOptions & GlobalCLIOptions) => {
const { createServer } = await import('./server')
try {
const server = await createServer({
root,
base: options.base,
mode: options.mode,
configFile: options.config,
configLoader: options.configLoader,
logLevel: options.logLevel,
clearScreen: options.clearScreen,
server: cleanGlobalCLIOptions(options),
forceOptimizeDeps: options.force,
})
await server.listen()
server.printUrls()
} catch (e) {
}
})
2. createServer
// packages/vite/src/node/server/index.ts
export function createServer(
inlineConfig: InlineConfig = {},
): Promise<ViteDevServer> {
return _createServer(inlineConfig, { listen: true })
}
3. _createServer
删除一些多余的代码先, 主要看三个步骤
- 处理config
- 创建http服务
- 返回 server
export async function _createServer(
inlineConfig: InlineConfig = {},
options: {
listen: boolean
previousEnvironments?: Record<string, DevEnvironment>
},
): Promise<ViteDevServer> {
const config = await resolveConfig(inlineConfig, 'serve')
const { root, server: serverConfig } = config
const middlewares = connect() as Connect.Server
const httpServer = await resolveHttpServer(serverConfig, middlewares, httpsOptions)
let server: ViteDevServer = {
httpServer,
async listen(port?: number, isRestart?: boolean) { },
printUrls() { },
}
return server
}
4. resolveConfig
主要用来处理参数 添加默认参数、处理config,然后返回
// packages/vite/src/node/config.ts
export async function resolveConfig( ): Promise<ResolvedConfig> {
let resolved: ResolvedConfig
resolved = { }
resolved = {
...config,
...resolved,
}
return resolved
}
5. resolveHttpServer
// packages/vite/src/node/http.ts
export async function resolveHttpServer(
{ proxy }: CommonServerOptions,
app: Connect.Server,
httpsOptions?: HttpsServerOptions,
): Promise<HttpServer> {
if (!httpsOptions) {
const { createServer } = await import('node:http')
return createServer(app)
}
// #484 fallback to http1 when proxy is needed.
if (proxy) {
const { createServer } = await import('node:https')
return createServer(httpsOptions, app)
} else {
const { createSecureServer } = await import('node:http2')
return createSecureServer(
{
// Manually increase the session memory to prevent 502 ENHANCE_YOUR_CALM
// errors on large numbers of requests
maxSessionMemory: 1000,
...httpsOptions,
allowHTTP1: true,
},
// @ts-expect-error TODO: is this correct?
app,
)
}
}
6. 静态资源代理 sirv
// packages/vite/src/node/server/index.ts
const middlewares = connect() as Connect.Server
middlewares.use(serveStaticMiddleware(server))
// packages/vite/src/node/server/middlewares/static.ts
export function serveStaticMiddleware(
server: ViteDevServer,
): Connect.NextHandleFunction {
const dir = server.config.root
const serve = sirv(
dir,
sirvOptions({
config: server.config,
getHeaders: () => server.config.server.headers,
}),
)
// Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
return function viteServeStaticMiddleware(req, res, next) {
serve(req, res, next)
}
}
// packages/vite/src/node/server/middlewares/static.ts
const sirvOptions = ({
config,
getHeaders,
disableFsServeCheck,
}: {
config: ResolvedConfig
getHeaders: () => OutgoingHttpHeaders | undefined
disableFsServeCheck?: boolean
}): Options => {
return {
dev: true,
etag: true,
extensions: [],
setHeaders(res, pathname) {
if (knownJavascriptExtensionRE.test(pathname)) {
res.setHeader('Content-Type', 'text/javascript')
}
const headers = getHeaders()
if (headers) {
for (const name in headers) {
res.setHeader(name, headers[name]!)
}
}
},
}
}
总结
vite本地访问项目时, 通过 http、https、http2启动本地服务。 使用第三包包 connect 作为服务的中间件。 使用第三方包 sirv 做静态资源的代理