前端构建工具vite进阶系列(二) -- vite的依赖预构建与配置文件相关处理
前言
上一章前端构建工具vite进阶系列(一) -- vite比webpack的优势与开箱即用中,我们稍微体验了一下使用vite
来打包资源,并且可以解决非相对/绝对路径的资源引入导致的报错问题,那么这一章我们来讲讲vite
的本身处理。vite
之所以能够比webpack
快几十倍,依赖预构建起到了很大的作用,那么这一章讲讲依赖预构建与环境变量相关的事情吧。
为什么要依赖预构建
-
CommonJS 和 UMD 兼容性: 开发阶段中,
Vite
的开发服务器将所有代码视为原生ES
模块。因此,Vite
必须先将作为CommonJS
或UMD
发布的依赖项转换为ESM
。当转换
CommonJS
依赖时,Vite
会执行智能导入分析,这样即使导出是动态分配的(如React
),按名导入也会符合预期效果:// 符合预期 import React, { useState } from 'react'
-
性能:
Vite
将有许多内部模块的ESM
依赖关系转换为单个模块,以提高后续页面加载性能。一些包将它们的
ES
模块构建作为许多单独的文件相互导入。例如,lodash-es
有超过 600 个内置模块!当我们执行import { debounce } from 'lodash-es'
时,浏览器同时发出600
多个HTTP
请求!尽管服务器在处理这些请求时没有问题,但大量的请求会在浏览器端造成网络拥塞,导致页面的加载速度相当慢。通过预构建
lodash-es
成为一个模块,我们就只需要一个HTTP
请求了!
vite是怎么解决路径问题的
vite
启动的时候,会执行bin
目录下的vite.js
文件,在这个文件里面我们会看到获取了当前的电脑的绝对路径
,如果不包含node_module
路径,就需要引入source-map-support
这个包来处理,如果是相对路径则会进行路径补全,当然在dev
环境下都会有node_module
路径,在prod
环境下,vite
会使用rollup
来进行打包。
进行路径补全当然可以解决这个问题,但是
vite
一想,我既然是基于ESM
规范的,那么如果用户使用了CommonJS
规范的导出包,那我还不是需要编译成ESM
吗?所以vite
就借助了esbuild
库(esbuild
是一个对js
语法处理的库),对用户使用的CommonJS
规范的库进行了转换成ESM
,并且输入在了node_module/.vite/deps
目录下。
- 一来可以统一
ESM
模块,统一模块规范。 - 二来可以解决
路径问题
,方便路径重写。 - 三来可以优化
http多包传输
的性能问题。
为什么原生ESM不支持node_module?:因为如果支持的话,那将会带来非常大的网络性能
问题,对于ESM
里面具有其他依赖的ESM
来说,那浏览器将会无限制
的请求依赖库,举个例子当我们使用lodash-es
库的时候,我们把不进行预构建的模块写在vite.config.json
里面。
export default {
optimizeDeps:{
exclude:["lodash-es"] // 当遇到lodash-es的时候 不进行预构建
}
}
那么就会产生如下多的数不清的文件。
所以vite
能够把ESM
库中的所有三方依赖全部集成,只生成一个
或者多个
文件(多个文件但是不是类似于上面这么多。。。)这才能够说明可以用来优化http多包传输
的性能问题,所以这上面也是vite
的依赖预构建做的事情。
- esbuild库 : esbuild.github.io/
配置文件相关处理
- 语法提示的支持
- 类型注解
/** @type import('vite').UserConfig*/
- 类型注解
-
defineConfig
- 环境配置文件处理
import {defineConfig} from 'vite';
import viteBaseConfig from './vite-base-config.js'
import viteDevConfig from './vite-dev-config.js'
import viteProdConfig from './vite-prod-config.js'
const handleEnv = {
"build":()=>{
// build的时候做的事情
return Object.assign({}, viteProdConfig, viteBaseConfig)
},
"serve":()=>{
// serve的时候做的事情
return Object.assign({}, viteDevConfig, viteBaseConfig)
}
}
export default defineConfig(({command})=>{
console.log(handleEnv[command]())
return handleEnv[command]()
})
- 环境变量处理
-
vite
对环境变量的处理是借助于第三方库dotenv
实现的,执行命令的时候,dotenv
会去读取.env
文件,然后注入到process
对象当中。当然用户配置大于默认,我们可以在vite.config.js
里面配置envDir
去指定环境变量的文件地址。 -
为什么
nodejs
可以读取ESM
规范的vite.config.js
呢?vite
在读取vite.config.js
文件的时候,如果遇到ESM
规范,node
会去解析成CommonJS
规范。- 如何解析:读取文件的结果就是
字符串
,通过替换import
成require
的方式来进行规范转换。
-
通过命令来获取环境参数
export default defineConfig(({command, mode})=>{
// mode 为执行指令环境
// npm run serve => mode:development
// npm run build => mode:production
console.log(handleEnv[command]())
return handleEnv[command]()
})
通过上述代码中的mode
,我们知道了只要我们敲命令,mode
会自动获取到,所以我们可以手动设置.env,vite
给我们提供了一个loadEnv
方法来获取环境变量。
export default defineConfig(({command, mode})=>{
// mode 为执行指令环境
// npm run serve => mode:development
// npm run build => mode:production
const env = loadEnv(mode, process.cwd(), '')
console.log('/////env', env)
console.log(handleEnv[command]())
return handleEnv[command]()
})
我们新建.env.production
和.env.development
文件,分别注入ENV = 10011
和ENV = 10010
// .env.production
ENV = 10011
BASE_URL = 'http://dev.api'
// .env.development
ENV = 10010
BASE_URL = 'https://api'
所以 npm run serve
的结果是:
所以
npm run build
的结果是:
import.meta.env
这个是官方提供的一个变量对象,他能够有效的读取到当前环境变量,我们先来看一看官网。
可以在代码中获取到
import.meta.env
的内容,如果要想获取到环境变量,则需要命名为VITE_*
,为前缀的值,否则vite
访问不到,原因是因为vite
做了一层拦截,把没有带VITE
前缀的变量,不会注入到import.meta.env
中。
当然这是默认的,一切要遵循配置大于默认,所以我们可以在
vite.config.js
中去定制envPrefix:"ENV_"
,才可以。
- 多种环境配置
- 在实际项目中肯定会有
测试环境
、预发布环境
等,那这里我们就按照prepare
来指定一下预发布环境。 .env.prepare
文件。
ENV_PREPARE = 10012 ENV_BASE_URL = https://prepare.api/
- 在实际项目中肯定会有
总结
本文主要讲解了vite
的预构建与config
文件、环境变量的处理,我们看到了其实跟webpack
很类似,在这里我们也预留了问题,dotenv
是怎么去处理环境变量的?ESM
是怎么去变成CommonJS
的,这些问题后期源码阅读的时候,都会得到解答,请戳 >>> 前端构建工具vite进阶系列(三) -- 静态资源与css模块化的处理
转载自:https://juejin.cn/post/7206568194466496573