本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究!

前语

上篇咱们说到webpack相关的规划和完成,Vite的呈现确实动摇了一下老大哥webpack的江湖地位,从2021年2月份Vite2更新,到2022年7月份Vite3发布,到今年2022年11月份Vite 4.0.0-alpha.0 (2022-11-07)开端更新,不得不说,Vite在改善前端开发体会的道路上越来越卷了,下面咱们还是带着问题去探究Vite的中心规划和完成:

前端工程化基建探究(5)Vite4.0.0都悄然在“卷”出来了,是时分去探究Vite的规划和完成了

一、Vite号称下一代的前端工具链,处理了什么问题?

1、改进了前端开发服务器发动时刻

Vite 经过在一开端将运用中的模块区分为依靠源码两类,改进了传统工具,在开发服务器发动时刻

依靠,Vite 将会运用esbuild预构建依靠,esbuild 运用 Go 编写,而且比以 JavaScript 编写的打包器预构建依靠快 10-100 倍

前端工程化基建探究(5)Vite4.0.0都悄然在“卷”出来了,是时分去探究Vite的规划和完成了

源码,这儿通常是指一些需求转化的文件(比方JSX,CSS 或者 Vue/Svelte 组件),Vite 以原生 ESM办法提供源码,让浏览器接收打包程序的作业,并按需提供源码。

二、Vite发动都做了什么?

注: 本文vite源码版本为 v3.2.4

因为在之前的文章有写类似检查源码的办法,这儿就快速定位 packages/vite/src/node/cli.ts

前端工程化基建探究(5)Vite4.0.0都悄然在“卷”出来了,是时分去探究Vite的规划和完成了
可以看到这儿创立server,是经过引证packagesvitesrcnodeserverindex.ts 里的createServer() 办法,咱们要点看看这个办法首要干了什么事情

前端工程化基建探究(5)Vite4.0.0都悄然在“卷”出来了,是时分去探究Vite的规划和完成了

咱们看着这段代码往下读:

...
 const config = await resolveConfig(inlineConfig, 'serve', 'development')
 ...

(1)首要会经过resolveConfig函数解析发动服务时分需求的装备,这包括plugins用户插件和内建插件、cacheDirnpm 依靠预构建之后的缓存目录、在之后浏览器按需获取文件时对恳求进行截获,回来相对应内容的处理函数createResolve,以及界说在vite.config.js里边的resolve,包括用户自界说的一些alias文件的处理等。

...
  const middlewares = connect() as Connect.Server
  const httpServer = middlewareMode
    ? null
    : await resolveHttpServer(serverConfig, middlewares, httpsOptions)
  const ws = createWebSocketServer(httpServer, config, httpsOptions)
  ...

(2)创立httpServerws服务,创立watcher,设置代码文件监听,创立server目标, 文件监听改变,websocket向前端通讯。然后注册一系列中间件用于处理浏览器恳求,包括对/js/css/vue的恳求等

...
  const container = await createPluginContainer(config, moduleGraph, watcher)
  ...

(3)创立插件处理中心container

...
 const server: ViteDevServer = {
 ...
  }
  server.transformIndexHtml = createDevHtmlTransformFn(server)
  ...

(4)处理html进行转化,在html文件中注入咱们在localhostnetwork 面板中看到的<script type="module" src="https://juejin.im/@vite/client"></script>脚本,运转vite相关的client脚本内容。

前端工程化基建探究(5)Vite4.0.0都悄然在“卷”出来了,是时分去探究Vite的规划和完成了

(5)在服务发动前,首要履行container.buildStart({})调用一切注册插件的buildStart钩子函数,然后运转initDepsOptimizer()优化预构建的依靠,接收来自浏览器的恳求。

三、解析经过源码vite的中的一些原理

3.1 Vite为什么要做预构建?

个人认为这是Vite在追求极速的服务发动,牺牲首屏渲染速度之间的一个挑选。完成Vite 针对用户项目中的各种文件都是不做打包处理的,而是在浏览器运转时按需恳求,并进行转化处理,相比其他构建工具需求打包依靠再发动服务来说,在耗时上是质的腾跃。

前面咱们对vite发动进程的剖析知道,发动Vite它会搜集处理config、注册各种中间件、初始化一些之后会用到的插件容器container以及模块依靠图moduleGraph等事情都不耗费时刻的,最耗费时刻的就依靠预构建。咱们可以简略看一下Vite的预构建做了那些事情

前端工程化基建探究(5)Vite4.0.0都悄然在“卷”出来了,是时分去探究Vite的规划和完成了

(1) 缓存判别,node_modules.vite_metadata.json看看是不是有前次缓存的预构建成果,经过判别存储缓存信息文件hash值是否与最新的hash值相同,要是相同则回来前次缓存的预构建成果

(2)依靠扫描,假如没有缓存成果和缓存hash值不一致,经过discoverProjectDependencies()进行依靠扫描,从入口开端搜集依靠,得到deps,将这些依靠项添加到已发现的列表中,preAliasPlugin用来支撑别号和优化的deps。

前端工程化基建探究(5)Vite4.0.0都悄然在“卷”出来了,是时分去探究Vite的规划和完成了

(3)构建依靠,经过esbuildDepPlugin()在这个进程中它将将非ESM标准的代码转化为契合ESM标准的代码,将第三方依靠内部的多个文件合并为单一的可缓存文件,削减http恳求数量。

前端工程化基建探究(5)Vite4.0.0都悄然在“卷”出来了,是时分去探究Vite的规划和完成了

网上有人专门拿出import { debounce } from "lodash-es"做试验,直接运用会时浏览器会导入600+个文件,需求1秒多,而经过依靠预构建之后,浏览器只需求导入一个文件,且只需 20 ms

(4)最后,履行writeFile,再将相关信息保存到_metadata.json,咱们可以看看它的内容

{
  "hash": "1a547ddf",
  "browserHash": "2065b8ab",
  "optimized": {
    "@vue/runtime-core": {
      "file": "E:/codeWorlk/xxx/node_modules/.vite/@vue_runtime-core.js",
      "src": "E:/codeWorlk/xxx/node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js",
      "needsInterop": false
    },
    "vue": {
      "file": "E:/codeWorlk/xxx/node_modules/.vite/vue.js",
      "src": "E:/codeWorlk/xxx/node_modules/vue/dist/vue.runtime.esm-bundler.js",
      "needsInterop": false
    },
    "vue-router": {
      "file": "E:/codeWorlk/xxx/node_modules/.vite/vue-router.js",
      "src": "E:/codeWorlk/xxx/node_modules/vue-router/dist/vue-router.esm-bundler.js",
      "needsInterop": false
    },
    "@vue/reactivity": {
      "file": "E:/codeWorlk/xxx/node_modules/.vite/@vue_reactivity.js",
      "src": "E:/codeWorlk/xxx/node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js",
      "needsInterop": false
    }
  }
}

从上面咱们可以看到 Vite 只对 npm 依靠进行预构建,关于用户编写的文件不进行预处理,而是经过浏览器支撑的 ES Module 来进行按需读取,所以假如用户文件过多,且没有进行必定的 Code Spliting 等操作。这时分首屏加载渲染是十分慢的。

3.2 一个恳求到Vite服务的进程是怎样样的?

<script type="module" src="/src/main.js"></script>
// or
import { get } from './utils'

咱们经过上文Vite创立服务的进程源码看到 经过transformMiddleware()函数去处理恳求


// main transform middleware
middlewares.use(transformMiddleware(server))

前端工程化基建探究(5)Vite4.0.0都悄然在“卷”出来了,是时分去探究Vite的规划和完成了
(1)首要它会判别是否恳求, 假如是恳求,就会进入运用插件容器解析、加载和转化,不是恳求就交给其他插件处理 (2)transformRequest()进行解析、加载和转化恳求 (3)判别依靠图谱中是否现已存在,存在的话直接回来module
(4)没有话调用插件的路径解析钩子(关于不同的资源会有不同的插件去处理),创立module并保存 (5)最后调用esbuildPlugin插件的transfrom钩子,回来编译后的code,并缓存到依靠map里

(3)Vite是怎样完成快速的热重载(HMR)?

HMR的原理大多差不多,首要是经过WebSocket创立浏览器和服务器的通讯监听文件的改动,当文件被修正时,服务端发送音讯告诉客户端修正相应的代码,客户端对应不同的文件进行不同的操作的更新。 咱们回顾发动Vite服务的(createServer)时分 const ws = createWebSocketServer(httpServer, config, httpsOptions)就发动了WebSocketServer服务,然后监听文件的改变

  watcher.on('change', async (file) => {
    file = normalizePath(file)
    if (file.endsWith('/package.json')) {
      return invalidatePackageData(packageCache, file)
    }
    // invalidate module graph cache on file change
    moduleGraph.onFileChange(file)
    if (serverConfig.hmr !== false) {
      try {
        await handleHMRUpdate(file, server)
      } catch (err) {
        ws.send({
          type: 'error',
          err: prepareError(err)
        })
      }
    }
  })

假如有改变,则则履行监听回调函数handleHMRUpdate(),从头打包文件,并告诉到客户端,客户端接收到信息,从头加载到更新后的模块。

最后

Vite4.0.0都悄然在“卷”出来了,期待它在一次次更新中对前端人愈加的友好,本文也是浅读源码其间的中心流程,更多细节,比方Vite的插件机制是怎样样的,具体是怎样样做文件扫描的,都值得各位前端人探究!

下一篇: 前端工程化基建探究(6)前端人对前端财物建造的思考和实践