共计 5065 个字符,预计需要花费 13 分钟才能阅读完成。
通过需求 / 痛点 来探索 vite 的产生背景以及 vite 的 作用
在企业级开发中:通常会具有如下的开发经历:
- ts 文件 —> tsc —>js 文件
- react / vue 安装 React-compiler / vue-compiler 将我们写的 jsx 代码或者.vue 文件转换为 render 函数
- less/sass/postCss/component-style 遇到这些 我们需要安装 less-loader/sass-loader 等等
- 语法降级 babel—> 将 es 转换为旧版浏览器可以接受的语法
- 体积优化:uglifyjs —> 将我们的代码进行压缩变成体积更小性能更高的文件
- 。。。
当我们改动一点代码后,就有可能 重新挨个手动执行以上的流程 非常麻烦
将 APP.tsx —>tsc —> APP.jsx —>Router-compiler –>app.js
我们希望:
我们写的代码一变化 —> 有人帮我们自动去 tsc react-compiler, less,bable,uglifyjs 全部挨个走一遍 —> 形成 js 文件
这个东西就叫做 构建工具
打包:将我们写的浏览器不认识的代码 交给构建工具进行编译处理的过程就叫做打包
打包完成后,会给我们 浏览器可以认识的文件
一个构建工具 到底承担了哪些脏活累活:
- 模块化开发支持 支持 直接从 node_modules 中引入代码 + 多种模块化支持
// 比如
import _ from 'lodash' // 直接从 node_modules 中取得模块 但在 index.html 中浏览器并不知道 node_modules
// commonJS 规范的模块化
const item = require('lodash')
- 处理代码兼容性:比如 babel 语法降级,less ts 的语法转换 , 是构建工具 将这些语法的对应的处理工具 集成起来 并进行自动化处理
- 提高项目性能:压缩文件,代码分割
- 优化开发体验:
- 构建工具会帮你自动监听文件的变化,当文件变化以后 会自动帮你调用对应的集成工具并重新打包 在浏览器重新运行 这整个过程叫做:热更新(HRM)
- 开发服务器:跨域的问题,
构建工具可以让我们不用每次都关心我们的代码在浏览器如何运行 我们只需要首次给构建工具 提供一个配置文件,这文件也不是必须的,如果不给,他会有默认的文件配置 有了这个集成的配置文件 我们就可以在下次需要更新的时候 调用一次对应的命令即可 再结合 热更新,就更加不需要管任何东西,他让我们不用关心生产的代码也不用关心代码如何在浏览器运行。只需要关心我们的开发 如何书写的更高效
与 webpack 对比
webpack 支持多种模块化:
const lodash = require('lodash')
import vue from 'vue' // 两种模块化
对于 webpack 支持同时使用的
会转化为:
const lodash = webpack_require('lodash')
const vue = webpack_require('vue')
webpack 的编译原理 AST 抽象语法分析的工具,分析处你写的这个 js 文件有哪些导入和导出操作,
构建工具运行在服务端。
因为 webpack 支持多种模块化,它一开始必须要统一模块化代码,所以意味着它要将所有依赖全部读一遍
vite 是基于 es module, 侧重点不同,webpack 更多的关注兼容性,vite 更关注浏览器端的开发体验
必须理解的 vite 脚手架和 vite 的区别
比如 使用脚手架:
yarn create vite
会发生:
- 帮我们全局安装一个东西:create-vite (vite 的脚手架)
- 直接运行 create-vite bin 目录下的一个执行配置
误区:认为官网中使用对应 yarn create 构建项目的过程也是 vite 在左做的事情
create-vite 和 vite 的关系是:create-vite 内置了 vite
使用 vue-cli 会内置 webpack
脚手架:直接帮助我们使用预设(已经下载好了一系列开发时用到的插件 是一种最佳实践 不再需要从 0 搭建一个项目)并且是官方推荐的
vite 启动项目初体验
开箱即用,在不需要做任何额外的配置就可以使用 vite 来处理构建工具。
默认情况下,es module 去导入资源 要么是绝对路径,要么是相对路径。
而不能通过 直接书写 某个模块的名称便可导入,这需要构建工具来处理。
commonJS 运行在服务端,在本地直接读取文件速度极快。而在浏览器端是没有实现 commonJS 的
vite 用于做代码处理 因此:
npm i vite -D
import _ from "lodash"
在处理的过程中如果看到了有非绝对路径和相对路径的引用,vite 会自动进行路径补全, 如:
import _vite_cjsImport_lodash from 'node_modules/.vite/lodash.js?v=xxxxx'
找寻依赖的过程是自当前目录依次向上查找,直到搜寻到根目录或者搜寻到对应依赖位置
生成和开发环境
npm run dev —> 每次依赖预构建 所重新构建的相对路径都是正确的
在生产环境
vite 会全权交给一个叫做 rollup 的库去完成生产环境的打包
实际上,vite 在考虑另外一个问题
有的包以 commonJS 的规范的格式导出:module.exports= {}
依赖预构建:首先,vite 会找到对应的依赖,然后调用 esbuild (go 编写 对 js 语法进行处理的一个库),将其他规范的代码 转换成 es module 规范然后放在当前目录下的 node_modules/.vite/deps
同时对 es module 的规范进行统一
这个过程就是 依赖 预构建,这个过程解决了这些问题:
- 不同的第三方库会有不同的 导出格式,这是 vite 自身无法约束的。
- 对路径的处理上 可以直接使用 .vite/deps 方便路径重写
- 网络多包传输的性能问题
(也是原生 es module 规范不敢支持 node_modules 的原因之一)
有了依赖预构建以后,无论有多少额外的 export 和 import vite 都会尽可能的只生成一个或者几个模块
vite.config.js === webpack.config.js
vite 配置文件的处理细节
- vite 配置文件的语法提示
如果使用 webstorm 那么可以得到很好的语法补全
如果使用 vscode 或者其他的编辑器,则需要做一些特殊处理
import {defineConfig} from 'vite'
export default defineConfig({
// 则可以得到语法提示
})
类型标注:
/**
* @param
* @return {string}
*/
function fun(){}
/**
* @type import('vite').UserConfig
* */
const viteConfig = {
optimizeDeps:{
exclude:[]
}
}
- 关于环境的处理
过去我们使用 webpack 的时候,我们要区分配置文件的一个环境
- webpack.dev.config
- webpack.prod.config
- vite.base.config
import {defineConfig} from 'vite';
import viteDevConfig from './vite.dev.config'
import viteProdConfig from './vite.prod.config'
import viteBaseConfig from './vite.base.config'
// 生产 与 开发环境的处理
const envResolver = {
"build": () =>
Object.assign({},viteBaseConfig, viteProdConfig),
"serve":() =>
Object.assign({},viteBaseConfig, viteDevConfig)
}
export default defineConfig(({command})=>{
// 是 build 还是 serve 主要取决与 我们执行的命令 是开发 dev 还是 生产 build
console.log('command',command)
return envResolver[command]();
})
这可以处理 在开发 或者 生产环境 指定 vite 的行为
vite 环境变量配置
环境变量:会根据当前代码环境产生值的变化的变量
代码环境:
- 开发环境
- 测试环境
- 预发布环境
- 灰度环境
- 生产环境
在和后端对接时,前端在开发环境中请求后端 API 地址和生产环境的后端 API 不会是同一个地址。
在 vite 中的环境变量处理:内置了dotenv 这个第三方库
在开启开发服务器 npm run dev 会读取目录下的.env 文件,并解析文件中的对应环境变量 并将其注入到 process 对象下
但是 vite 考虑到和其他配置的一些冲突问题,他不会直接注入到 process 对象下。
涉及到 vite.config.js 的一些配置
- root
- envDir 用来配置当前环境变量的文件地址
process.cwd 方法 返回按当前 node 进行的工作目录
在调用 loadEnv()方法时,会做如下几件事:
- 直接找到.env 文件 并解析其中的环境变量
- 会将传入的 mode 变量的值 进行拼接
并根据我们提供的目录(process.cwd()) 来取对应的配置文件进行解析,并放进一个对象.env.development
- 可以理解成
const baseEnvConfig = 读取.env 的配置
const modeEnvConfig = 读取 env 相关配置
const lastEnvConfig = {
...baseEnvConfig,
...modeEnvConfig
}
如果时客户端,vite 会将环境变量注入到 import.meta 对象中
vite 做了一个拦截,为了防止我们将隐私性的变量直接送进 import.meta.env 中 所以他做了拦截,如果环境变量的前缀 不是以 VITE_ 开头
为什么 vite.config.js 中可以书写成 es module 形式?
在 node 端,模块化使用 CommonJS 规范
这是因为 vite 在读取 vite.config.js 文件时,会率先 node 去解析文件语法,如果发现是 es module 规范会直接将其替换成 commonJS 规范
node 在读取文件时,可以得到文件内容的字符串,通过 字符串 api 的 replace 方法进行替换
.env:所有环境都需要用到的环境变量
: 默认情况下 开发环境需要用到的环境变量,默认下,vite 会将我们开发环境取名为 development.env.development
.env.production 生产环境我们要用到的环境变量,也是 vite 默认将生产环境取名为 production
vite.config.js 中
defineConfig(({comman,mode}=>{}))
- command 也会根据环境时,
- mode 是根据使用的命令来决定的,当使用 npm run dev vite 就会将 mode 设置为开发环境的名称
这个操作也等于:npm run dev –mode development 用来在开启开发服务器时手动设置 mode
【原理】vite 是如何让浏览器识别.vue 文件的
yarn install vite
yarn create 实际上等于在安装 create-vite 脚手架 然后使用脚手架的指令去构建项目
实习一套简单的 vite 的开发服务器
目的:
- 解决标题问题
- 对开发服务器的原理层面有一个基础的简单认识
会涉及 node 的一些知识
koa.js : node 端框架
在 vite 中处理 css
vite 天生就支持对 css 的直接处理
- vite 读取到 main.js 时 也会读取 导入的 css 文件
- 直接去使用 fs 模块去读取 index.css 中文件内容
- 直接创建一个 style 标签,将 index.css 内容直接 copy 进 style 标签里
- 将 style 标签插入到 index.html 的 head 中
- 将该 css 文件中的内容直接替换为 js 脚本
- (方便热更新或者 css 模块化,)同时设置 Content-Type 为 js 从而让浏览器以 JS 脚本形式来执行该 CSS 文件
不同环境的配置文件
在开发环境中运行npm run dev
执行对应的命令 –mode development 会默认带该参数 表示当前环境为开发环境,那么会读取根目录下的
.env.development
相应的 在生产环境 也就是运行npm run build
命令 默认跟一个参数 –mode production 表示当前环境是生产环境会 会读取根目录下的
.env.development