注:这是一个demo,未编写完成
Vite 是一个前端构建工具
vue-cli 内部使用是 webpack 进行打包,webpack 的热更新为以当前修改的文件作为入口重新 build 打包,所有涉及到的依赖也会被重新加载一次。
vite 开发时会启动一台静态页面的服务器,不会对文件代码进行打包,而是根据客户端的请求加载不同的模块,实现按需加载。
具有快速冷启动、按需编译等优秀特质。
vue-cli 的开发模式是使用的 webpack 的 webpack-dev-server,需要进行打包才可以使用。而 vite 则是基于 ESM(ES Module),不需要打包直接可以运行。
vue-cli 的热更新基于 webpack 即重新打包,vite 的热更新则是基于缓存
两种 vite 创建 vue3 项目方式:
1 | # vue 官网 |
注:弹出的预设选项中,
Vanilla
是原生 JavaScript/TypeScript 项目的意思。
前者可以自动整合 pinia
、vue-router
和 esLint
等工具。
Vite 内部使用名为 Rollup 的打包工具。
Rollup:与 Webpack 类似,但专注于 ES6 的模块打包工具,亮点在于在打包时会静态分析 ES6 模块代码中的 import,排除未实际使用的代码,有助于减小构建包的体积。(Webpack 也有,但概念为 Rollup 最先提出)
插件 @vitejs/plugin-vue
用于为 vite 提供 vue3 单文件组件的支持。
起步
下面来创建一个最简单的 vite 项目。
首先新建目录,使用 npm 初始化该目录,并安装 vite。
1 | mkdir vite-min-test && cd vite-min-test |
在根目录下新建 index.html
与 index.js
,在 index.html
中引入 index.js
,注意 type 应设置为 module
。
1 |
|
修改 package.json
,设置启动与打包命令
1 | { |
启动项目,获得一个开发服务器地址,开始修改 index.js
查看效果。
1 | npm run dev |
打包项目则执行 build
命令即可。
1 | npm run build |
值得注意的是,vite 打包后依然是以 ESM
方式引入 js,而 ESM 要求必须要通过 URL 方式加载,因此打包后本地 file 协议是无法运行的。
而默认情况下,打包生成的 index.html
对入口文件的引入为绝对路径,例如此时为 /assets/index-f271f06b.js
,这就要求了所启动的服务器,例如 vscode 的 live server,必须以打包目录为根目录,这对预览来说非常不方便。因此 vite 又提供了一个预览命令。新增 package.json
如下
1 | { |
该命令将会以打包目录为根目录启动一个服务,执行如下命令即可实现打包结果的预览。
1 | npm run preview |
特点
外置的 index.html
在 vite 中,index.html 被放置在了根目录下。这是由于在 vite 中,index.html 被定义为了开发服务器的入口文件。在打包时,该 index.html 中的 url 将会被自动转换。
预构建
由于在开发阶段中,vite 的开发服务器将所有的代码视为 ESM,这对于部分使用 CommonJS / UMD 模块化的依赖将出现无法兼容的问题。且同时,部分依赖内部存在许多内部模块,分别引入将会有很严重的性能问题。
因此便有了预构建操作,预构建将会把 CommonJS / UMD 转换为 ESM,使得允许开发者直接使用 ESM 导入裸模块;并将有许多内部模块的 ESM 依赖关系转换为单个模块,以提高后续页面加载性能。
在首次执行 vite 服务启动后,将会对 node_modules 模块使用 esbuild 进行预构建,esbuild 使用 Go 语言编写,性能比 JavaScript 编写的打包器预构建要快得多。
TypeScript
Vite 天然支持 .ts
文件的引入,但仅进行 TypeScript 到 JavaScript 的转译工作,不会执行类型的检查。这一过程使用 esbuild 实现,速度较 tsc 要快速许多。
PostCSS
当项目中存在有效的 PostCSS 配置时(例如 postcss.config.js
),将会被自动应用于所有已导入的 css。
且 CSS 压缩将在 PostCSS 处理之后再执行,并会使用 build.cssTarget
选项。
预处理器
Vite 提供了对诸如 .less
、.scss
等文件的内置支持,无需配置 loader,但需要手动安装预处理器
1 | # .scss and .sass |
CSS Modules
对于 .module.css
为后缀的 CSS 文件,将会被认为一个 CSS modules 文件,再导入时将会返回一个模块对象。
1 | /* test.module.css */ |
在入口文件中为元素设置类名,此时页面中可以得到一个红色正方形:
1 | // index.ts |
同样的,也可以同时使用 css modules
与 预处理器,例如 test.modules.scss
。
例如对于 test.module.scss
:
1 | /* test.module.scss */ |
引入并查看打印结果:
1 | // index.ts |
具名导入 css
vite 允许具名导入 CSS 文件来获取处理过后的 CSS 字符串,但需要添加 ?inline
,此时导入的 CSS 文件将不会被注入到页面中。
1 | /* test.css */ |
入口文件中导入并查看打印结果:
1 | import testStyle from "./test.css?inline"; |
同样的,该特性也允许与预处理器共同使用,且得到的字符串为转义过后的 CSS 字符串。
例如对于 test.scss
:
1 | .red { |
入口文件中导入并查看打印结果:
1 | import testStyle from "./test.scss?inline"; |
导入 json
与 webpack 相同,Vite 允许导入 json,以及获取 json 中的某一个属性。
1 | import { version } from "./package.json"; |
将资源引入为 URL
引入一个资源时将会返回解析后的公共路径。
1 | import img from "./assets/images/小孤独.png"; |
且在生产构建后将会自动拼接哈希值,例如此处在开发环境下为 /src/assets/images/小孤独.png
,生产环境下将变为 /src/assets/images/小孤独.5d1fye3.png
。
该行为类似于 webpack 中的 file-loader
,当资源体积小于 build.assetsInlineLimit
时也同样会被内联为 base64 data URL。区别在于导入既可以使用绝对公共路径(基于开发期间的项目根路径),也可以使用相对路径。
url()
在 CSS 中的引用、vue
插件的 SFC 模板中也以同样的方式处理。
显式 URL 引入
常见的图像、媒体和字体文件类型被自动检测为资源,即导入默认获得公共路径,若需要扩展默认列表,啧需要修改选项 build.assetsInclude
。
而对于未被包含在 build.assetsInclude
内的资源,可以通过添加 ?url
后缀来显示的导入为一个 URL。
1 | import importTest from "./import-test.ts?url"; |
public
目录
默认情况下,被放在 public 目录下的文件有以下特点:
- 打包时会被完整复制到目标目录的根目录下
- 开发时能直接通过
/
根路径访问到 - 名称不会被追加 hash 值
注:永远不要在 JavaScript 代码中引用 public 目录中的文件
完整解析静态资源
在原生 ESM 中存在一个 import.meta.url
,它可以暴露当前模块的 URL。将它与原生的 URL 构造器结合使用,可以完整的解析静态资源 URL:
1 | const src = new URL( "./target.ts", import.meta.url ).href; |
注:由于
import.meta.url
在浏览器和 node.js 环境下语义并不相同,因此不要再 SSR 中使用该方法。
客户端类型
Vite 相关的默认的类型定义是编写给自己的 Node.js API 的,要将其补充到一个 Vite 应用的客户端代码环境中,需要在根目录下添加一个 d.ts 声明文件,可暂定为 env.d.ts
:
1 | /// <reference types="vite/client" /> |
或在 tsconfig.json
中的 compilerOptions.types
作如下配置:
1 | { |
点击进入可以发现,vite/client
是一个名为 client.d.ts
的声明文件,里面包含了 import.meta
、*.module.css
、*.mp4
、?url
等所有内置变量、常用文件、CSS Module和拼接参数的类型声明,以便于 TypeScript 实现正常的类型判断。如果你不希望编写代码引入文件时出现奇奇怪怪的 ts 类型报错的话,请务必添加此配置。
注:若要覆盖默认的类型定义,需要在三斜线的上方添加定义,例如:
1
2
3
4
5 declare module '*.svg' {
// ...
}
/// <reference types="vite/client" />
构建生产版本
默认情况下,Vite 的目标是能够支持原生 ESM script 标签、支持原生 ESM 动态导入 和 import.meta
的浏览器,允许通过 build.target
配置项指定构建目标,最低支持 es2015
。
同时,默认情况下 Vite 只处理语法转译,且不包含任何 polyfill。若有兼容低版本浏览器的需求,可以通过插件 @vitejs/plugin-legacy
来实现 polyfill 的语法填充。
环境变量和模式
vite 在一个特殊的 import.meta.env
上暴露环境变量,下面是一些所有情况下都可以使用的内建变量:
1 | // 应用运行的模式 |
在生产环境下,这些变量将会被静态替换。因此在使用时务必使用静态写法,诸如 import.meta.env["MODE"]
的取值是无效的。
偶尔可能会出现 JavaScript 或 Vue 模板中的例如 "import.meta.env.MODE"
字符串也被替换的情况(官网说的,什么怪东西)。
.env
Vite 使用 dotenv
从 envDir
配置项所指定的目录下(默认根路径)的以下文件中加载环境变量,加载的环境变量中以 VITE_
为前缀的将会被挂载到 import.meta.env
上面,其他的则不会(该前缀可以通过配置项 envPrefix
进行修改)。
1 | .env # 所有情况下都会加载 |
.env
文件有以下特点:
- 专门指定了
mode
的环境变量文件的优先级将会高于通用的优先级,即.env.asuka
的优先级比.env
要高。 - Vite 本身存在的环境变量不会被
.env
文件覆盖。 - 对
.env
的文件需要重启 Vite 服务器才能生效。 - 如果需要在环境变量中使用
$
符号,则必须使用\
对其进行转义。
mode
上面的 mode
与内置变量 import.meta.MODE
的值相同。
默认情况下,在开发服务器即执行 vite
命令时,mode
为 development
;而当执行 vite build
所生成的代码,则默认 mode
为 production
。此时会对应的加载 .env.development
或 .env.production
。
可以通过在命令后追加 --mode
flag 来自行指定 mode
。例如如下命令:
1 | { |
此时运行 npm run asuka
,则会加载 .env.asuka
文件(如果有)。
TypeScript 提示
尽管 vite/client
做出了较为全面的类型声明,但它并不包含我们在 .env
中自定义的环境变量,此时我们需要在 env.d.ts
配置我们自己自定义的环境变量的类型声明。
1 | /// <reference types="vite/client" /> |
问题
vite 创建项目后,启动时提示 permission denied 127.0.0.1:5173
?
vite 3.0 后将默认端口改为了 5173
,使用如下命令查看系统保留端口:
1 | netsh int ipv4 show excludedportrange protocol=tcp |
发现该端口的确被列为了保留端口(下图中的 5160 - 5259)。
此时只能修改 vite.config.js
配置,修改为保留端口范围以外的端口。
1 | import { defineConfig } from 'vite' |