vite介绍
vite是基于原生的ES-Module的构建工具
vite是一个开发构建工具,开发过程中它利用浏览器native ES Module特性导入组织代码,生产中利用rollup作为打包工具,它有如下特点:
- 光速启动
- 热模块替换(HMR)
- 按需编译
为什么使用vite
在浏览器支持ES模块之前,JavaScript并没有提供原生的机制,让开发者以模块化的方式开发。
打包就是使用工具抓取处理,把我们的原代码模块,串联成在浏览器可以运行的文件。
| 开发环境下 | 服务器启动 | 更新 |
|---|---|---|
| vue-cli | 优先抓取构建整个应用。依赖方面:大多数时候,依赖都是在开发过程中,不变的、纯的js代码,并且一些较大的依赖处理代价很高,例如有上百个模块组件库。依赖也存在多种模块化的格式,比如ESM、CommonJS这样的格式等。源码方面:除了纯的js文件,例如jsx、cssvue、这些文件还需要转换、或者是编辑。不是所有的源码都需要被同时的加载,例如基于路由的拆分的代码模块。这种对源码和以来的处理方式,势必会导致服务器启动慢。 | 基于打包器启动时,重建整个包的效率是很低的。这样的更新速度会随着应用体积增长而直线下降。一些打包器的开发服务器,会将构建内容存入内存,比如webpack。这样只需要在文件更改的时候,是一部分模块失活。但它任需整个重新构建,并重载页面。这样代价是很高的,并且重新加载页面,会消除应用的当前状态,所以打包器支持了动态模块热重载,即HMR。它允许一个模块热替换它自己,而不会影响页面的其余部分,这就大大改进了开发体验。比如webpack-dev-server,就提供了HMR这样的功能。然而在实践中会发现,就是采用了HMR这样的模式,其热更新的速度也会随着应用规模的增长而显著下降。 |
| vite | vite在一开始会将应用中的模块区分为源码和依赖两类。vite使用ESBuild来构建依赖,ESBuild使用go语言(编译型语言)编写,比JavaScript编写的打包器快10-100倍。vite以原生的ESM提供源码,实际是让浏览器接管了部分的打包工作。vite只需要在浏览器请求源码的时候,进行转换,并按需提供源码,根据场景动态的导入代码。 | vite的HMR是在ESM上执行的,当编辑一个文件的时候,vite只需要精确的使已编辑的模块与最近的HMR边界时间的链失活,无论应用大小,HMR始终保持快速更新,因为大多数时候,指示模块本身被更新了。vite同时利用http头来加速整个页面的重新加载。源码模块的请求,会根据304来进行协商缓存,而依赖模块的请求会通过Cache-Control来进行强缓存。一旦被缓存,他们将不需要再次请求,即浏览器可以做更多的事情。 |
| 生产环境下 | |
|---|---|
| 为什么生产环境下任需打包? | 生产环境下不能直接使用开发环境的玩法。尽管原生的ESM得到广泛支持,但是由于嵌套导入,会导致额外的网络往返。再生产环境中发布未打包的ESM任然效率低下,及时使用了http/2也是如此,为了在生产环境中获得最佳的加载性能,最好还是将代码进行tree-shaking、懒加载chunk分割等操作。其中chunk分割的目的,是获得更好的缓存,要确保开发服务器和生产环境构建之间的最有输出和行为一致,并不容易。所以vite附带了一套构建优化的构建命令,开箱即用。 |
| 为什么不适用ESBuild打包? | 虽然ESBuild速度快,并且在构建库方面是一个比较出色的工具了。但一些针对构建应用的重要功能,任在持续开发中,特别是代码分割和CSS处理方面。Rollup在应用打包方面更加成熟和灵活。尽管如此,官方也表示,在未来这些功能稳健以后,也不排除使用ESBuild作为生产构建其的可能 |
npm依赖解析和预构建
import { createApp } from 'vue' 直接放在浏览器中是不能执行的,通过vite来帮忙。vite会检测所有的源文件当中,此类裸模块的导入,并执行以下两个操作:预构建、重写url。预构建可以页面加载速度,并且将CommonJS UMD转化为ESM格式。预构建是由esbuild来执行的,这使得vite的启动时间比任何JavaScript的打包器都要快。重写导入为合法的url,将import { createApp } from 'vue'的路径改写为/node_modules/.vite/deps/vue.js?v=46b0d5b8,以便浏览器可以正确的导入他们。
"依赖预构建"过程的两个目的
- 完成CommonJS和UMD兼容性 在开发过程中,vite会将所有的代码视为原生的ES模块,vite必须先将作为CommonJS和UMD发布的依赖项转换为ESM。当转换CommonJS依赖的时候,vite会进行智能的导入分析,这样即使导出的是动态分配的表示也能达到预期的效果。
- 性能方面
vite会将许多内部的模块的ESM依赖关系,转换为单个的模块,以提高我们后续页面加载的性能。一些包将它们的ES模块的构建作为许多单独文件相互的导入,比如ES这里我们有一个lodash,那lodash es再去导入的时候,其实上有超过600个内置的模块需要去导入。执行
import { debounce } from 'lodash-es',会同时发出600多个http请求。尽管服务器端在处理这些请求的时候是没有问题的,但是大量的请求会在浏览器端造成拥塞,导致页面的加载速度相当慢。通过预构建lodash-es,把它变成一个模块,只需要做一次http请求即可。
esbuild特点
- 极快的速度而不需要缓存
- ES6和CommonJS模块支持
- ES6模块的tree shaking
- 一个用于JavaScript和Go的API
- TypeScript和JSX语法支持
- Source maps支持
- 简化打包
- 插件支持
vite缓存
vite默认在模块预构建完成之后,会对构建好的模块进行缓存。
- vite在服务器端,是通过文件系统来实现缓存的,vite会将与构建的依赖缓存到node_modules/.vite这个文件夹下。
- package.json文件中的dependencies列表
- pnpm-lock.yaml文件
- vite.config.ts相关字段的配置
当发生以上情况时,就会重新进行预构建。如果想要强制进行预构建,使用--fource命令启动服务器。这个命令会强制本地服务端的依赖的缓存,即把依赖的模块的缓存都清理调,重新生成。或者通过手动把.cache文件删除
- vite在浏览器端,通过解析后的依赖请求,以HTTP头的max-age=315360000immutable来实现强缓存。
- 通过控制台操作禁用缓存
- 通过--fource命令(同上)
- 重新载入页面,也会将缓存清理
模块热重载
vite提供了一套原生的ESM的HMR API。vite内置了HMR到vue单文件组件(SFC),React Fast Refresh,@prefresh/vite
vite.config.js中自动设置了import vue from '@vitejs/plugin-vue',defineConfig配置项中声明了plugins: [vue()],这样项目就自动具备了HMR的效果。
CJS, AMD, UMD 和 ESM是什么
TypeScript
vite天然支持.ts文件引入,vite仅仅支持.ts文件的转译工作,并不执行任何类型检查。
静态资源处理
- ?url
- ?raw 【直接引入文件全部代码】
- new URL()
- public目录 【这个目录下的资源文件可以直接在页面上使用】
json和glob导入
// 表示导入./components/glob文件夹下所有的文件
const modules = import.meta.glob('./components/glob/*')
console.log('modules', modules)
// 打印结果如下图,是一个对象,key是文件的目录名字,value是对应的导入函数
1 创建新项目 (vite 3.0版本)
1. yarn create vite <项目名>
2. cd <项目名>
3. pnpm install 【npm install, yarn install】
4. pnpm dev 【npm run dev, yarn dev】
5. pnpm build 打包
6. pnpm preview 预览
2. 文件目录结构
.gitignore文件:上传git忽略的内容
index.html文件:在根目录下,是项目的入口文件,<script type="module" src="/src/main.ts"></script>。mian.js中使用了es module,即es模块的import语法,不能直接在浏览器上使用,需要设置type="module" 。【如果使用webpack构建项目,index.html是放在public文件下的】
package.json文件:定义name、version,开发、生产依赖以及npm脚本
pnpm-lock.yaml文件:如同 yarn.lock、package-lock.json 一样,可以为项目提供一份各个依赖稳定的版本信息
README.md文件:帮助(使用)文档
tsconfig.json文件:TypeScript 编译的相关配置
vite.config.ts文件:vite的相关配置
2. 配置vite.config.ts文件
2.1 遇到问题
😈 找不到模块“path”或其相应的类型声明。
原因:path模块是node.js内置的功能,但是node.js本身并不支持ts
解决方案:安装@types/node
pnpm install @types/node -D
2.2 build.minify配置
- 类型:
boolean | 'terser' | 'esbuild' - 默认:
'esbuild'
设置为false可以禁用最小化混淆,或是用来指定使用哪种混淆器。虽然 Terser 相对较慢,但大多数情况下构建后的文件体积更小。ESbuild 最小化混淆更快但构建后的文件相对更大。它比 terser 快 20-40 倍,压缩率只差 1%-2%。
注意,在 lib 模式下使用'es'时,build.minify选项不会缩减空格,因为会移除掉 pure 标注,导致破坏 tree-shaking。 当设置为'terser'时必须先安装 Terser。
pnpm add -D terser
2.3 mock数据配置
安装依赖:
yarn add vite-plugin-mock mockjs -D
import { viteMockServe } from 'vite-plugin-mock'
// 配置项
viteMockServe({
// default
mockPath: 'mock',
// localEnabled: command === 'serve',
}),
在根目录下新建mock文件夹,index.ts文件(写测试数据)
// test.ts
import { MockMethod } from 'vite-plugin-mock'
export default [
{
url: '/api/get',
method: 'get',
response: ({ query }) => {
return {
code: 0,
data: {
name: 'vben',
},
}
},
},
] as MockMethod[]
安装axios
pnpm add axios
根目录下新建.env.development文件 (开发环境加载的配置文件)
VITE_BASE_API=http://api.test.xxx.xxx.com/api
根目录下新建.env.production文件 (生产环境加载的配置文件)
VITE_BASE_API=http://api.test.xxx.xxx.com
测试mock数据
import axios from 'axios'
const getApi = async() => {
const { data } = await axios.get('/api/get')
console.log('测试api', data)
console.log('测试 base_api', import.meta.env.VITE_BASE_API)
}
getApi()
2.3 gzip压缩配置
安装插件
pnpm add vite-plugin-compression -D
// gzip压缩插件
import viteCompression from 'vite-plugin-compression';
export default defineConfig({
plugins: [
vue(),
// gzip压缩
viteCompression(),
],
})
pnpm build打包之后可以看到gzip压缩前后的体积大小
2.4 图片压缩打包
安装插件
pnpm add vite-plugin-imagemin -D
// 图片压缩插件
import viteImagemin from 'vite-plugin-imagemin'
export default defineConfig({
plugins: [
vue(),
// 压缩图片
viteImagemin({
gifsicle: {
optimizationLevel: 7,
interlaced: false,
},
optipng: {
optimizationLevel: 7,
},
mozjpeg: {
quality: 20,
},
pngquant: {
quality: [0.8, 0.9],
speed: 4,
},
svgo: {
plugins: [
{
name: 'removeViewBox',
},
{
name: 'removeEmptyAttrs',
active: false,
},
],
},
}),
],
})
pnpm build打包之后可以看到图片压缩前后的体积大小
2.5 格式化
安装相关插件
- @typescript-eslint/eslint-plugin 它作为 eslint 默认规则的补充,提供了一些额外的适用于 ts 语法的规则。
- @typescript-eslint/parser 是 一个 ESLint 解析器,它可以将 TS 源代码解析为 TypeScript ESTree,然后在 TypeScript ESTree 之上构建规则。
- eslint-plugin-vue 是对 .vue 文件进行代码校验的插件
pnpm add eslint prettier @typescript-eslint/eslint-plugin @typescript-eslint/parser @vue/eslint-config-prettier @vue/eslint-config-typescript babel-eslint eslint-config-prettier eslint-plugin-prettier eslint-plugin-vue -D
.eslintrc.js文件
module.exports = {
// 是否开启 eslint
root: true,
env: {
browser: true,
node: true,
es6: true,
},
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/typescript/recommended',
'@vue/prettier',
],
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
},
rules: {
'prettier/prettier': 'error',
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/indent': ['off'],
'@typescript-eslint/no-unused-vars': ['error'],
},
globals: {
defineProps: 'readonly',
defineEmits: 'readonly',
defineComponent: 'readonly',
defineExpose: 'readonly',
},
}
2.6 共享配置
项目运行会先查找根目录下的.vscode文件夹,执行里面的文件配置。如果没有.vscode文件夹,会按照本地的配置执行。