vite简介
vite预构建
-
将非
ESM
规范的代码转换为符合ESM
规范的代码,另外就是将第三方依赖内部的多个文件合并为一个,减少http
请求数量 -
简单来说,
vite
在一开始将应用中的模块区分为依赖
和源码
两类 -
「依赖部分」更多指的是代码中使用到的第三方模块,比如
vue、lodash、react
等,vite
将会使用esbuild
在应用启动时对于依赖部分进行预构建依赖 -
「源码部分」 比如说平常我们书写的一个一个
js、jsx、vue
等文件,这部分代码会在运行时被编译,并不会进行任何打包。vite
以 原生ESM
方式提供源码 -
在开发环境中
vite
是让浏览器接管了打包程序的部分工作,vite
只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理 -
在生产环境中,
Vite
利用Rollup
对代码进行打包处理,并配合着tree-shaking
/懒加载和chunk
分割的方式为浏览器提供最后的代码资源
esbuild
-
Vite
是使用Esbuild
来做预构建和内容转换的,同样规模的项目,使用Esbuild
可以将打包速度提升10-100
倍 -
Esbuild
是一款基于Go
语言开发的javascript
打包工具,最大的一个特征就是快 -
Esbuild
支持多种模块格式,包括CommonJS
、ES6
模块、AMD
等,使得它适用于任何类型的JavaScript
项目
缓存
-
在Vite中,缓存是一个重要的概念,因为它可以显著加快开发服务器的热重载速度
-
Vite使用了两级缓存:一级是内存中的模块缓存,二级是基于文件系统的缓存
-
内存中的模块缓存:Vite会将每个模块的转换结果保存在内存中,以便在开发过程中快速访问
-
基于文件系统的缓存:Vite会将经过转换的文件存储在
node_modules/.vite/
文件夹中,并且会在文件系统中缓存这些文件的依赖和变化 -
package.json
中的dependencies列表;包管理器的package-lock.json
文件;vite.config.js
相关字段中的配置;当以上三个地方发生变化时,vite均会进行重新预购构建 -
更改缓存文件的位置
// vite.config.js
export default defineConfig({
cacheDir: './.cache'
})
模块热重载HMR
- HMR 的全称叫做
Hot Module Replacement
,即模块热替换
或者模块热更新
- HMR 的作用就是在页面模块更新的时候,直接把页面中发生变化的模块替换为新的模块,同时不会影响其它模块的正常运作
- Vite本身也实现了一套 HMR 系统,他是基于原生的 ESM 模块规范来实现,在文件发生改变时 Vite 会侦测到相应 ES 模块的变化,从而触发相应的 API,实现局部的更新
- Vite 通过
import.meta.hot
对象暴露手动 HMR API
vite基础配置
配置文件
- Vite 会自动解析项目根目录下名为
vite.config.js
的配置文件(也支持其他 JS 和 TS 扩展名) - 最基础的配置,
vite.config.js
export default {
// 配置选项
}
- 显式地通过
--config
命令行选项指定一个配置文件
vite --config my-config.js
- 可以使用
defineConfig
工具函数配合开发工具进行智能提示
import { defineConfig } from 'vite'
export default defineConfig({
})
vite-plugin-html
- vite内置了非常多的插件,开发者无需关注过多的插件配置
- vite-plugin-html 是一个基于ejs模板库动态控制整个html文件内容的一个插件
- 插件安装
npm install vite-plugin-html -D
- vite.config.js添加配置
import { CreateHtmlPlugin } from 'vite-plugin-html'
plugins: [
createHtmlPlugin({
minify: true, // 是否压缩代码
pages: [
{ // 进行多页面打包配置
filename: "index.html", // 生成的文件名称
template: "./index.html", // 模板文件
entry: "./src/main.js", // 打包入口文件
injectOptions: { // 注入页面信息
data: {
title: "vite项目练习",
}
}
}
]
}),
]
- 在html中使用
<title><%= title %></title>
别名配置
- vite对静态资源是开箱即用
- 路径别名配置
resolve: {
alias: {
'@': path.resolve(__dirname, "./src"),
'@assets': path.resolve(__dirname, "./src/assets")
}
},
vite打包配置
build: {
rollupOptions: {
external: [], // 哪些依赖无需打包,例如使用cdn单独引入的包不需要参与打包
output: { // 针对图片、样式文件、js文件进行分类规整
assetFileNames(assetInfo) {
if (assetInfo.name.endsWith('.css')) {
return 'css/[name]-[hash].css'
}
const imgExts = ['.png', '.jpg', '.jpeg', '.webp', '.gif', '.svg', '.ico']
if (imgExts.some(ext => assetInfo.name.endsWith(ext))) {
return 'imgs/[name]-[hash].[ext]'
}
return 'assets/[name]-[hash].[ext]'
}
},
},
assetsInlineLimit: 4096, // 小于4kb会转成base64字符串放在打包文件里
outDir: "dist", // 打包后的文件夹名称
assetsDir: "js" // 静态资源(js)文件夹名称
}
vite处理css
- vite天生就支持对css文件的直接处理
vite在读取到main.js中引用到了index.css
直接使用fs模块去读取index.css中的文件内容
直接创建一个style标签,将index.css中文件内容直接copy进style标签里
将style标签插入到index.html的head中
将css文件直接替换为js脚本(方便热更新或者css模块化),同时设置content-type,从而让浏览器以js脚本的形式来执行css后缀的文件
- vite中的css模块化(less、sass同理)
xx.module.css(module的一种约定,表示需要开启css模块化)
它会将你的所有类名进行一定规则的替换,同时创建一个映射对象
将替换过后的内容塞进style标签里,然后放入到head标签中
- index.module.css
.main-box {
width: 200px;
height: 200px;
border: 1px solid orange;
}
- main.js
import componentALess from "./css/index.module.css";
const div = document.createElement('div')
div.className = componentALess.mainBox
document.body.appendChild(div)
vite配置css
- 在vite.config.js中我们通过css属性去控制整个vite对css的处理行为
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
css: {
modules: {
/**
* camelCase {"main-box":"_main-box_1eq4v_1","mainBox":"_main-box_1eq4v_1"}
* camelCaseOnly {"mainBox":"_main-box_1eq4v_1"}
* dashes {"main-box":"_main-box_1eq4v_1","mainBox":"_main-box_1eq4v_1"}
* dashesOnly {"mainBox":"_main-box_1eq4v_1"}
*/
localsConvention: "camelCase",
// local | global 配置为global时,无法通过import的方式进行引用
scopeBehaviour: "local",
hashPrefix: "", // 配置hash生成规则,主要和 name 联用。
globalModulePaths: [] // 配置不参与模块化的文件路径
},
devSourceMap: true, // 开启css的sourceMap(文件索引)
}
})
- less、sass等预处理器
# 使用sass
npm install sass -D
# 使用less
npm install less -D
- less、sass等预处理器配置项
import { defineConfig } from 'vite'
export default defineConfig({
css: {
preprocessorOptions: {
// 整个的配置对象都会最终给到less的执行参数(全局参数)中去
less: { // less相关配置
math: 'always',
globalVars: { // 全局变量
mainColor: 'red',
}
},
sass: {} // sass相关配置
},
}
})
- postcss
浏览器的兼容性,预处理器并不能够解决这些问题
对未来css属性的一些使用降级问题(如css变量, --global-color)
前缀补全(如–webkit)
postcss将语法进行编译(嵌套语法、函数、变量)成原生css
目前less和sass等一系列预处理器的postcss插件已经停止维护了,需要自己使用less或sass编译器编译,然后将编译结果发给postcss,因此说postcss是后处理器
- postcss-依赖安装
npm i postcss postcss-preset-env -D
- postcss-使用方式一(推荐),项目根目录新建
postcss.config.js
文件,配置如下
module.exports = {
plugins: [
require('postcss-preset-env')({
browsers: [ // 添加前缀的浏览器列表
"> 1%",
"Chrome > 31",
"Firefox > 31",
"ie >= 10",
"last 10 versions"
],
autoprefixer: { grid: true }, // grid布局是否添加前缀
}),
],
};
- postcss-使用方式二(了解即可),在
vite.config.js
中添加配置
import postcssPresetEnv from 'postcss-preset-env'
css: {
postcss: {
plugins: [
postcssPresetEnv({
browsers: [ // 添加前缀的浏览器列表
"> 1%",
"Chrome > 31",
"Firefox > 31",
"ie >= 10",
"last 10 versions"
],
autoprefixer: { grid: true }, // grid布局是否添加前缀
}),
],
},
}
vite环境变量
- 在项目根目录下新建
.env.dev
,.env.test
,.env.prod
文件,分别写入不同的测试变量
/*
.env.dev
VITE_TEST = 'dev'
.env.test
VITE_TEST = 'test'
.env.prod
VITE_TEST = 'prod'
*/
- 修改命令行脚本
"scripts": {
"dev": "vite --mode dev",
"build:test": "vite build --mode test",
"build:prod": "vite build --mode prod"
}
- 业务代码中使用
console.log(import.meta.env.VITE_TEST)
- 需要注意的是:文件名称中务必包含对应的mode,这样Vite会自动将配置文件的变量注入到业务代码中
- 环境变量的其它配置
import { defineConfig } from 'vite'
export default defineConfig({
envPrefix: "VITE_", // 以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中,默认是VITE_
envDir: '/', // 用于加载 .env 文件的目录。可以是一个绝对路径,也可以是相对于项目根的路径
define: { // 定义全局常量替换方式。其中每项在开发环境下会被定义在全局,而在构建时被静态替换
"process.env": process.env // 解决部分依赖包process错误问题
},
})
开发服务器
- 开发服务器常用配置
server: {
host: '0.0.0.0', // 允许其他人访问
port: 3000, // 自定义端口,默认为5173
open: false, // 服务启动后,自动在浏览器中打开,默认是不打开的
hmr: true, // 为开发服务启用热更新,默认是不启用热更新的
proxy: {
'/foo': 'http://localhost:4567',
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
}
},
vite插件
@vitejs/plugin-vue2
-
@vitejs/plugin-vue2是一个专为Vue2.x设计的Vite插件,它让你能够在Vite构建工具中无缝地使用和编译Vue2组件
-
特别要注意的是,@vitejs/plugin-vue2这个插件仅支持Vue版本大于等于2.7.0
-
vue2项目迁移vite打包,依赖包安装
npm install @vitejs/plugin-vue2 -D
- vite.config.js增加插件配置
import vue from '@vitejs/plugin-vue2'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
vue()
],
})
@vitejs/plugin-vue
- @vitejs/plugin-vue 是一个 Vite 插件,提供了对 Vue3 单文件组件支持。
- 它还提供了一些其他功能,例如自动导入 Vue 组件和样式等
- 依赖包安装
npm install @vitejs/plugin-vue -D
- 使用方法同@vitejs/plugin-vue2
@vitejs/plugin-legacy
[!NOTE]
默认情况vite的兼容目标是支持原生ES模块,原生ESM动态导入和
import.meta
的浏览器,官方预设的兼容目标如下:
- es2020
- edge 88
- firefox 78
- chrome 87
- safari 14
所以vite在打包后不会做任何兼容处理,这就导致了一些浏览器版本低一点的设备,打开网站就会出现白屏的情况
-
@vitejs/plugin-legacy是一个兼容低版本浏览器的官方插件
-
他会自动生成传统浏览器的 chunk及与其相对应 ES 语言特性方面的 polyfill,兼容版的 chunk 只会在不支持原生 ESM 的浏览器中进行按需加载
-
插件安装
npm install @vitejs/plugin-legacy -D
- vite.config.js增加配置
import legacyPlugin from '@vitejs/plugin-legacy'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
legacyPlugin({
targets: ['defaults', 'Chrome >= 51', 'Edge >= 15', 'Safari >= 10'],
renderLegacyChunks: true, // 开启传统浏览器polyfill
polyfills: true // 针对传统浏览器的polyfill,采用默认的规则
}),
],
build: {
target: 'es2015',
minify: 'esbuild', // 默认值是esbuild,当设置为 'terser' 时或者老版本必须先安装 npm add -D terser
// terserOptions: {
// compress: {
// // 生产环境时移除console
// drop_console: true,
// drop_debugger: true,
// }
// },
},
})
- 官方定义,在
@vitejs/plugin-legacy
没有明确指定插件配置参数targets
时,他会去读取项目中的.browserslistrc
文件,所以也可以在文件中进行定义
Chrome >= 51
Edge >= 15
Safari >= 10
Firefox >= 54
Opera >= 38
iOS >= 10
Android >= 5
not IE <= 11
vite-plugin-mock
- mockjs: 模拟海量数据的,vite-plugin-mock的依赖项就是mockjs
- 插件安装
npm install vite-plugin-mock mockjs -D
- vite.config.js中增加配置
import { ViteMockServe } from 'vite-plugin-mock'
plugins: [
ViteMockServe({
mockPath: 'mock'
})
]
- 项目根目录创建mock文件夹,与上边mockPath保持一致,在mock文件夹下创建index.js文件
import mockJS from "mockjs"
module.exports = [
{
method: "post",
url: "/api/users",
response: ({body}) => {
return {
code: 200,
msg: "success",
data: mockJS.mock({
"data|100": [{
name: "@cname",
ename: mockJS.Random.name(),
"id|+1": 1,
}]
})
}
}
}
]
配置文件汇总
文件目录
│ .browserslistrc
│ .env.dev
│ .env.prod
│ .env.test
│ index.html
│ package-lock.json
│ package.json
│ postcss.config.js
│ vite.config.js
│
└─src
│ app.vue
│ main.js
│
├─assets
│ logo.png
│
└─css
index.module.css
vite.config.js
import { defineConfig } from 'vite'
import { createHtmlPlugin } from 'vite-plugin-html'
import vue from '@vitejs/plugin-vue2'
import legacyPlugin from '@vitejs/plugin-legacy'
import path from 'path'
export default defineConfig({
base: './', // 公共基础路径,可以是绝对路径、相对路径或域名
plugins: [
legacyPlugin({
renderLegacyChunks: true, // 开启传统浏览器polyfill
polyfills: true // 针对传统浏览器的polyfill,采用默认的规则
}),
createHtmlPlugin({
minify: true, // 是否压缩代码
pages: [
{ // 进行多页面打包配置
filename: "index.html", // 生成的文件名称
template: "./index.html", // 模板文件
entry: "./src/main.js", // 打包入口文件
injectOptions: { // 注入页面信息
data: {
title: "vite项目练习",
}
}
}
]
}),
vue() // 支持vue开发插件
],
resolve: {
alias: {
'@': path.resolve(__dirname, "./src"),
'@assets': path.resolve(__dirname, "./src/assets")
}
},
optimizeDeps: {
exclude: [] // 将指定数组中的依赖不进行依赖预构建
},
css: {
modules: {
/**
* camelCase {"main-box":"_main-box_1eq4v_1","mainBox":"_main-box_1eq4v_1"}
* camelCaseOnly {"mainBox":"_main-box_1eq4v_1"}
* dashes {"main-box":"_main-box_1eq4v_1","mainBox":"_main-box_1eq4v_1"}
* dashesOnly {"mainBox":"_main-box_1eq4v_1"}
*/
localsConvention: "camelCase",
// local | global 配置为global时,无法通过import的方式进行引用
scopeBehaviour: "local",
hashPrefix: "", // 配置hash生成规则,主要和 name 联用。
globalModulePaths: [] // 配置不参与模块化的文件路径
},
preprocessorOptions: {
// 整个的配置对象都会最终给到less的执行参数(全局参数)中去
less: { // less相关配置
math: 'always',
globalVars: { // 全局变量
mainColor: 'red',
}
},
sass: {} // sass相关配置
},
devSourceMap: true, // 开启css的sourceMap(文件索引)
},
envPrefix: "VITE_", // 以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中,默认是VITE_
envDir: '/', // 用于加载 .env 文件的目录。可以是一个绝对路径,也可以是相对于项目根的路径
define: { // 定义全局常量替换方式。其中每项在开发环境下会被定义在全局,而在构建时被静态替换
"process.env": process.env // 解决部分依赖包process错误问题
},
build: {
target: 'es2015', // 设置最终构建的浏览器兼容目标
minify: 'esbuild', // 'terser' | 'esbuild',客户端构建默认为'esbuild'
rollupOptions: {
external: [], // 哪些依赖无需打包,例如使用cdn单独引入的包不需要参与打包
output: { // 针对图片、样式文件、js文件进行分类规整
assetFileNames(assetInfo) {
if (assetInfo.name.endsWith('.css')) {
return 'css/[name]-[hash].css'
}
const imgExts = ['.png', '.jpg', '.jpeg', '.webp', '.gif', '.svg', '.ico']
if (imgExts.some(ext => assetInfo.name.endsWith(ext))) {
return 'imgs/[name]-[hash].[ext]'
}
return 'assets/[name]-[hash].[ext]'
}
},
},
assetsInlineLimit: 4096, // 小于4kb会转成base64字符串放在打包文件里
outDir: "dist", // 打包后的文件夹名称
assetsDir: "js" // 静态资源(js)文件夹名称
},
})