vite本地运行-启动本地服务

187 阅读2分钟

vite本地运行-启动本地服务

创建调试项目

我们先创建一个最简单的项目,来看看vite是如何启动本地服务的。

  1. playground下面新建个文件夹vite-test1
  2. 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>
  1. playground/vite-test1 新建 main.js
console.log('hello world')
  1. 使用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:*"
  }
}
  1. .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,

删除一些干扰代码,实际上就剩三步

  1. 创建服务
  2. 监听端口
  3. 控制台打印日志
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

删除一些多余的代码先, 主要看三个步骤

  1. 处理config
  2. 创建http服务
  3. 返回 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 做静态资源的代理