vite官方文档:Vite | 下一代的前端工具链 (vitejs.cn)
Vite是Vue团队的官方出品,背靠这么大的生态,Vue-cli会在下面两个版本中将vite作为预设构建工具
未来你使用vue-cli去构建vue项目的时候你要写的vue.config.js不再是webpack的配置而是vite的配置(目前只基于浏览器项目)
Vite也支持直接构建react项目,也支持构建angular项目,svelte项目也支持构建
目录
构建工具是什么
企业级项目里都可能会具备哪些功能?
- 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 --->将我们的代码进行压缩变成体积更小性能更高的文件
将App.tsx ---> tsc ---> App.jsx ---> React-complier--->js文件
构建工具能够帮你把tsc,react-compiler,less,babel,uglifyjs全部集成到一起,我们只需要关心我们写的代码就好了。使我们写的代码一变化---〉有人帮我们自动去tsc,react-compiler,less, babel,uglifyjs全部挨个走一遍---返回一个-->js文件打包:将我们写的浏览器不认识的代码交给构建工具进行编译处理的过程就叫做打包,打包完成以后会给我们一个浏览器可以认识的文件
一个构建工具承担了哪些功能
1. 模块化开发支持:支持直接从node_modules里引入代码+多种模块化支持
<script type="module">
// 因为浏览器并不知道node_module
import _ from 'lodash'//只能使用'./','/','../'的方式引入
const lodash=require('lodash');//浏览器不认识
console.log("_",_)
</script>
2. 处理代码兼容性:比如babel语法降级,less , ts 语法转换(不是构建工具做的,构建工具将这些语法对应的处理工具集成进来自动化处理)
3.提高项目性能:压缩文件,代码分割
4.优化开发体验:
- 构建工具会帮你自动监听文件的变化,当文件变化以后自动帮你调用对应的集成工具进行重新打包,然后再浏览器重新运行(整个过程叫做热更新,hot replacement)
- 开发服务器:跨域的问题,用react-cli create-react-element-vue-cli·解决跨域的问题,
每次解析文件的顺序不能出错
构建工具他让我们可以不用每次都关心我们的代码在浏览器如何运行,我们只需要首次给构建工具提供一个配置文件(这个配置文件也不是必须的,如果你不给他他会有默认的帮你去处理),有了这个集成的配置文件以后,我们就可以在下次需要更新的时候调用一次对应的命令就好了,如果我们再结合热更新,我们就更加不需要管任何东西,这就是构建工具去做的东西。它让我们不用关心产生的代码也不用关心代码如何在浏览器运行,只需要关心我们的开发怎么写的爽就怎么写好了。
市面上主流的构建工具:
- webpack
- vite
- parcel
- esbuild
- rollup
- grunt
- gulp
vite相较于webpack的优势
当我们开始构建越来越大型的应用时,需要处理的 JavaScript 代码量也呈指数级增长。包含数千个模块的大型项目相当普遍。我们开始遇到性能瓶颈―使用JavaScript 开发的工具通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用HMR(热更新),文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感。
我们的项目越大----〉构建工具( webpack )所要处理的js代码就越多【跟webpack的一个构建过程(工作流程)有关系】
造成的结果:构建工具需要很长时间才能启动开发服务器启动开发服务器---〉把项目跑起来)
webpack能不能改?如果一旦要改那么将会动到webpack的大动脉
webpack支持多种模块化,他一开始必须要统一模块化代码,所以意味着他需要将所有的依赖全部读一遍
index.js
// 这段代码最终会到浏览器中去运行
const lodash=requre('lodash');//commonjs规范
import Vue from 'vue'//es6 module
// webpack是允许我们这样写的
//webpack的编译原理,AST 抽象语法分析的工具分析出你写的这个js文件有哪些导入和导出操作构建工具是运行在服务端的
// webpack的一个转换结果
const lodash=webpack_require('lodash')
const Vue = webpack_require('vue')
//webpack最终生成文件
(function(modules){
function webpack_require(){}
// 入口是index.js
// 通过webpack的配置文件得来的:webpack.config.js ./src/index.js
module[entry]
},({
'./src/index.js':(webpack_require)=>{
const lodash=webpack_require('lodash')
const Vue = webpack_require('vue')
}
}))
Q:vite会直接把webpack取代吗?不会
webpack支持多种模块化,工程可能不只是跑在浏览器端
vite不允许commonjs规范的代码,只用esmodule的规范,不需要将所有的依赖读一遍
两者侧重不一样,webpack更关注兼容性,ite关注浏览器端的开发体验
传统webpack打包流程:把所有依赖全部打包再去开启开发服务器
Vite 直接开启开发服务器,加载入口文件,只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。
vite脚手架与vite的区别
敲了```yarn create vite```发生了什么?
- 帮我们全局安装一个东西:create-vite (vite的脚手架)
- 直接运行这个create-vite bin目录的下的一个执行配置
我们之前接触过vue-cli
可能会存在误区:认为官网中使用对应yarn create构建项目的过程也是vite在做的事情
create-vite和vite的关系是什么呢?—-- create-vite内置了vite(类似于使用vue-cli 会内置webpack)
es moudule的规范
在默认情况下,我们的esmodule去导入资源的时候,要么是绝对路径,要么是相对路径(不认识node_modules目录)
既然我们现在的最佳实践就是node_modules,那为什么es官方在我们导入非绝对路径和非相对路径的资源的时候不默认帮我们搜寻node_modules呢?——太消耗性能了,如果直接寻找的话被引入的依赖可能也引入了其他依赖)
vite的依赖构建
在处理的过程中如果说看到了有非绝对路径或者相对路径的引用,他则会尝试开启路径补全
import _ from "/node_modules/.vite/lodash"; // lodash可能也import了其他的东西
==>
import __vite__cjsImport0_lodash from "/node_modules/.vite/deps/lodash.js?v=eb
找寻依赖的过程是自当前目录依次向上查找的过程,直到搜寻到根目录或者搜寻到对应依赖为止/user/node_modules/lodash浏览器找不到了(解决办法,依赖预构建)
yarn dev -->开发(每次依赖预构建所重新构建的相对路径都是正确的)
vite会全权交给一个叫做rollup的库去完成生产环境的打包
依赖预构建
有的包他是以commonjs规范的格式导出axios
首先vite会找到对应的依赖,然后调用esbuild(对js语法进行处理的一个库),将其他规范的代码转换成esmodule规范然后放到当前目录下的node_modules/ .vite/deps,同时对esmodule规范的各个模块进行统一集成
解决了三个问题:
- 不同的第三方包会有不同的导出格式(这个是vite没法约束人家的事情)
- 对路径的处理上可以直接使用.vite/deps,方便路径重写
- 网络多包传输的性能问题(也是原生esmodule规范不敢支持node_modules的原因之一),有了依赖预构建以后无论他有多少的额外export和import,vite都会尽可能的将他们进行集成最后只生成一个或者几个模块
假设lodash又依赖了其他的模块,并且这些模块都是用export导出,此时lodash也需要请求其他多的依赖)
```js
//a
export default function a() {}
```
```js
export {default as a) from './a.js
//相当于
//import a from './a/js'
//export const a=a
```
```js vite重写以后,浏览器不会再去加载a依赖了
function a(){}
```
vite.config-js a= webpack.config.js
//vite.config.js 文件
export default {
optimizeDeps:{
exclude:["lodash-es"],//当遇到lodash-es这个依赖的时候不进行依赖预构建,网页引入大量依赖
}
}
vite配置文件的处理细节
- vite配置文件的语法提示
//vite.config.js import { defineConfig } from 'vite';//语法提示 export default defineConfig ({ optimizeDeps: { exlude:[],//将指定数组中的依赖不进行依赖预构建 }, }) ``` ``` //语法提示的另一种写法 /** @type import ( 'vite' ).UserConfig */ const viteConfig={ optimizeDeps: { exlude:[],//将指定数组中的依赖不进行依赖预构建 }, } export default viteConfig; //举个例子 /** *@param *@return {string} */ function bar(){} //bar.出现的提升全是字符串函数
- 如果你使用的是webstorm,那你可以得到很好的语法补全
- 如果你使用是vscode或者其他的编辑器,则需要做一些特殊处理
- 关于环境的处理
过去我们使用webpack的时候,我们要区分配置文件的一个环境
- webpack.dev.config
- webpack.prod.config
- webpack.base.config
- webpackmecae
//vite.config.js
import { defineConfig } from 'vite';//语法提示
import viteBaseConfig from './vite.base.config'
import viteProdConfig from './vite.prod.config'
import viteDevConfig from './vite.dev.config'
//策略模式
const envResolver={
'build':()=>{
console.log('生产环境……')
return Object.assign({},viteBaseConfig,viteProdConfig)
},
'serve':()=>({...viteBaseConfig,...viteDevConfig}),
//'serve':()=>Object.assign({},viteBaseConfig,viteDevConfig),
}
export default defineConfig (({command:'build'|'serve'})=>{
//是build还是serve主要取决于我们敲的命令是开启开发环境还是生产环境
console.log('command',command)
return envResolver[command]();
/*
if(command==='build')
{
//代表生产环境配置
}else{
//代表开发环境配置
}
*/
})
//vite.base.config.js
import {defineConfig} from 'vite'
export default defineConfig({
optimizeDeps: {
exlude:[],//将指定数组中的依赖不进行依赖预构建
},
})
//vite.dev.config.js
import {defineConfig} from 'vite'
export default defineConfig({})
//vite.prod.config.js
import {defineConfig} from 'vite'
export default defineConfig({})
vite环境变量配置
会根据当前的代码环境产生值的变化的变量就叫做环境变量
代码环境:1.开发环境
2.测试环境
3.预发布环境
4.灰度环境
5.生产环境
```
举个例子:百度地图sdk,小程序的sdk
APP_KEY:测试环境和生产还有开发环境是不一样的key
- 开发环境:110
- 生产环境:111
- 测试环境:112
我们去请求第三方sdk接口的时候需要带上的一个身份信息
```
//request.js
const getUserPosition=()=>{
axios.post({
params:{
APP_KEY:110
}
})
}
```
我们在和后端对接的时候,前端在开发环境中请求的后端API地址和生产环境的后端API地址是一个吗
肯定不是同一个
- 开发和测试:http://test.api/
- 生产: https://api/
```
//axios.baseUrl='http:tes.api/'
//生产:https://api/
const getCurrentUserInfo=()=>{
axios.post({
url:'/user/info'
})
}
//.env.development
APP_KEY=110
BASE_URL='http://test.api/'
//.env.production
APP_KEY=112
BASE_URL='https://api/'
希望每次运行项目时APP_KEY与BASE_URL自动修改
在vite中的环境变量处理:dotenv这个第三方库(vite内置),敲下命令行启动项目时会寻找当前目录下的.env文件,dotenv会自动读取.env文件,并解析这个文件中的对应环境变量,并将其注入到process对象下(但是vite考虑到和其他配置的一些冲突问题,他不会直接注入到process对象下)
涉及到vite.config.js中的一些配置:
- root
- envDir:用来配置当前环境变量的文件地址
vite给我们提供了一些补偿措施:我们可以调用vite的loadEnv来手动确认env文件
process.cwd方法:返回当前node进程的工作目录
.env: 所有环境都需要用到的环境变量
.env.development:开发环境需要用到的环境(默认情况下vite将我们的开发环境取名为development)
.env.production:生产环境需要用到的环境变量【默认情况下vite将我们的生产环境取名为production)
pnpm dev --mode development会将mode设置为development传递进来(补全node参数)
pnpm dev --mode develop mode会被设置为develop
mode是development,vite就会去找.env.development文件
当我们调用loadenx的时候,他会做如下几件事:
- 直接找到.env文件不解释并解析其中的环境变量并放进一个对象里
- 会将传进来的mode这个变量的值进行拼接:`.env.[mode]`,并根据我们提供的目录( process.cwd() )去取对应的配置文件并进行解析,并放进一个对象
- 我们可以理解为:
const baseEnvConfig=读取.env的配置 const modeEnvConfig=读取env相关配置 const latsEnvConfig={...baseEnvConfig,...modeEnvConfig} //loadEnv函数读取两个文件
如果是客户端,vite会将对应的环境变量注入到import.meta.env里去
vite做了一个拦截,他为了防止我们将隐私性的变量直接送进import.meta.env中,所以他做了一层拦截,如果你的环境变量不是以VITE_开头的,他就不会帮你注入到客户端中去.
修改环境变量的开头可以去使用envPrefix配置
//vite.config.js
```
```
const envResolver={
'build':()=>{
console.log('生产环境……')
return Object.assign({},viteBaseConfig,viteProdConfig)
},
'serve':()=>({...viteBaseConfig,...viteDevConfig}),//新配置里是可能会被配置envDir
}
export default defineConfig (({command:'build'|'serve',mode})=>{
//mode 根据当前命令来的
//pnpm dev node默认将mode设置为development(即pnpm dev --mode development)
console.log('command',command)
console.log('process',process,process.env);//不会直接写入process
//当前env文件所在的目录
console.log(process.cwd())
//第二个参数不是必须要使用process.cwd(),也可以是自己写的工作目录,如:'C:\Users\Administrator\Desktop\新建文件夹',
//用于说明env的文件在哪个目录下
const env=loadEnv(mode,process.cwd(),'')//第三个参数默认为.env文件
console.log(env.APP_KEY)//'100'
return envResolver[command]();
}
//.env
APP_KEY='100'
//request.js
console.log(import.meta.env);
为什么vite.config.js可以书写成esmodule的形式?
这是因为vite他在读取这个vite.config.js的时候会率先node去解析文件语法,如果发现你是esmodule规范会直接将你的esmadule规范进行替换变成commonjs规范
//.env.production
APP_KEY=112//打印的import.meta.env对象中不会出现
VITE_APP_KEY=113//打印的import.meta.env对象中出现
//.env.development
APP_KEY=110
VITE_APP_KEY=222
//vite.base.config.js
import {defineConfig} from 'vite'
export default defineConfig({
optimizeDeps: {
exlude:[],//将指定数组中的依赖不进行依赖预构建
},
envPrefix:'ENV_'//配置vite注入客户端环境变量校验的env前缀
})
//.env.development
ENV_APP_KEY=111
ENV_BASE_URL='http://dev.api'
//.env.test
ENV_APP_KEY=119
ENV_BASE_URL='http://test.api'
//request.js
const getUserPosition=()=>{
axios.post({
params:{
APP_KEY=import.meta.env.ENV_APP_KEY
}
})
}
//启动test环境
//package.json
"scripts": {
"dev": "vite",
"build": "vite build"
"test":"vite --mode yest"
}