为什么选择vite?
讲vite之前,我们先来了解一下webpack的原理。
webpack支持多种模块化(浏览器端和服务端都可以运行)。比如:
// index.js
const lodash = require("lodash"); // commonjs规范
import React from 'react'; // es6 module
webpack将上述代码编译:(编译后才能在浏览器运行)
webpack的编译原理:构建AST,抽象语法分析上述js文件有哪些导入和导出的操作,但是这个过程是运行在服务端的。
(function(modules){
function webpack_require() {
// 入口是index.js
modules[entry](webpack_require);
}
}, ({
"./src/index.js": (webpack_require) => {
const lodash webpack_require("lodash");
const Vue = webpack_require("vue");
}
}))
因为webpack支持这种多模块的形式,所以必须统一模块化代码。因此构建项目之前它会把所有的依赖都读取一便进行转换,导致项目启动时非常耗时。
vite: 基于es module,所以启动项目时不需要把所有的代码都读取一遍。
侧重点:
webpack:兼容性
vite:浏览器端的开发体验
vite官方文档
vite初体验
npm init -y
npm i lodash
创建文件:
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>
<script src="./main.js" type="module"></script>
</head>
<body></body>
</html>
main.js
import { x } from "./counter.js";
console.log(x);
counter.js
import _ from "loadash";
const x = 1;
export { x };
在默认情况下,es module导入资源时,要么是绝对路径,要么是相对路径。
对于
import _ from "lodash";
最佳方式应该是从node_modules导入,为什么es官方在我们使用非绝对路径或相对路径导入时,不默认帮我们直接从node_modules下导入呢?
假设es官方帮我们做了这件事情
浏览器帮我们导入了lodash,如果lodash里面又有import导入语句,那么浏览器又会继续加载其他js文件(通过网络请求),性能会很差。类似下图:
commonJS是运行在服务端的,所以跟es module不太一样,它直接读取node_modules下的包。
npm i vite -D
修改package.json
{
"name": "vitePractice",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "vite",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"loadash": "^1.0.0"
},
"devDependencies": {
"vite": "^3.1.8"
}
}
counter.js
import _ from "lodash";
const x = 1;
console.log(_);
export { x };
运行代码
npm run dev
lodash没有报错了,vite帮我们解决了上述问题。
那么vite是怎么解决这个问题的呢?
import _ from "lodash";
// 对于上述语句,vite在处理时发现并没有相对路径或者绝对路径的引用,它会尝试去补全路径
// 结果:
import _ from "/node_modules/.vite/lodash"
原理:
依赖预构建:
首先vite会找到对应的依赖,然后调用esbuild(对js语法进行处理的一个库),将其他规范的代码 转换成esmodule规范,然后放到当前目录下的node_modules/.vite/deps,同时对esmodule规范的各个模块进行统一集成。
解决的问题:
- 不同的第三方包会有不同的导入格式
- 对路径的处理上可以直接使用.vite/deps,方便路径重写
- 网络多包传输的性能问题
vite 开发/生产模式配置
不同环境的配置是不一样的
- vite.dev.config.js 开发环境
- vite.prod.config.js 生产环境
- vite.base.config.js 共有环境
- vite.config.js vite运行调用的环境
package.json:
{
"name": "vitePractice",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "vite",
"build": "vite build",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"loadash": "^1.0.0"
},
"devDependencies": {
"vite": "^3.1.8"
}
}
vite.config.js:
import { defineConfig } from "vite";
import viteBaseConfig from "./vite.base.config";
import viteDevConfig from "./vite.dev.config";
import viteProdConfig from "./vite.prod.config";
const envResolve = {
build: () => ({ ...viteBaseConfig, ...viteProdConfig }),
serve: () => ({ ...viteBaseConfig, ...viteDevConfig }),
};
export default defineConfig(({ command }) => {
console.log(command);
return envResolve[command]();
});
vite.base.config.js:
// 共有的环境配置
import { defineConfig } from "vite";
export default defineConfig({
optimizeDeps: {
exclude: [],
},
});
vite.dev.config.js:
// 开发环境配置
import { defineConfig } from "vite";
export default defineConfig({});
vite.prod.config.js:
// 生产环境配置
import { defineConfig } from "vite";
export default defineConfig({});
vite 环境变量
vite内置了dotenv这个第三方库
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)
yarn dev --mode development 会将mode设置为development传递进来
当我们调用loadenv的时候, 他会做如下几件事:
- 直接找到.env文件不解释 并解析其中的环境变量 并放进一个对象里
- 会将传进来的mode这个变量的值进行拼接: .env.development, 并根据我们提供的目录去取对应的配置文件并进行解析, 并放进一个对象
- 我们可以理解为
const baseEnvConfig = 读取.env的配置
const modeEnvConfig = 读取env相关配置
const lastEnvConfig = { …baseEnvConfig, …modeEnvConfig }
如果是客户端, vite会将对应的环境变量注入到import.meta.env里去。vite做了一个拦截, 他为了防止我们将隐私性的变量直接送进import.meta.env中, 所以他做了一层拦截, 如果你的环境变量不是以VITE开头的, 他就不会帮你注入到客户端中去, 如果我们想要更改这个前缀, 可以去使用envPrefix配置。
补充一个小知识: 为什么vite.config.js可以书写成esmodule的形式, 这是因为vite他在读取这个vite.config.js的时候会率先node去解析文件语法, 如果发现你是esmodule规范会直接将你的esmodule规范进行替换变成commonjs规范
目录
说明:
- .env 公共环境的配置
- .env.development 开发环境的配置
- .env.production 生产环境的配置
格式:KEY = VALUE
客户端只能访问到VITE_为前缀的KEY环境变量
访问格式:import.meta.env
修改文件
package.json:
{
"name": "vitePractice",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "vite --mode development",
"build": "vite build --mode production",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"loadash": "^1.0.0"
},
"devDependencies": {
"vite": "^3.1.8"
}
}
vite.config.js
import { defineConfig, loadEnv } from "vite";
import viteBaseConfig from "./vite.base.config";
import viteDevConfig from "./vite.dev.config";
import viteProdConfig from "./vite.prod.config";
const envResolve = {
build: () => ({ ...viteBaseConfig, ...viteProdConfig }),
serve: () => ({ ...viteBaseConfig, ...viteDevConfig }),
};
/*
@params:
command: buidl | serve 环境
mode: 命令行传进来的值。比如: yarn run dev --mode development
*/
export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd(), "");
console.log(env);
return envResolve[command]();
});
运行结果:
yarn run dev