vite快速上手
前言
Vite是思维比较前卫而且先进的构建工具 他确实解决了一些webpack解决不了的问题,同时降低了一些心智负担, 已经有一些大厂在使用vite去构建项目
- vite基于自己得天独厚的优势,他未来一定会占有一席之地, 前段时间阿里的面试已经在问Vite了
- Vite是vue团队的官方出品,背靠这么大的生态,vue-cli会在下面两个版本中将vite作为预设构建工具
- 未来你使用vue-cli去构建vue项目的时候, 你要写的vue.config.js不再是webpack的配置, 而是vite的配置(目前只基于浏览器项目)
- Vite也支持直接构建react项目,也支持构建angular项目,svelte项目也支持构建
构建工具
企业级项目开发过程中, 需要做哪些工作?
- 浏览器他只认识html,css,js, 所以就会产生大量编译工作:
- typescript: 如果遇到ts文件, 我们需要使用tsc将typescript代码转换为js代码
- React/Vue: 安装react-compiler/vue-complier,将我们写的jsx文件或者.vue文件转换为render函数
- less/sass/postcss/component-style: 我们需要安装less-loader,sass-loader等一系列编译工具
- 语法降级: babel--->将es的新语法转换旧版浏览器可以接受的语法
- 体积优化: uglifyjs--->将我们的代码进行压缩变成体积更小性能更高的文件
- 以上这些动作, 只要文件稍微改一点点东西,就要从新跑一遍, 非常麻烦
- 需要有一个工具能够帮你把tsc,react-compiler,less,babel,uglifyjs全部集成到一起
- 这样, 我们只需要关心我们写的业务代码就好了
- 只要我们写的代码一变化 --->有人帮我们自动去把tsc,react-compiler,less,babel,uglifyjs全部挨个走一遍 --->得到可执行的js
- 这个工具就叫做**构建工具**
- 将我们写的浏览器不认识的代码 交给构建工具进行编译处理的过程就叫做打包,
- 打包完成以后会给我们一个浏览器可以认识的文件
- 一个构建工具他到底承担了哪些脏活累活:
- 模块化开发支持: 支持直接从node_modules里引入代码 + 多种模块化支持
- .处理代码兼容性: 比如babel语法降级,less,ts 语法转换(**不是构建工具做的,构建工具将这些语法对应的处理工具集成进来自动化处理**)
- 提高项目性能: 压缩文件,**代码分割**
- 优化开发体验:
-
- 构建工具会帮你自动监听文件的变化,当文件变化以后自动帮你调用对应的集成工具进行重新打包,然后再浏览器重新运行(整个过程叫做热更新,hot replacement)
- 开发服务器:跨域的问题,用react-cli create-react-element vue-cli 解决跨域的问题
构建工具是什么?
- 构建工具让我们不用关心代码如何在浏览器运行,
- 让我们专注于业务逻辑的实现
- 我们只需要给构建工具提供一个配置文件(如果不配置,他会有默认的配置),
- 他就可以按照我们的要实现代码编译, 代码压缩, 代码热更新等诸多功能
- 构建工具的本质是一个集成工具
常见的构建有哪些?
- webpack
- vite
- parcel
- esbuild
- rollup
- grunt
- gulp
vite相对于webpack的优势: 服务的启动和更新速度更快
官方文档: https://cn.vitejs.dev/guide/why.html#the-problems
了解webpack的工作流程
- 当我们执行 npm run dev 类似的命令启动前端服务后, 经过这样一个流程:
/**webpack支持多种模块化
*/
const lodash = require("lodash") // commonjs规范
import Vue from "vue" // es6 module规范/**webpack的转译过程1.经过AST抽象语法分析工具分析2.得到JS文件的所有导入和导出操作3.把所有的导入和导出语法统一为webpack语法4.前端不能操作文件, 但是webpack运行在服务端5.下面是一个简单复现代码, 了解就行
*/
(function(){function webpack_require() {}// 通过webpack的配置文件指定入口文件: index.jsmodules[entry](webpack_require)
},({"./src/index.js": (webpack_require) => {const loadsh = webpack_require("lodash")const Vue = webpack_require("vue")}
}))/**webpack的转译结果
*/
const loadsh = webpack_require("lodash")
const Vue = webpack_require("vue")
- 因为webpack支持多种模块化, 所以需要统一转译模块化代码后才能启动服务, 这种加载全部依赖后再运行项目的模式也叫全量加载
- 项目越大, webpack所处理的js代码越多, 启动服务器的时间就越长
- webpack能不能改呢? 不能, 因为这种机制性的改动, 后果是不可预估的
了解vite的工作流程
- 当我们执行 npm run dev 类似的命令启动前端服务后, 经过这样一个流程:
- 这种先启动服务, 再根据使用情况加载依赖的模式称为按需加载
- 按需加载相对全量加载, 冷启动和热更新的优势明星, 且项目越大优势越明显
- vite会不会直接把webpack干翻?
- vite是基于es modules支持规范,关注浏览器端的开发体验,
- webpack拥有更好的兼容性, node环境和浏览器环境都能跑
- 侧重点不一样,不存在谁替代谁
vite是什么
vite官网搭建vite项目文档教程
vite官网: https://vitejs,dev/guide/#scaffolding-your-frst-vite-project
当我们执行`yarn create vite`命令
- 帮我们全局安装一个东西: create-vite(vite的脚手架)
- 直接运行这个create-vite bin目录的下的一个可执行配置
create-vite 和 vite的关系是什么?
- create-vite内置了vite, create-vite是脚手架, vite是构建工具
- 脚手架是对构建工具的进一步简化, 提供了最佳实践的配置(预设), 可以做到开箱即用
- 我们之前接触过的vue-cli就内置了webpack
- 我们先学习vite, 再使用create-vite
vite基本使用
用vite启动项目
- 创建工程文件
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><script src="./main.js" type="module"></script>
</body></html>
export const count = 0
import { count } from './counter.js'console.log(count);
- 初始化工程
npm init -y
npm i lodash
import _ from 'lodash'
console.log(_, "lodash");export const count = 0
## 上面的报错意思: 就是找不到lodash模块, 检查资源引用的方式, 只识别 / ./ ../方式引入资源;
## 当我们使用非绝对路径或者相对路径引入资源, 浏览器不自动搜寻node_modules呢?
## 答案: 当我们打开HTML网页, 首先通过HTTP加载main.js, 然后再加载counter.js, 然后加载lodash, 然后 lodas还会依赖其他资源, 这些都要通过网络加载, 这些加载是非常占用网络资源的, 所以浏览器不愿意自动加载node_modules资源
## 解决: 使用vite构建工具
- 安装vite, 使用vite运行项目
npm i vite -D
{"name": "01tiyan","version": "1.0.0","description": "","main": "main.js","scripts": {"dev": "vite", // 新增"test": "echo \"Error: no test specified\" && exit 1"},"keywords": [],"author": "","license": "ISC","dependencies": {"lodash": "^4.17.21"},"devDependencies": {"vite": "^7.1.3"}
}
vite的打包构建
- 浏览器默认支持的EsModues规范, 只识别绝对路径或相对路径的方式引入资源, 为什么使用vite启动项目后, 就能识别 import _ from 'lodash' 这段代码了呢?
- 在开发环境中, vite在打包的过程中会进行依赖预构建, 效果如下
##依赖预构建做了什么事:
首先vite会找到对应的依赖,然后调用esbuild(对js语法进行处理的一个库),将其他规范的代码转换成esmodule规范,
然后放到当前目录的node modules/.vite/deps中, 同时对esmodule规范的各个模块进行统一集成##寻找依赖的过程
找寻依赖的过程是自当前目录依次向上查找的过程,直到搜寻到根目录或者搜寻到对应依赖为止##最终效果
``` 原始代码
import _ from "lodash"
`````` 打包后的代码
import _ from "/node_modules/.vite/deps/lodash.js"
```
- vite的依赖预构建非常重要, 解决了3个问题:
- 统一导出格式: 不同的第三方包会有不同的导出格式, 这个vite无法约束, 所以vite找到依赖之后, 会调用esbuild对js语法进行处理, 将其他规范的代码统一转换成esmodule规范, 然后放到当前目录下的 node_modules/.vite/deps中, 同时对esmodules规范的各个模块进行统一集成
- 路径补全: 由于统一导出格式时把所有的依赖都放在了 .vite/deps 目录中, 所以路径重写就非常方便了, 只要遇到非绝对路径和相对路径的引用, 就尝试路径补全
- 解决网络多包传输的性能问题: 有了依赖预构建以后无论有多少的额外export 和 import, vite都会尽可能的将他们进行集成, 最后只生成一个或者几个模块
/**
原始代码, index.js依赖a.js
*/
```a.js
export default function a() {}
``````index.js
export {defaault as a} from './a.js'// 上面的语法相当于先导入再导出
// import a from './a.js'
// export const a = a
```/**
vite重写之后, 被导入的代码直接替换到文件中(目的是尽可能的集成)
*/
```index.js
function a() {}
```
- 在生产环境中, vite会全权交给rollup这个库去完成打包的, 让代码兼容更多的环境
vite配置文件
import { defineConfig } from 'vite'export default defineConfig({optimizeDeps: {exclude: [] // 数组中的依赖项不进行依赖构建}
})
import { defineConfig } from 'vite'export default defineConfig({})
import { defineConfig } from 'vite'export default defineConfig({})
import { defineConfig } from "vite"
import viteBaseConfig from './vite.base.config'
import viteDevConfig from "./vite.dev.config"
import viteProdConfig from "./vite.prod.config"// 策略模式
const envResolver = {"build": () => {console.log("生产环境配置文件");return ({ ...viteBaseConfig, ...viteProdConfig })},"serve": () => {console.log("开发环境配置文件");return ({ ...viteBaseConfig, ...viteDevConfig })}
}export default defineConfig(({ command }) => {return envResolver[command]()
})
{"name": "01tiyan","version": "1.0.0","description": "","main": "main.js","scripts": {"dev": "vite","build": "vite build",},"keywords": [],"author": "","license": "ISC","dependencies": {"lodash": "^4.17.21"},"devDependencies": {"lodash-es": "^4.17.21","vite": "^7.1.3"}
}
vite的环境变量
基本概念
环境变量: 根据当前的代码环境而确定最终值的变量就叫做环境变量代码环境:
1,开发环境
2. 测试环境
3,预发布环境
4. 灰度环境
5,生产环境
如何配置
- 明确配置需求
接入百度地图sdk,小程序的sdk
APP KEY: 测试环境和生产还有开发环境是不一样的key
- 开发环境: 110
- 生产环境: 111
我们去请求第三方sdk接口的时候需要带上的一个身份信息对接后端API, 生产环境和开发环境是同一个地址吗? 肯定不是
- 开发环境: http://www.test.api/
- 生产环境: http://www.api/
- 新增环境变量文件
ENV_APP_KEY = 110
ENV_BASE_URL = http://www.test.api/
ENV_APP_KEY = 111
ENV_BASE_URL = http://www.api/
## 环境变量文件的分类
.env:所有环境都需要用到的环境变量
.env.development:开发环境需要用到的环境变量(默认情况下vite将我们的开发环境取名为development)
.env.production:生产环境需要用到的环境变量(默认情况下vite将我们的生产环境取名为production)
- 配置变量前缀
import { defineConfig } from 'vite'export default defineConfig({envPrefix: "ENV_" // 配置vite注入客户端环境变量校验的前缀, 默认是VITE_
})
- 使用环境变量
import { defineConfig, loadEnv } from "vite"
import viteBaseConfig from './vite.base.config'
import viteDevConfig from "./vite.dev.config"
import viteProdConfig from "./vite.prod.config"// 策略模式
const envResolver = {"build": () => {console.log("生产环境配置文件");return ({ ...viteBaseConfig, ...viteProdConfig })},"serve": () => {console.log("开发环境配置文件");return ({ ...viteBaseConfig, ...viteDevConfig })}
}export default defineConfig(({ command, mode }) => {// loadEnv()方法的作用: 手动确认env文件// 参数1: 通过mode传过来的字符擦混// 参数2: 当前env文件所在的目录, process.cwd()就是获取node的工作进程目录// 参数3: 环境变量的文件名, 默认是.env// 为什么? 环境变量文件是可以在配置中修改的, 如果要在这里提前拿到我们定义的环境变量, 就要这么做// 补充: process是Node服务给我提供的对象const env = loadEnv(mode, process.cwd(), "")// 查看生效的环境变量console.log("process=", env);return envResolver[command]()
})
// 读取环境变量
// 开发环境的key是110. 生产环境的key是111
const getUserPosition = () => {console.log("ENV_APP_KEY", import.meta.env.ENV_APP_KEY);return {APP_KEY: import.meta.env.ENV_APP_KEY}
}
getUserPosition()// 读取环境变量
// 开发环境是 http://www.test.api/
// 生产环境是 http://www.api/
const getBaseUrl = () => {console.log("ENV_BASE_URL", import.meta.env.ENV_BASE_URL);return {ENV_BASE_URL: import.meta.env.ENV_BASE_URL}
}
getBaseUrl()
import { count } from './counter.js'
import './request.js'console.log(count);
- 启动项目
## 启动项目:
npm run dev## 真正执行的命令:
npm run dev --mode development
详细的执行流程
- 执行 npm run dev命令, node就会去package.json文件的scripts中匹配脚本, 启动 vite 服务
## 补充更多细节:
npm run dev -启动-> vite -真正执行-> vite --mode development## 简单理解为: 自动补全
npm run dev --mode development -启动-> vite --mode development
- vite内置了dotenv这个第三方库, dotenv会自动读取.env文件, 解析这个文件中的环境变量, 并将其注入到process对象下 (不会直接注入,要考虑配置冲突问题)
import { defineConfig, loadEnv } from "vite"
import viteBaseConfig from './vite.base.config'
import viteDevConfig from "./vite.dev.config"
import viteProdConfig from "./vite.prod.config"// 策略模式
const envResolver = {"build": () => {console.log("生产环境配置文件");return ({ ...viteBaseConfig, ...viteProdConfig })},"serve": () => {console.log("开发环境配置文件");return ({ ...viteBaseConfig, ...viteDevConfig })}
}export default defineConfig(({ command, mode }) => {/**按照正常的执行流程, 在这里是拿不到环境变量的涉及到vite.config.js中的配置-- root-- envDir: 用来配置当前环境变量文件的地址既然环境变量的文件可以vite.config.js中的配置自定义这里连环境变量文件都没确定呢, 怎么拿环境变量中的数据?所以强调 "不会直接注入,要考虑配置冲突问题"*//**不过业务中, 我们确实有在这里使用环境变量的需求vite给我们提供了一些补偿机制: 我们可以调用vite的loadEnv来手动确认env文件因此可以提前拿到环境变量文件中的数据强调下 process.cwd()的作用, 就是得到工程目录, 如 D:\UserDatas\Note\21_Vite\01tiyan */const env = loadEnv(mode, process.cwd(), "")console.log(env); // 查看生效的环境变量/**当我们调用loadenv的时候,他会做如下几件事:1,直接找到.env文件不解释, 并解析其中的环境变量 并放进一个对象里2,会将传进来的mode这个变量的值进行拼接: 得到`.env.development`3. 根据我们提供的目录(D:\UserDatas\Note\21_Vite\01tiyan), 去取对应的配置文件并进行解析,并放进一个对象4.我们可以理解为```jsconst baseEnvConfig=读取.env的配置const modeEnvConfig=读取env相关配置const lastEnvConfig = {...baseEnvConfig, ...modeEnvConfig }```5.最终得到全部的环境变量文件的值6.如果是客户端,vite会将对应的环境变量注入到import.meta.env里去7.vite做了一个拦截, 他为了防止我们将隐私性的变量直接送进import.meta.env中,所以他做了一层拦截,8.如果你的环境变量不是以VITE开头的,他就不会帮你注入到客户端中去, 如果我们想要更改这个前缀,可以去使用envPrefix配置*/return envResolver[command]()
})
## 补充一个小知识:
为什么vite.cong.js可以书写成esmdule的形式,这是因为vite他在读取这个vite.cong.js的时候,会率先node去解析文件语法,如果发现你是esmadule规范, 会直接将你的esmadule规范进行替换变成commonjs规范
再添加一个测试环境
- 新增环境变量文件
ENV_APP_KEY = 120
ENV_BASE_URL = http://测试
- 在脚本中配置mode
{"scripts": {"dev": "vite","build": "vite build","test": "vite --mode test" // 新增}
}
- 启动项目