Vite原理浅析

什么是 Vite

Vite 是新型的前端构建工具,是 Vue3 作者尤雨溪备受关注的新作品,考虑到【懒编程】的读者多数都是后端,为了方便大家理解,在谈 Vite 前,先简单理一下前端现在大体的状况。

几年前,前端开发还是在 HTML、CSS、JS 的时代,那个时代下,大家通常使用 JQuery 来写页面逻辑,用 Bootstrap 来构建页面布局,没有什么编译、打包、热加载之类的概念,后来 Node.js 出现了,JavaScript 基于 Node.js 可以编写服务端程序了,Express、Kos 等 Node.js 领域的 Web 开发框架也出现了,从此前端走上了工程化的不归路。

前端之所以工程化,主要是为了提高开发效率和维护成本,相比于 JQuery 时代,现在开发前端项目配合各种框架、手脚架和 CSS 样式库,可以快速搭建一个美观的网站,而 Vite 便是工程化开发前端项目流程中的用于构建项目的工具。

为了理解 Vite 的设计优势,本文会从浏览器 ES Modules 开始讨论,然后与 Webpack 这个使用最广泛的打包构建工具进行对比,最后使用 Vite 并聊一下其实现原理。

ES Modules

随着前端工程的复杂化,很多逻辑写在一块难以维护,所以前端开始分模块,ES6 规范中有 modules 的概念,然后可以通过 import 关键字导入其他模块,但 ES6 推出挺长一段时间内,都只有 Node.js 中才能无阻的使用 improt,而浏览器却不支持,这个现象一直持续到 2018 年 8 月份,此后部分浏览器便支持 import 语法了,至今多数浏览器都支持 import 语法,如下图:

Vite原理浅析_第1张图片举一个简单的理解来理解浏览器对 ES Modules 的支持。

在 a.html 中,写入如下 script 代码:


  // 导入firstBlood模块
  import { pColor } from './firstBlood.mjs';
  // 设置颜色为红色
  pColor('red');

对于引入模块部分的 script,需要将 type 设置为 module,此外,导入的 JS 模块文件其后缀为 mjs,这其实是一种约定,直接使用以 js 后缀结尾的文件也是可以的。

firstBlood.mjs 代码如下:

// export一个改变

元素颜色的方法 export function pColor (color) {   const p = document.querySelector('p');   p.style.color = color; }

当浏览解析到 import { pColor } from './firstBlood.mjs'; 时,会向当前域名发起一个获取相应资源的请求,这个请求具体的 URL 为 http:/localhost:3000/src/firstBlood.mjs

有了浏览器对 ES Modules 支持的前置知识,可以更好的明白,相比于其他构建工具Vite 的优势。

Webpack 打包慢的问题

Webpack 是前端领域使用最广的项目构建工具,其工作流程如下图:

Vite原理浅析_第2张图片当我们在开发项目时,通常会遇到少量的代码改动,只要一改动,webpack 就会将项目中所有代码进行重新打包,然后再通过热更新(Hot Module Replacement, HMR)的方式推送到浏览器页面中,这样开发者不需要重启服务,页面也不会全部刷新,页面中的各种状态变量也都还在,但一个愈发严重的问题便是,webpack 打包速度比较慢,对于稍大的前端项目,项目里可以依赖着上千个第三方库,随着项目增大,webpack 打包速度会越来越慢,造成热更新越来越慢,可能你改一点代码,需要等十几秒钟。

对于这种情况,常见的做法便是使用 thread-loader、cache-loader 或代码分片的方式进行优化,但随着项目进一步扩大,这些优化会达到瓶颈。

Vite 基于浏览器原生的 ES Module 做到真正的按需加载,从根上解决热更新的问题。

Vite 实现原理

使用 Vite 构建前端项目时,Vite 会在一开始将项目中的代码分为依赖和源码两大类。

依赖即开发项目时使用的第三方库,一个稍大的项目会有成百上千个依赖,处理这些依赖库的性能代价是比较高的,而且不同依赖库使用的模块化规则不同(如 UMD、ESM 或 CommonJS),所以还需要将其转成相关的模块化规则。

对于依赖的处理,我们通常称为依赖预构建,Vite 使用 esbuild 来实现依赖预构建,将 CommonJS 和 UMD 的依赖库转为 ESM 形式,此外因为依赖库大多数时候不会变化,Vite 会将构建好的依赖存到 node_modules/.vite 目录中,如果依赖变化(package.json 等文件中依赖变化了)则会重新构建。

Vite 使用 esbuild 这个全新 js 打包工具的原因是该工具使用 Go 开发,相比于 gulp、rollup 等使用 JS 开发的传统打包工具,esbuild 会快上十几倍,因为打包其实是 CPU 密集型操作,编译型语言会比动态语言快很多(目前前端的一个趋势便是使用编译型语言开发的工具去替代已有的一些工具)。

esbuild 目前还比较新,虽然重要的功能都有了,但还是有部分功能缺失,如对 CSS 处理还不太好,所以 Vite 目前只是要 esbuild 实现开发期间的构建操作,项目开发完要正式构建时使用的是 rollup,rollup 非常成熟,虽然慢一点,但正式构建在开发过程中不会太频繁。

对于源码,通常就是我们开发项目时自己写的 JS 代码,这些代码时常被修改,但并不是每次修改都需要重新加载所有源码的,Vite 利用浏览器 ES Modules(ESM),当浏览器发起相应模块的请求时,Vite 内置的基于 Koa 构建的 web 服务器会拦截 ES Module 请求,并通过 path 找到想要目录的文件,通过简单的处理再返回给浏览器。

Vite 跳过了打包的动作,Webpack 每次需要将代码打包成一个完整的 JS,当项目变大时,打包就会变慢,这是根本原因,而 Vite 在开发阶段构建项目时会将其构建成 ESM 的形式,这让浏览器来决定什么使用要加载什么模块,然后 Vite 拦截并处理浏览器对模型加载的请求,从而实现真正的按需加载,不再需要打包。

通过 Vite 官网中的两张图,可以更清晰的理解其中的差异:

Vite原理浅析_第3张图片上图表示的是打包类型的前端构建工具,需要先将模块打包(Bundle)成一个整体,然后再提供出去。

Vite原理浅析_第4张图片而 Vite 这类基于浏览器 ESM 形式的构建工具,利用浏览器实现了按需加载,相比打包类型的构建工具快上很多,而且随着项目的增大,热更新等也不会变慢,大大提高了开发效率。

虽然浏览器对 ESM 的支持已经很广泛了,但 Vite 还是选择在生产环境时使用 rollup 来打包,因为在生产环境下,使用未打包的 ESM 会产生比较多的 HTTP 请求,相对打包而言,效率还是比较低下的,所以 Vite 上生产依旧打包,并使用了 tree-shaking、懒加载等技巧让上生产环境的代码更加优雅。

简单而言,Vite 对开发环境与生产环境提供了不同的解决方案,其最终目的是提高开发效率。

简单使用 Vite

这里,我使用 yarn 来构建 Vite 项目

yarn create vite my-vue-app --template vue-ts
cd my-vue-app
yarn
yarn dev

上述命令中,使用 yarn create 获取 vite 并通过 vite 构建名为 my-vue-app 的项目,使用 vue-ts 模板,即使用 TS 来开发基于 Vue 框架构建的前端项目,创建完项目后,cd 进入,然后安装依赖并运行项目。

Vite原理浅析_第5张图片

你可能感兴趣的:(vue,python,java,javascript,web)