介绍
Vite 和 Webpack 一样,也是个前端工程化构建工具。根据官方文档的描述它由两部分组成:
- 一个开发服务器,它基于原生 ES 模块提供了丰富的内建功能,如速度快到惊人的模块热更新(HMR)。
- 一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。
对于生产构建,vite 的目标浏览器是:
支持原生 ES 模块、原生 ESM 动态导入和 import.meta。
现代浏览器大多已经能够支持 ES6+ 的语法,我们只需要在 index.html 使用 <script src>
引入 js 文件时,添加 type
属性为 module
,即可以支持 ES Module 的模块化方案:
html
<!-- index.html -->
<script src="./index.js" type="module"></script>
所以在开发项目时,没必要再像 webpack 那样将 es6+ 的语法使用 babel 等工具转成 es5。但是 vite 也不是什么都没做就直接把我们编写的 js 代码扔给浏览器了,因为还有一些问题需要处理,比如:
- 在
import
文件时必须添加后缀名,也就是不能省略.js
; - 如果导入的文件又依赖其它的文件,那么每个 js 文件都会被依次加载,影响效率;
比如使用了 lodash 库,这里安装的是使用 ES Module 规范的 lodash-es,导入时也是直接去找到导出了 lodash
对象的 lodash.default.js:
javascript
// index.js
import _ from './node_modules/lodash-es/lodash.default.js'
const add = (augend, addend) => {
return _.add(augend, addend)
}
console.log(add(1, 2))
lodash.default.js 里又导入了 array.js、collection.js 等,array.js 里又需要依赖其它文件,这些文件都会被一个个加载:
- 无法处理 .ts 或 .vue 等文件。
借助 vite,就能帮我们解决上述这些问题。
安装与使用
初体验
先使用 pnpm 直接安装个 vite 来体验下:
powershell
pnpm init
pnpm add vite -D
安装成功后可以在 node_modules.bin 目录下看到已经有了名为 vite 的可执行文件:
所以可以直接执行:
powershell
npx vite
vite 就会开启开发服务器并构建我们的项目。现在,在导入文件时,就可以去掉后缀名,并且直接从 lodash-es 导入:
javascript
import _ from 'lodash-es'
const add = (augend, addend) => {
return _.add(augend, addend)
}
console.log(add(1, 2))
vite 会将 lodash 相关文件都放到一个文件去,这样无疑减少了 http 请求,提高了效率:
处理 ts 文件
vite 对 ts 是天生支持的,无需像在 webpack 中使用时还要安装 typescript 和 ts-loader。比如项目中有 ts 代码:
typescript
// utils\index.ts
export function sayHello(msg: string) {
console.log(msg)
}
javascript
// index.js
import { sayHello } from './utils/index'
const msg = 'hello juejin'
sayHello(msg)
在开发阶段,vite 会使用 ESBuild 将 ts 文件转换成 js 代码,然后通过使用 connect 创建的开发服务器将对 utils/index.ts 的请求进行转发,将转换成 js 的文件返回给浏览器,所以最终浏览器得到的 utils/index.ts 里其实都是 js 代码,可以直接解析:
甚至在 index.html 的 <script>
引入的文件都可以直接是 ts 文件:
html
<!-- index.html -->
<script src="./index.ts" type="module"></script>
处理样式文件
vite 不需要额外的配置就能处理 css、sass 和 less 等文件,但是如果使用了 sass 或 less 还是需要安装这些预处理依赖的。比如在 index.js 导入了 css 和 scss 文件:
javascript
import './styles/main.css'
import './styles/main.scss'
那么就需要安装好 sass:
powershell
npm i sass -D
如果要使用 PostCSS,也是只要安装了 postcss 和其依赖的插件,比如 postcss-preset-env,然后再配置下 postcss.config 即可,而不需要像 webpack 那样还要去 webpack.config 配置什么 loader:
powershell
npm i postcss postcss-preset-env -D
javascript
// postcss.config.js
module.exports = {
plugins: [require('postcss-preset-env')]
}
处理 vue 文件
如果我们的项目里有 vue 文件:
vue
<!-- vue\App.vue -->
<script setup>
import { ref } from 'vue'
const msg = ref('Hello')
</script>
<template>
<div>{{ msg }}</div>
</template>
javascript
// index.js
import { createApp } from 'vue'
import App from './vue/App.vue'
const app = createApp(App)
app.mount(document.querySelector('#app'))
我们需要先安装 vue:
powershell
pnpm add vue
并且安装处理 vue 文件的插件 @vitejs/plugin-vue:
powershell
pnpm add @vitejs/plugin-vue -D
然后在 vite.config.js 中使用插件,这里直接使用 ES Module 的语法来导出对象,因为 vite5 中 CJS 的 Node API 已经被废弃:
javascript
import vue from '@vitejs/plugin-vue'
export default {
plugins: [vue()]
}
如果是使用脚手架工具 create vite 创建的 vue 项目,即:
powershell
pnpm create vite
在生成的 vite.config.ts 中代码如下,可以看到和上面我们自己写的差不多,但是使用了 defineConfig
这个方法,好处在于编写时会有提示,不容易敲错:
typescript
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
})
在浏览器查看效果时,可以看到,浏览器获取的 App.vue 文件里的内容其实也是被替换成了 js 代码:
打包与预览
执行如下命令便可对项目打包:
powershell
npx vite build
打包完成后可以执行如下命令预览:
powershell
npx vite preview
自动导入文件
下面再顺便说一个 vite 如何自动导入文件的功能。
在使用 vite 构建的 vue + ts 项目中,当需要定义路由时,因为我是将各个 route 对象抽离到了 src\router\modules 目录下定义,然后在 src\router\index.ts 通过 router.addRoute
动态添加的:
所以需要在 index 文件自动地去获取 modules 目录下的各个 route 对象。这一功能如果是在原来使用 vue-cli 搭建的项目中,可以使用 webpack 的 require.context()
,而在 vite 中,则是使用 import.meta.glob()
:
typescript
const modulesFiles: Record<string, any> = import.meta.glob('./modules/*.ts', {
eager: true
})
如果单独打印 console.log(import.meta.glob('./modules/*.ts'))
,会发现其结果为一个对象,里面的 key 都是文件的路径,value 都是函数:
import()
的返回值是一个 promise,需要我们在 .then
里获取导入的结果然后编写后续逻辑,这主要是为了实现懒加载,比如我们在定义 route 的 component
属性时一般也是使用这种语法来实现的路由懒加载。但是此处我们显然希望立即获取到结果,于是我们给 import.meta.glob()
传入了第 2 个参数 { eager: true }
。
直接打印 modulesFiles
,结果如下:
现在对象里的 value 就都是 Module 了,我们可以通过 default
属性来获取各个文件导出的 route 对象了:
typescript
for (const key in modulesFiles) {
const module = modulesFiles[key].default
router.addRoute(module)
}