Open FionaRush opened 3 years ago
随着 Vue3 的热潮,Vite 也因“快”而红。Vite 最初是针对 Vue3 的一个打包编译工具,目前已经升级到可以支持大部分前端框架的打包编译。在 2021 年开工前一天,尤大大就给我们安排上了开工学习大礼包 —— Vite。一经发布就受无数人追捧,NPM 的下载量也是一路飙升。截止到目前,最近一周的下载量已经达到 5w+。
Vue3
Vite
NPM
Vite 是一种新型的前端构建工具,开箱即用,旨在为现代 Web 项目提供更快,更精简的开发体验。主要由两部分组成:
Web
ESM
HMR
API
回想一下目前的大部分项目,使用 webpack 构建工具,模块间通过 import 和 export 的方式导入导出,利用 webpack 静态资源分析得出完整的依赖图谱,再生对应的 bundle 文件。webpack做的就是分析代码。转换代码,编译代码,输出代码,最终形成打包后的代码,浏览器认识可执行的代码。
webpack
import
export
bundle
然而,天下苦 webpack 久矣。有时候会看见一名前端小伙伴,打开项目先 run serve ,然后去倒杯茶,慢慢悠悠地喝一口,这时候项目才启起来,在这短暂的登台后才能开始愉快的 coding。 webpack 的打包过程:
run serve
coding
Commonjs
AMD
CMD
AST
打包这一过程,就连热更新也需要先打包再展示在浏览器。打包构建过程图解如下。
对于 webpack 而言,打包过程必不可少。打包时间没有几分钟也有几十秒,随着项目越来越大,打包时间会长达几分钟之久,就连 HMR 也需要好几秒,可谓耗时耗力,就会出现“喝茶跑项目”的情况。
面对 webpack 这一问题,开发人员可以通过代码拆分等,通过优化依赖图谱,帮助 webpack 更好的静态资源分析,从而提升生产环境的性能。但是这一操作对开发环境,只提高了可读性,降低了维护成本,对性能并没有帮助;加之启动开发服务器慢,真正的开发中是一路坎坷。
针对打包慢这一问题,Vite 采取按需加载的方式解决。
主要是利用了浏览器对 ESM 的支持,这实际上是让浏览器接管了打包程序的部分工作。只有在真正需要使用的时候,才会进行打包处理,减少了页面加载出来前的打包时间,也有了更好的 HMR ,更快的冷启动。
Vite 会事先在本地启一个服务器,当浏览器执行到 import 后,才会去服务器读取该模块依赖,这就保证了只有在真正使用模块的时候,才会解析这个模块,对于暂时没有用到的模块,完全不参与构建的过程,最大程度做到按需加载。构建过程图解如下。
尽管服务器在处理这些请求时没有问题,但大量的请求,会拖慢页面的加载速度。 例如,lodash-es 有超过 600 个内置模块。当我们执行 import { debounce } from 'lodash-es' 时,浏览器会同时发出 600 多个 HTTP 请求,来引入所需模块。为解决这一问题,Vite 采用了依赖预构建。
lodash-es
import { debounce } from 'lodash-es'
HTTP
预构建就是在 run server 前,对需要的依赖进行预编译,在导入模块时,会动态引入该模块的预编译文件。具体的工作流程如下:
run server
首次启动 Vite,在没有配置 config 文件的前提下,Vite 会先解析 AST语法树里面使用到的依赖,将该依赖进行预编译;
config
将编译后的文件强缓存在内存中,./node_modules/.vite 文件下,在 run server 的时候,使用 HTTP 头来缓存请求得到的依赖;
./node_modules/.vite
解析后的依赖请求会在 HTTP 头 max-age=31536000,immutable 强缓存,一旦被缓存,下一次请求将直接读取缓存内容;若缓存失效,该依赖在启动前将进行新一次的预编译;
max-age=31536000,immutable
如果没有找到存在的缓存,Vite 会抓取源码,并自动发现依赖项导入(即 "裸引入"),将这些导入作为预构建的入口点。
当然了,如果某些不想进行某些依赖项目的打包,可以在 vite.config.js 文件中配置 optimizeDeps 对象内容,可以选择是否需要进行预编译的依赖的名称,Vite 也会根据 optimizeDeps 来确定是否对该依赖进行预编译。
vite.config.js
optimizeDeps
如果出于某些原因,需要强制 Vite 重新绑定依赖,使用 --force 命令强制重新打包依赖,或者手动删除 node_modules/.vite 目录。
--force
node_modules/.vite
看到这里可能会疑惑,Vite 不是不进行打包编译,为什么又进行了依赖与构建呢?这里我们可以把预编译依赖理解为一种优化,是一种没有可以但是有它更香的升级。关于预编译的两个目的,Vite 的文档上也阐述很清楚:
CommonJS
UMD
import React, { useState } from 'react'
Dev Tool
Network
冷启动开发服务器慢。webpack 基于打包器的方式,在提供服务前急切地抓取和构建整个应用;
Vite 缩短数倍的冷启动时间,启动开发服务器更快。
依赖
源码
esbuild
webpack:
Vite:
webpack 已经主宰多年,版本稳定;生态丰富,它的 loader 和 plugin 非常多;使用者较多;项目使用相对来说风险较低;
loader
plugin
Vite 初出茅庐,锋芒毕露,还在迭代优化升级中;在生态上不如 webpack,社区插件较少;目前较多的是观望者,没有大规模使用,很多问题还没有暴露出来,因此项目使用风险相对于 webpack 而言更高。
这样虽好,试想在请求大模块和多模块情况下,会进行多次请求就造成网络拥塞,导致页面加载速度变慢。
与 Vue CLI 类似,Vite 也提供用 npm 或者 yarn 来生成项目结构的方式。选择目录,打开终端,依次执行下面的命令构建脚手架项目,并启动项目。需要注意的是 Node.js 版本 >= 12.0.0。
Vue CLI
npm
yarn
Node.js
npm init @vitejs/app <project-name> yarn create @vitejs/app cd <project-name> npm install npm run dev
Vite 提供了一些预设模板,比如要构建一个 Vite + Vue 项目,运行:
Vite + Vue
# npm 6.x npm init @vitejs/app my-vue-app --template vue # npm 7+, 需要额外的双横线: npm init @vitejs/app my-vue-app -- --template vue # yarn yarn create @vitejs/app my-vue-app --template vue
目前支持的预设模板:
vanilla vue vue-ts react react-ts preact preact-ts lit-element lit-element-ts svelte svelte-ts
随着
Vue3
的热潮,Vite
也因“快”而红。Vite 最初是针对Vue3
的一个打包编译工具,目前已经升级到可以支持大部分前端框架的打包编译。在 2021 年开工前一天,尤大大就给我们安排上了开工学习大礼包 ——Vite
。一经发布就受无数人追捧,NPM
的下载量也是一路飙升。截止到目前,最近一周的下载量已经达到 5w+。What is Vite
Vite
是一种新型的前端构建工具,开箱即用,旨在为现代Web
项目提供更快,更精简的开发体验。主要由两部分组成:ESM
提供了 丰富的内建功能,如热更新HMR
;API
的设计,预配置输出高度优化的静态资源用于生产。Why use Vite
回想一下目前的大部分项目,使用
webpack
构建工具,模块间通过import
和export
的方式导入导出,利用webpack
静态资源分析得出完整的依赖图谱,再生对应的bundle
文件。webpack做的就是分析代码。转换代码,编译代码,输出代码,最终形成打包后的代码,浏览器认识可执行的代码。然而,天下苦
webpack
久矣。有时候会看见一名前端小伙伴,打开项目先run serve
,然后去倒杯茶,慢慢悠悠地喝一口,这时候项目才启起来,在这短暂的登台后才能开始愉快的coding
。webpack
的打包过程:Commonjs
、AMD
和CMD
,webpack
都会对其进行分析来获取依赖,构建依赖图谱;AST
抽象语法树;AST
阶段中去处理代码,把AST
抽象语法树变成浏览器可读的代码即bundle
文件。打包这一过程,就连热更新也需要先打包再展示在浏览器。打包构建过程图解如下。
对于
webpack
而言,打包过程必不可少。打包时间没有几分钟也有几十秒,随着项目越来越大,打包时间会长达几分钟之久,就连HMR
也需要好几秒,可谓耗时耗力,就会出现“喝茶跑项目”的情况。面对
webpack
这一问题,开发人员可以通过代码拆分等,通过优化依赖图谱,帮助webpack
更好的静态资源分析,从而提升生产环境的性能。但是这一操作对开发环境,只提高了可读性,降低了维护成本,对性能并没有帮助;加之启动开发服务器慢,真正的开发中是一路坎坷。How to solve it
针对打包慢这一问题,
Vite
采取按需加载的方式解决。主要是利用了浏览器对
ESM
的支持,这实际上是让浏览器接管了打包程序的部分工作。只有在真正需要使用的时候,才会进行打包处理,减少了页面加载出来前的打包时间,也有了更好的HMR
,更快的冷启动。Vite
会事先在本地启一个服务器,当浏览器执行到import
后,才会去服务器读取该模块依赖,这就保证了只有在真正使用模块的时候,才会解析这个模块,对于暂时没有用到的模块,完全不参与构建的过程,最大程度做到按需加载。构建过程图解如下。尽管服务器在处理这些请求时没有问题,但大量的请求,会拖慢页面的加载速度。 例如,
lodash-es
有超过 600 个内置模块。当我们执行import { debounce } from 'lodash-es'
时,浏览器会同时发出 600 多个HTTP
请求,来引入所需模块。为解决这一问题,Vite
采用了依赖预构建。依赖预构建
预构建就是在
run server
前,对需要的依赖进行预编译,在导入模块时,会动态引入该模块的预编译文件。具体的工作流程如下:首次启动
Vite
,在没有配置config
文件的前提下,Vite
会先解析AST
语法树里面使用到的依赖,将该依赖进行预编译;将编译后的文件强缓存在内存中,
./node_modules/.vite
文件下,在run server
的时候,使用HTTP
头来缓存请求得到的依赖;解析后的依赖请求会在
HTTP
头max-age=31536000,immutable
强缓存,一旦被缓存,下一次请求将直接读取缓存内容;若缓存失效,该依赖在启动前将进行新一次的预编译;如果没有找到存在的缓存,
Vite
会抓取源码,并自动发现依赖项导入(即 "裸引入"),将这些导入作为预构建的入口点。当然了,如果某些不想进行某些依赖项目的打包,可以在
vite.config.js
文件中配置optimizeDeps
对象内容,可以选择是否需要进行预编译的依赖的名称,Vite
也会根据optimizeDeps
来确定是否对该依赖进行预编译。如果出于某些原因,需要强制
Vite
重新绑定依赖,使用--force
命令强制重新打包依赖,或者手动删除node_modules/.vite
目录。看到这里可能会疑惑,
Vite
不是不进行打包编译,为什么又进行了依赖与构建呢?这里我们可以把预编译依赖理解为一种优化,是一种没有可以但是有它更香的升级。关于预编译的两个目的,Vite
的文档上也阐述很清楚:CommonJS
和UMD
兼容性。开发阶段中,Vite
的开发服务器将所有代码视为原生ESM
。因此,Vite
必须先将作为CommonJS
或UMD
发布的依赖项转换为ESM
。 当转换CommonJS
依赖时,Vite
会执行智能导入分析,这样即使导出是动态分配,按名导入也会符合预期效果。例如Vite
将有许多内部模块的ESM
依赖关系转换为单个模块,这样提高后续页面加载性能。减少模块间的依赖引用导致过多的请求次数。 继续上述例子lodash-es
,如果在没用依赖预编译的情况下,我们打开页面的Dev Tool
的Network
面板,可以看多大概有 600 个 相关依赖,请求花费了 1.11s。 通过预构建将lodash-es
打包编译为一个模块,就只需要一次HTTP
请求。预构建后的请求花费了 142ms 缩短了 7 倍多的时间,这里节省的时间,就是我们常说的冷启动时间。这使得Vite
的冷启动时间比webpack
要快得多。webpack VS Vite
服务器启动时间
webpack
冷启动开发服务器慢。
webpack
基于打包器的方式,在提供服务前急切地抓取和构建整个应用;Vite 缩短数倍的冷启动时间,启动开发服务器更快。
依赖
和源码
两类,改进了开发服务器启动时间;Vite
将会使用esbuild
预构建依赖和依赖缓存;ESM
的支持,按需加载,减少不必要的时间开支;热更新速度
webpack:
HMR
更新速度也会随着应用规模的增长而显著下降。Vite:
Vite
同时利用HTTP
头来加速整个页面的重新加载,依赖模块通过与构建进行缓存,减少再次请求,热更新较快。Vite
中,HMR
是在原生ESM
上执行的,HMR
不会随着规模的增长而改变速度;生态
webpack
已经主宰多年,版本稳定;生态丰富,它的loader
和plugin
非常多;使用者较多;项目使用相对来说风险较低;Vite
初出茅庐,锋芒毕露,还在迭代优化升级中;在生态上不如webpack
,社区插件较少;目前较多的是观望者,没有大规模使用,很多问题还没有暴露出来,因此项目使用风险相对于webpack
而言更高。这样虽好,试想在请求大模块和多模块情况下,会进行多次请求就造成网络拥塞,导致页面加载速度变慢。
How to use
与
Vue CLI
类似,Vite
也提供用npm
或者yarn
来生成项目结构的方式。选择目录,打开终端,依次执行下面的命令构建脚手架项目,并启动项目。需要注意的是Node.js
版本 >= 12.0.0。Vite
提供了一些预设模板,比如要构建一个Vite + Vue
项目,运行:目前支持的预设模板: