不读Vite源码不知道的知识

2,633 阅读4分钟

前言

最新在看vite官方文档和vite源码,官网的内容大家都可以看到就不需要搬运内容了,但是通过阅读源码也学到了一些知识,这些知识不看源码是无法得知的,于是就在本文总结一下那些通过看vite源码才知道的知识。

指定项目启动端口号

我们可以通过在vite.config.js中配置server.port(默认3000)来指定我们想要开启的端口号,如果这个指定的端口号没有被占用,就可以成功启动项目;如果这个指定的端口号已经被占用,则vite会帮助我们寻找下一个未被占用的端口号。

当然以上行为是在server.strictPort设为false(同时也是默认值)的情况下发生的。若你将server.strictPort设为true,表示你只想开启server.port端口,如果这个端口号被占用了,不需要vite帮助我们寻找下一个未被占用的端口号,所以,此时,项目会启动失败。

这两个配置项的设置如下

 //vite.config.js
{
  server: {
    port: 3002,
    strictPort: false,
  }
}

那么,vite源码中是如何实现这一部分的设置的呢?

export async function httpServerStart(
  httpServer: HttpServer,
  serverOptions: {
    port: number
    strictPort: boolean | undefined
    host: string | undefined
    logger: Logger
  }
): Promise<number> {
  return new Promise((resolve, reject) => {
    let { port, strictPort, host, logger } = serverOptions

    const onError = (e: Error & { code?: string }) => {
      if (e.code === 'EADDRINUSE') { // node中,端口被占用就抛这个错误code
        if (strictPort) {
          // 如果设定的端口被占用,又设置了strictPort,就直接启动项目失败,不会继续帮我们寻找下一个可用端口
          httpServer.removeListener('error', onError)
          reject(new Error(`Port ${port} is already in use`))
        } else {
          // 端口自增策略,即如何帮我们寻找下一页未被占用的可用端口
          httpServer.listen(++port, host)
        }
      } else {
        httpServer.removeListener('error', onError)
        reject(e)
      }
    }

    httpServer.on('error', onError)

    httpServer.listen(port, host, () => {
      httpServer.removeListener('error', onError)
      resolve(port)
    })
  })
}

可以通过我在B站发的视频学习这部分内容:www.bilibili.com/video/BV1HP…

裸模块

已经看过vite文档的朋友应该都知道,开发阶段中,Vite 的开发服务器将所有代码视为原生 ES 模块,用到某个模块就向开发服务器请求模块,开发服务器返回该模块,这才使得vite非常快。

假设我们的项目使用的vue框架,我们在引入Vue时,会这样写,import { createApp } from 'vue',像"vue"这样不带任何路径标识的写法,像vue这样的第三方依赖包引入的写法,我们称之为裸模块。但是es模块引入时,只支持用相对路径或者绝对路径引入一个模块,不支持裸模块的引入方式,vite就做了一些转换工作,帮我们把这样的引入方式换成了带路径标识的引入方式。

那么,裸模块这个概念到底是在哪里定义的呢?其实是在html规范里写的。链接在此8.1.5.4:html.spec.whatwg.org/multipage/w…

This restriction is in place so that in the future we can allow custom module loaders to give special meaning to "bare" import specifiers, like import "jquery" or import "web/crypto". For now any such imports will fail, instead of being treated as relative URLs.

这里提到了"bare" import specifiers,就是指vue这样的第三方包引入的方式,我们将裸模块标识符 所 代表的模块 叫做裸模块。

可以通过我在B站发的视频学习这部分内容:www.bilibili.com/video/BV1vf…

打开浏览器

vite可以让我们选择是否在启动项目的时候,自动打开浏览器。默认不打开。通过server.open设置。

 //vite.config.js
{
  server: {
    open: true // 默认是false
  }
}

这一部分的设置是在源码openBrowser.js文件中,

import open from 'open'

function startBrowserProcess(browser: string | undefined, url: string) {
  // If we're on OS X, the user hasn't specifically
  // requested a different browser, we can try opening
  // Chrome with AppleScript. This lets us reuse an
  // existing tab when possible instead of creating a new one.
  const shouldTryOpenChromeWithAppleScript =
    process.platform === 'darwin' && (browser === '' || browser === OSX_CHROME)

  if (shouldTryOpenChromeWithAppleScript) {
    try {
      // Try our best to reuse existing tab
      // on OS X Google Chrome with AppleScript
      execSync('ps cax | grep "Google Chrome"')
      // 如果是mac,则使用execSync node原生方法开启一个浏览器进程。
      execSync('osascript openChrome.applescript "' + encodeURI(url) + '"', {
        cwd: path.dirname(require.resolve('vite/bin/openChrome.applescript')),
        stdio: 'ignore'
      })
      return true
    } catch (err) {
      // Ignore errors
    }
  }

  // Fallback to open
  // (It will always open new tab)
  try {
    const options: open.Options = browser ? { app: { name: browser } } : {}
    // 用第三方包open打开浏览器进程
    open(url, options).catch(() => {}) // Prevent `unhandledRejection` error.
    return true
  } catch (err) {
    return false
  }
}

可以看到,如果我们设置了server.open: true。 大多数情况下,是通过一个第三方包open,打开的浏览器进程。

是os x时,用的是node原生方法execSync()开启的浏览器进程。

可以通过我在B站发的视频学习这部分内容:

www.bilibili.com/video/BV1gf…

命令行工具cac.js

当我们在终端输入vite serve的时候,vite会使用命令行工具处理我们的命令名称和命令参数,进而执行响应的动作action,那么vite使用的什么命令行工具呢?

// cli.ts
import { cac } from 'cac'

可以从cli.ts中看到,vite没有使用commander.js这个工具,而是使用的更轻量级的cac.js。

可以通过我在B站发的视频学习这部分内容:www.bilibili.com/video/BV1Tg…

结语

这篇文章也会持续更新,当然首先肯定是先在B站更新视频内容,所以大家可以先去B站关注下我。