vite priview解析

55 阅读3分钟

1. 函数定义和配置解析

export async function preview(
  inlineConfig: InlineConfig = {},
): Promise<PreviewServer> {
  const config = await resolveConfig(
    inlineConfig,
    'serve',
    'production',
    'production',
    true,
  )
  • preview函数接收一个可选的inlineConfig参数,用于配置预览服务器。
  • 调用resolveConfig函数解析配置,返回一个config对象。

2. 检查构建目录

  const distDir = path.resolve(config.root, config.build.outDir)
  if (
    !fs.existsSync(distDir) &&
    config.plugins.every((plugin) => !plugin.configurePreviewServer) &&
    process.argv[1]?.endsWith(path.normalize('bin/vite.js')) &&
    process.argv[2] === 'preview'
  ) {
    throw new Error(
      `The directory "${config.build.outDir}" does not exist. Did you build your project?`,
    )
  }
  • 检查构建输出目录distDir是否存在。
  • 如果没有插件实现configurePreviewServer钩子,并且是通过CLI调用的preview命令,则抛出错误提示用户先构建项目。

3. 创建HTTP服务器

  const app = connect() as Connect.Server
  const httpServer = await resolveHttpServer(
    config.preview,
    app,
    await resolveHttpsConfig(config.preview?.https),
  )
  setClientErrorHandler(httpServer, config.logger)
  • 创建一个connect应用实例app
  • 调用resolveHttpServer函数创建HTTP服务器httpServer
  • 设置客户端错误处理函数。

4. 初始化服务器配置

  const options = config.preview
  const logger = config.logger

  const closeHttpServer = createServerCloseFn(httpServer)
  • 获取预览服务器的配置options和日志记录器logger
  • 创建关闭HTTP服务器的函数closeHttpServer

5. 定义预览服务器对象

  const server: PreviewServer = {
    config,
    middlewares: app,
    httpServer,
    async close() {
      teardownSIGTERMListener(closeServerAndExit)
      await closeHttpServer()
    },
    resolvedUrls: null,
    printUrls() {
      if (server.resolvedUrls) {
        printServerUrls(server.resolvedUrls, options.host, logger.info)
      } else {
        throw new Error('cannot print server URLs before server is listening.')
      }
    },
    bindCLIShortcuts(options) {
      bindCLIShortcuts(server as PreviewServer, options)
    },
  }
  • 定义PreviewServer对象,包含配置、中间件、HTTP服务器等属性。
  • 定义关闭服务器的方法close
  • 定义打印服务器URL的方法printUrls
  • 定义绑定CLI快捷键的方法bindCLIShortcuts

6. 设置SIGTERM信号监听器

  const closeServerAndExit = async () => {
    try {
      await server.close()
    } finally {
      process.exit()
    }
  }

  setupSIGTERMListener(closeServerAndExit)
  • 定义关闭服务器并退出的函数closeServerAndExit
  • 设置SIGTERM信号监听器,以便在接收到信号时关闭服务器并退出进程。

7. 应用插件钩子

  const postHooks: ((() => void) | void)[] = []
  for (const hook of config.getSortedPluginHooks('configurePreviewServer')) {
    postHooks.push(await hook(server))
  }
  • 获取并应用所有插件的configurePreviewServer钩子。

8. 配置CORS和代理

  const { cors } = config.preview
  if (cors !== false) {
    app.use(corsMiddleware(typeof cors === 'boolean' ? {} : cors))
  }

  const { proxy } = config.preview
  if (proxy) {
    app.use(proxyMiddleware(httpServer, proxy, config))
  }
  • 根据配置添加CORS中间件。
  • 根据配置添加代理中间件。

9. 添加压缩和基础中间件

  app.use(compression())

  if (config.base !== '/') {
    app.use(baseMiddleware(config.rawBase, false))
  }
  • 添加压缩中间件。
  • 如果配置了基础路径,则添加基础中间件。

10. 静态资源和HTML回退中间件

  const headers = config.preview.headers
  const viteAssetMiddleware = (...args: readonly [any, any?, any?]) =>
    sirv(distDir, {
      etag: true,
      dev: true,
      extensions: [],
      ignores: false,
      setHeaders(res) {
        if (headers) {
          for (const name in headers) {
            res.setHeader(name, headers[name]!)
          }
        }
      },
      shouldServe(filePath) {
        return shouldServeFile(filePath, distDir)
      },
    })(...args)

  app.use(viteAssetMiddleware)

  if (config.appType === 'spa' || config.appType === 'mpa') {
    app.use(htmlFallbackMiddleware(distDir, config.appType === 'spa'))
  }
  • 添加静态资源中间件viteAssetMiddleware
  • 根据应用类型添加HTML回退中间件。

11. 应用插件后钩子

  postHooks.forEach((fn) => fn && fn())
  • 执行所有插件的后钩子。

12. 添加index.html和404中间件

  if (config.appType === 'spa' || config.appType === 'mpa') {
    app.use(indexHtmlMiddleware(distDir, server))
    app.use(notFoundMiddleware())
  }
  • 添加index.html中间件和404中间件。

13. 启动HTTP服务器

  const hostname = await resolveHostname(options.host)
  const port = options.port ?? DEFAULT_PREVIEW_PORT

  await httpServerStart(httpServer, {
    port,
    strictPort: options.strictPort,
    host: hostname.host,
    logger,
  })

  server.resolvedUrls = await resolveServerUrls(
    httpServer,
    config.preview,
    config,
  )
  • 解析主机名和端口。
  • 启动HTTP服务器。
  • 解析并设置服务器URL。

14. 打开浏览器

  if (options.open) {
    const url = server.resolvedUrls?.local[0] ?? server.resolvedUrls?.network[0]
    if (url) {
      const path =
        typeof options.open === 'string' ? new URL(options.open, url).href : url
      openBrowser(path, true, logger)
    }
  }
  • 如果配置了自动打开浏览器,则打开浏览器并访问服务器URL。

15. 返回预览服务器对象

  return server as PreviewServer
}
  • 返回预览服务器对象。

总结

该代码定义了一个preview函数,用于启动一个Vite预览服务器,模拟生产环境部署。主要功能包括:

  1. 解析配置并检查构建目录。
  2. 创建HTTP服务器和connect应用实例。
  3. 设置服务器关闭逻辑和SIGTERM信号监听器。
  4. 应用插件钩子和中间件(如CORS、代理、压缩、静态资源、HTML回退等)。
  5. 启动HTTP服务器并解析服务器URL。
  6. 根据配置自动打开浏览器。
  7. 返回预览服务器对象,提供关闭服务器、打印URL和绑定CLI快捷键等功能。