vite 原理详解

Vite 介绍

ViteVue作者尤雨溪又一个备受关注的开源项目,它是一个前端构建工具,可类比Webpack。它主要解决了传统bundle-base服务器在开发时遇到的两个问题:

  1. 服务器启动速度慢,而且其启动时间是跟应用规模成正比的。
  2. 在更新时,即便使用了HMR,但是其热更新的时间仍是会随着应用规模的增长而直线下降。

它解决的是开发的时候的效率问题,对于生产环境则是交给了Rollup。除此之外,它还有以下优点:

  • tsjsxcss等开箱即用,无需配置。
  • 对于库开发者也是可以通过简单的配置即可打包输出多种格式的包。
  • 开发和生产共享了rollup的插件接口,大部门的rollup插件可以在vite上使用。
  • 类型化配置,配置文件可以使用ts,具有配置类型提示。

Get Started

创建项目模板

Vite提供了一个快捷创建各类型项目模板的包@vitejs/create-app的包,对外暴露了可执行的bin文件:

yarn create @vitejs/app my-vue-app --template vue

顺便提一下, yarn createyarn提供的一个聚合两个命令的语法糖,上面命令等价:

yarn global add @vitejs/create-app
@vite/create-app my-vue-app --template vue

这里是创建vue3模板。除此之外,vite还支持

  • vanilla
  • vue-ts
  • react
  • react-ts
  • preact
  • preact-ts
  • lit-element
  • lit-element-ts

启动vite

开发环境,启动服务器

vite

生产环境,打包应用或包

vite build

这里使用的都是vite的核心包 vite,所有的优化都集中在这个包中,另外vite还提供了vite.config.ts的配置文件,允许针对整个构建过程做出一些配置。

上面的vitevite build是做了什么呢?

image.png

vite命令会执行vite.js这个脚本,而在这个脚本中会执行start函数,最终引入了node目录下的cli脚本,接着看看cli脚本中干了什么?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3RbFNBxs-1623254893999)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a92b9ef738b048c4911fc443f9df0380~tplv-k3u1fbpfcp-watermark.image)]

cli脚本中主要监听了vitevite build两个命令,这便是vite开发和生产的入口了。

Vite 原理

前文提到了vite主要解决了传统开发构建工具启动和更新慢的问题,那么vite是怎样解决这两个问题呢?
vite主要通过 esbuild预构建依赖和让浏览器接管部分打包程序两种手段解决了这两个问题,下面细讲这两大手段。

esbuild预构建依赖

vite将代码分为源码和依赖两部分并分别处理,所谓依赖便是应用使用的第三方包,一般存在于node_modules目录中,一个较大项目的依赖及其依赖的依赖,加起来可能达到上千个包,这些代码可能远比我们源码代码量要大,这些依赖通常是不会改变的(除非你要进行本地依赖调试),所以无论是webpack或者vite在启动时都会编译后将其缓存下来。区别的是,vite会使用esbuild进行依赖编译和转换(commonjs包转为esm),而webpack则是使用acorn或者tsc进行编译,而esbuild是使用Go语言写的,其速度比使用js编写的acorn速度要快得多。

image.png

esbuild官方做了一个测试,打包生产环境的three.js包十次,上图是各大工具的打包时长。esbuild在打包速度上比现在前端打包工具快10-100倍。

而且vite在打包之后,还会对这些依赖包的请求设置cache-control: max-age=31536000,immutable;,即设置了强缓存,之后针对依赖的请求将不会到达服务器。如果要进行依赖调试,可以在启动服务器时使用 --force 标志,它会重新打包依赖。下面看看源码中是怎样实现这些的?

node/cli.ts

// dev
cli
  .command('[root]') // default command
  .alias('serve')
  .option('--host [host]', `[string] specify hostname`)
  .option('--port <port>', `[number] specify port`)
  .option('--https', `[boolean] use TLS + HTTP/2`)
  .option('--open [path]', `[boolean | string] open browser on startup`)
  .option('--cors', `[boolean] enable CORS`)
  .option('--strictPort', `[boolean] exit if specified port is already in use`)
  .option('-m, --mode <mode>', `[string] set env mode`)
  .option(
    '--force',
    `[boolean] force the optimizer to ignore the cache and re-bundle`
  )
  .action(async (root: string, options: ServerOptions & GlobalCLIOptions) => {
   
//   核心代码在server脚本中
    const {
    createServer } = await import('./server')
    try {
   
    //   创建服务器
      const server = await createServer({
   
        root,
        base: options.base,
        mode: options.mode,
        configFile: options.config,
        logLevel: options.logLevel,
        clearScreen: options.clearScreen,
        server: cleanOptions(options) as ServerOptions
      })
    //   监听端口
      await server.listen()
    } catch (e) {
   
      createLogger(options.logLevel).error(
        chalk.red(`error when starting dev server:\n${
     e.stack}`)
      )
      process.exit(1)
    }
  })

node/server/index.ts文件中:

import {
    DepOptimizationMetadata, optimizeDeps } from '../optimizer'

export async function createServer(
  inlineConfig: InlineConfig = {
   }
): Promise<ViteDevServer> {
   
   // 省略无关代码

  // 优化
  const runOptimize = async () => {
   
    //   cacheDir为缓存目录,一般为node_modules/.vite 目录
    if (config.cacheDir) {
   
      server._isRunningOptimizer = true
      try {
   
        // 依赖预构建
        server._optimizeDepsMetadata = await optimizeDeps(config)
      } finally {
   
        server._isRunningOptimizer = false
      }
      server._registerMissingImport = createMissingImporterRegisterFn(server)
    }
  }

  if (!middlewareMode && httpServer) {
   
    // overwrite listen to run optimizer before server start
    const listen = httpServer.listen.bind(httpServer)
    httpServer.listen = (async (port: number, ...args: any[]) => {
   
      try {
   
        // 执行所有插件的 buildStart方法
        await container.buildStart({
   })
        // 依赖预构建优化
        await runOptimize()
      } catch (e) {
   
        httpServer.emit('error', e)
        return
      }
      return listen(port, ...args)
    }) as any
  }
}

node/optimizer/index.ts:

import {
    build, BuildOptions as EsbuildBuildOptions } from 'esbuild'

export async function optimizeDeps(
  config: ResolvedConfig,
  force = config.server.force,
  asCommand = false,
  newDeps?: Record<string, string> // missing imports encountered after server has started
): Promise<DepOptimizationMetadata | null> {
   
  
 // 缓存目录存在一个保存预构建信息的配置 
  const dataPath = path.join(cacheDir, '_metadata.json')
  const mainHash = getDepHash(root, config)
  const data: DepOptimizationMetadata = {
   
    hash: mainHash,
    browserHash: mainHash,
    optimized: {
   }
  }
  // 强制重新预构建
  if (!force) {
   
    let prevData
    try {
   
      prevData = JSON.parse(fs.readFileSync(dataPath, 'utf-8'))
    } catch (e
  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
vite.config.ts是Vite项目的配置文件,用于配置的构建、开发和部署等参数。下面是vite.config.ts的一些常用配置项的详解: 1. `root`:指定项目根目录,默认为当前工作目录。 :指定项目在上的基础路径,默认为`/`。如果你的项目署在子路径下,可以通过设置该项来指定。 3. `publicDir`:指静态资源存放的目录,默认为public`。在该目录下的文件被复制到建输出目录。 4. `build`:用于配置构建相关的参数。 - `outDir`:指定构建输出目录,默认为`dist`。 - `assetsDir`:指定构建输出的静态资源目录,默认为`assets`。 - `sourcemap`:是否生成源映射文件,默认为`false`。 - `minify`:是否压缩构建输出,默认为`true`。 - `terserOptions`:用于配置Terser压缩器的选项。 - `cssCodeSplit`:是否将CSS代码拆分成单独的文件,默认为`true`。 - `rollupOptions`:用于配置Rollup打包器的选项。 5. `server`:用于配置开发服务器相关的参数。 - `host`:指定服务器主机名,默认为`localhost`。 - `port`:指定服务器端口号,默认为`3000`。 - `https`:是否启用HTTPS,默认为`false`。 - `proxy`:用于配置代理服务器的选项。 6. `plugins`:用于配置Vite插件。可以通过该选项来扩展Vite的功能。 7. `resolve`:用于配置模块解析相关的参数。 - `alias`:用于配置模块别名,可以简化模块引入的路径。 - `extensions`:指定模块的扩展名,默认为`['.js', '.jsx', '.ts', '.tsx', '.json']`。 8. `css`:用于配置CSS相关的参数。 - `preprocessorOptions`:用于配置CSS预处理器的选项。 以上是vite.config.ts的一些常用配置项的详解,你可以根据项目需求进行相应的配置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值