Vite
是一种新型前端构建工具,能够显著提升前端开发体验。它主要由两部分组成:
- 一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能,如速度快到惊人的 模块热更新(HMR)。
- 一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。
构建工具
什么是构建工具
构建工具是一个把源代码生成可执行应用程序的过程自动化的程序,构建包括编译、连接跟把代码打包成可用的或可执行的形式
构建工具能做什么
- 代码转化(ts ==> js, Vue/React代码 ==》 js)
- 代码降级(babel实现es6 ==> es5转换)
- Less/Sass等语言转换
- 代码压缩
- 模块化语法编译
- 代码分割
- 热更新
- 开发服务器
Vite相比webpack的优势
当我们开始构建越来越大型的应用时,需要处理的 JavaScript 代码量也呈指数级增长。包含数千个模块的大型项目相当普遍。我们开始遇到性能瓶颈 —— 使用 JavaScript 开发的工具通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用 HMR,文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感。
Vite 旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 ES 模块,且越来越多 JavaScript 工具使用编译型语言编写。
- 服务启动
webpack
因为需要兼容不同的模块化规范,需要从打包入口文件开始分析,然后将文件中的不同模块化规范转换成自己的语法规范,并收集各个模块之间的依赖,当模块越来越多时,处理的时间就会越久
需要将所有的模块分析完成并生成对应的js文件之后,才去启动server,如下图所示:
vite
Vite 以 原生 ESM 方式提供源码。这实际上是让浏览器接管了打包程序的部分工作:Vite 只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。
- 模块热更新
webpack
webpack在监听文件变化之后,都需要经过重新编译和转换之后,再触发热更新逻辑
vite
vite在监听文件变化之后,则触发热更新逻辑,浏览器重新请求此文件
vite脚手架和vite
当我们执行
yarn create vite时,实际是调用的create-vite这个脚手架,而create-vite中内置了vite,可以类比vue-cli和webpack的关系
- vite脚手架提供了一些预设(比如vue,vue-ts, react, react-ts等模板)
- vite 是构建工具
vite使用
安装vite
npm init -y // 生成package.json文件
yarn add vite -D
// or
npm i vite -D
创建文件
- 文件目录
//
|- index.html
|- main.js
|- count.js
|- package.json
- index.html
在一个 Vite 项目中,
index.html在项目最外层而不是在public文件夹内。这是有意而为之的:在开发期间 Vite 是一个服务器,而index.html是该 Vite 项目的入口文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
// type = "module" 告诉浏览器是使用的es6 module
<script src="../main.js" type="module"></script>
</body>
</html>
- main.js
import {num} from './count.js'
console.log(num)
- count.js
export const num = 0
- package.json 配置脚本
{
"name": "vite",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"dev": "vite"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"vite": "^3.0.9"
}
}
处理第三方模块
比如在项目中引入lodash
- 安装依赖
npm i lodash -S
- 在count.js中引用
import _ from 'lodash'
console.log(_)
export const num = 0
- network中请求结果
vite对引入的第三方做了转换,转换后的代码存放在node_modules/.vite/deps下
为什么要做转换:
项目中使用的第三方库不一定是使用ESM标准,而vite只支持ESM标准
两个目的:
- 将不同的第三方库的代码进行转换,并存放在node_modules/.vite/deps,方便使用相对路径使用
- 提升性能,Vite 将有许多内部模块的 ESM 依赖关系转换为单个模块,以提高后续页面加载性能(减少http请求次数)
针对提升性能可以看下面的列子:
- 引入lodash-es模块
npm i lodash-es -S
- main.js使用
import lodashEs from 'lodash-es'
console.log(lodashEs)
- 默认情况下 vite会将第三方模块中的依赖转换为对应的方法块,如下图所示
- 修改配置查看做处理的结果
// vite.config.js
import { defineConfig } from "vite";
export default defineConfig({
optimizeDeps: { // 告知vite这个模块不进行依赖预构建
exclude: ['lodash-es']
},
});
再次刷新页面结果如下(依赖的模块会被一次请求,很大的影响了性能):
vite中使用环境变量
Vite 使用 dotenv 从你的 环境目录 中的下列文件加载额外的环境变量:
加载的环境变量也会通过
import.meta.env 暴露给客户端源码。
为了防止意外地将一些环境变量泄漏到客户端,只有以 VITE_ 为前缀的变量才会暴露给经过 vite 处理的代码
- 创建.env文件
// .env
VITE_BASE_URL = 'http://test/api'
- 使用
// request.js
const BASE_URL = import.meta.env.VITE_BASE_URL
const service = {
baseUrl: BASE_URL
}
针对css资源的支持
vite中默认是能解析css的, 如果使用了预编译语言如less, sass等, 需要安装对应语言的编译器
- 安装依赖(以sass为例)
npm i sass -D
- 样式编写与引入
// index.scss
$bgColor: blue;
.box1 {
width: 400px;
height: 200px;
background: $bgColor;
}
// main.js
import './index.scss'
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>hello Vite</h1>
<div class="box1"></div>
<script src="main.js" type="module"></script>
</body>
</html>
问题
- 样式冲突和覆盖问题
vite 在处理css资源时,将css文件的内容放置style标签内,并拼接到头部head中,如果存在同名的样式,则后面的会影响到前面的样式,如下图所示
- 通过cssModule来解决样式冲突问题
规定以
*.module.css命名的css文件就表示开启了css 模块化
// index.module.scss
.bg {
width: 300px;
height: 300px;
background: url('../assets/bg.png');
background-size: cover;
}
经过vite处理之后,会将原有的类名转换带hash值的唯一值, 如下所示
遗留问题
目前cssModule虽然实现了类名唯一值, 但是页面中的类名还是未转换前的,需要通过import cssModule from 'index.module.css'; 然后在vue页面中使用
设置样式,感觉有点繁琐,暂时没找到合适的转换方式
图片,字体资源
vite在处理图片资源时,默认将资源引入为URL
服务时引入一个静态资源会返回解析后的公共路径(js文件或者css中的背景图片):
// main.js
import imgUrl from './img.png'
document.getElementById('hero-img').src = imgUrl
// index.css
.bg {
width: 500px;
height: 300px;
background: url('../assets/bg.png');
background-size: cover;
}
// iconfont.css
@font-face {
font-family: "iconfont"; /* Project id 3590825 */
src: url('../fonts/iconfont.woff2?t=1660614433445') format('woff2'),
url('../fonts/iconfont.woff?t=1660614433445') format('woff'),
url('../fonts/iconfont.ttf?t=1660614433445') format('truetype');
}
-
较小的资源体积小于
assetsInlineLimit选项值(默认4kb) 则会被内联为 base64 data URL -
未被包含在内部列表或
assetsInclude中的资源,可以使用?url后缀显式导入为一个 URL,如下所示
import workletURL from 'extra-scalloped-border/worklet.js?url'
CSS.paintWorklet.addModule(workletURL)
vite常用配置
server配置
import { defineConfig } from "vite";
export default defineConfig({
server: {
port: '9530', // 设置服务器端口
open: true, // 是否启动时, 自动打开浏览器
cors: true, // 是否开启跨域
proxy: {
'/rms': {
target: 'http://rms.com/api',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
},
})
alias别名
import { defineConfig } from "vite";
import path from 'path'
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@comp': path.resolve(__dirname, './src/components')
}
}
})
构建相关
import { defineConfig } from "vite";
export default defineConfig({
build: {
terserOptions: { // js处理配置
compress: { // 压缩配置
drop_console: true, // 删除log语句
}
}
}
})