Skip to content

vite

es 模块

什么是 es 模块?它是 js 定义模块化的一种标准,让代码组织更加清晰,每个模块的命名都不会冲突。

模块的语法有:

js
// 导入
import xx from 'xx'

// 导出
export const a = 1
export default b = 2

在 es6 之前,js 没有原生实现 es 模块化语法。但在 es6 及以后,js 已经原生实现了 es 模块化语法。

处理静态资源

webpack 项目肯定不会处理在 public 目录下或使用绝对路径引入的静态资源,而是直接将其复制到打包后的目录中。

旧版本也不会处理 static 目录下的资源,但会处理 assets 目录下的资源。但新版本不管是 static 还是 assets 目录,都会处理了。

默认情况下,TypeScript 不会将静态资源导入视为有效的模块

public

如果你有下列这些资源:

  • 不会被源码引用(例如 robots.txt)
  • 必须保持原有文件名(没有经过 hash)
  • ...或者你压根不想引入该资源,只是想得到其 URL。

那么你可以将该资源放在指定的 public 目录中,它应位于你的项目根目录。该目录中的资源在开发时能直接通过 / 根路径访问到,并且打包时会被完整复制到目标目录的根目录下。

目录默认是 <root>/public,但可以通过 publicDir 选项 来配置。

请注意:

引入 public 中的资源永远应该使用根绝对路径 —— 举个例子,public/icon.png 应该在源码中被引用为 /icon.png。 public 中的资源不应该被 JavaScript 文件引用。

index.html 与项目根目录

在 vite 中,index.html 中的 URL 将被自动转换,因此不再需要像 webpack 项目使用 %PUBLIC_URL% 占位符了。

%PUBLIC_URL% 的作用是正确处理部署的路径。比如你部署到 /app/ 目录下,而不是根目录,使用了 %PUBLIC_URL% 路径才会找到。

%PUBLIC_URL% 会被替换为 /app/。

html
<link rel="icon" href="<%= BASE_URL %>favicon.ico">

功能

对非常基础的使用来说,使用 Vite 开发和使用一个静态文件服务器并没有太大区别。

然而,Vite 提供了许多增强功能。

依赖解析与预构建

想象你用 express 启动了一个服务器并返回一个 index.html,script 标签设置 type="module",并引入一个入口文件。入口文件中像下面这样裸模块导入。

原生 ES 导入不支持下面这样的裸模块导入:

js
import { someMethod } from 'my-dep'

上面的代码会在浏览器中抛出一个错误。

Vite 将会检测到所有被加载的源文件中的此类裸模块导入,并执行以下操作:

  1. 预构建 它们可以提高页面加载速度,并将 CommonJS / UMD 转换为 ESM 格式。预构建这一步由 esbuild 执行,这使得 Vite 的冷启动时间比任何基于 JavaScript 的打包器都要快得多。

  2. 重写导入为合法的 URL,例如 /node_modules/.vite/deps/my-dep.js?v=f3sf2ebd 以便浏览器能够正确导入它们。

依赖是强缓存的

Vite 通过 HTTP 头来缓存请求得到的依赖,所以如果你想要编辑或调试一个依赖,可以配置禁用强缓存:

js
    optimizeDeps: {
      force: true // 设置为 true 可以强制依赖预构建,而忽略之前已经缓存过的、已经优化过的依赖。
    },

天然支持 ts 文件

但仅转译,不会作类型检查。

在开发时,如果你需要更多的 IDE 提示,我们建议在一个单独的进程中运行 tsc --noEmit --watch。或者如果你喜欢在浏览器中直接看到上报的类型错误,可以使用 vite-plugin-checker。

使用仅含类型的导入导出可以避免一些不正确打包的问题。

js
import type { T } from 'only/types'
export type { T }

TypeScript 编译器选项

tsconfig.json 中 compilerOptions 下的一些配置项需要特别注意。

isolatedModules

孤立模块。应该设置为 true。

下面是一个简单的示例,演示了开启和关闭 isolatedModules 配置的效果。

假设有两个 TypeScript 文件:foo.ts 和 bar.ts,它们的内容如下:

ts
// foo.ts
const foo = 1;

// bar.ts
console.log(foo);

在这个例子中,bar.ts 文件引用了 foo.ts 文件中定义的变量 foo,但是并没有显式地通过 import 和 export 来声明依赖关系。

如果使用默认的编译配置进行编译,即没有开启 isolatedModules 配置,那么编译器会将两个文件视为一个模块,并且会认为变量 foo 是在全局作用域下定义的。这样,bar.ts 文件可以成功地访问到变量 foo,并输出其值为 1。

但是,如果开启 isolatedModules 配置,那么编译器会将每个文件都视为独立的模块,并且会在编译 bar.ts 文件时发现变量 foo 来自于另外一个文件 foo.ts,但是没有通过 import 和 export 声明依赖关系。这样,编译器会报错,提示找不到变量 foo:

shell
error TS2304: Cannot find name 'foo'.

为了解决这个问题,我们需要显式地在 bar.ts 文件中引入 foo.ts 文件中定义的变量 foo,如下所示:

js
// bar.ts
import { foo } from './foo';
console.log(foo);

这样,当开启 isolatedModules 配置时,编译器就能正确地识别依赖关系,并且可以成功地编译和运行代码。

useDefineForClassFields

类字段定义语法。默认为 true。

ts
// 在这个例子中,count 属性会被定义为 Person 类的静态属性,因此我们可以通过类名来访问它。
// 而 name 和 age 属性会被定义为 Person 类的实例属性,在每个 Person 对象上都会有一份,因此我们可以通过实例来访问它们。
class Person {
  static count = 0;
  name: string = '';
  age: number = 0;
}

客户端类型

Vite 默认的类型定义是写给它的 Node.js API 的。要将其补充到一个 Vite 应用的客户端代码环境中,请添加一个 d.ts 声明文件:

比如 src/vite-env.d.ts 文件

ts
/// <reference types="vite/client" />

同时,你也可以将 vite/client 添加到 tsconfig 中的 compilerOptions.types 下:

json
{
  "compilerOptions": {
    "types": ["vite/client"]
  }
}

这将会提供以下类型定义补充:

  • 资源导入 (例如:导入一个 .svg 文件)
  • import.meta.env 上 Vite 注入的环境变量的类型定义
  • import.meta.hot 上的 HMR API 类型定义

css

内联与 url 导入

Vite 通过 postcss-import,支持 css 文件的内联导入和 url 导入。(style 和 url('./xx'))

PostCSS 配置

自动加载 PostCSS 配置。(任何受 postcss-load-config 支持的格式,例如 postcss.config.js)

请注意,CSS 最小化压缩将在 PostCSS 之后运行,并会使用 build.cssTarget 选项。

CSS 预处理器

只需下载对应预处理器,然后 <style lang="sass"> 自动开启

shell
# .scss and .sass
npm add -D sass

静态资源处理

导入一个静态资源会返回解析后的 URL:

js
import imgUrl from './img.png'
document.getElementById('hero-img').src = imgUrl

添加一些特殊的查询参数可以更改资源被引入的方式:

js
// 显式加载资源为一个 URL
import assetAsURL from './asset.js?url'
js
// 以字符串形式加载资源
import assetAsString from './shader.glsl?raw'
js
// 加载为 Web Worker
import Worker from './worker.js?worker'
js
// 在构建时 Web Worker 内联为 base64 字符串
import InlineWorker from './worker.js?worker&inline'

JSON

JSON 可以被直接导入 —— 同样支持具名导入:

js
// 导入整个对象
import json from './example.json'
// 对一个根字段使用具名导入 —— 有效帮助 treeshaking!
import { field } from './example.json'

Glob 导入

Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块:(其实是封装的 fast-glob 模块)

js
const modules = import.meta.glob('./dir/*.js')

以上将会被转译为下面的样子:

js
// vite 生成的代码
const modules = {
  './dir/foo.js': () => import('./dir/foo.js'),
  './dir/bar.js': () => import('./dir/bar.js'),
}

你可以遍历 modules 对象的 key 值来访问相应的模块:

js
for (const path in modules) {
  modules[path]().then((mod) => {
    console.log(path, mod)
  })
}

上面是懒加载的,在构建时会分离出 chunk。如果想要一次性全部加载出来,可以传入第二个参数 eager:

js
const modules = import.meta.glob('./dir/*.js', { eager: true })

以上会被转译为下面的样子:

js
// vite 生成的代码
import * as __glob__0_0 from './dir/foo.js'
import * as __glob__0_1 from './dir/bar.js'
const modules = {
  './dir/foo.js': __glob__0_0,
  './dir/bar.js': __glob__0_1,
}

插件

使用插件

若要使用一个插件,需要将它添加到项目的 devDependencies 并在 vite.config.js 配置文件中的 plugins 数组中引入它。例如,要想为传统浏览器提供支持,可以按下面这样使用官方插件 @vitejs/plugin-legacy:

shell
npm add -D @vitejs/plugin-legacy
js
// vite.config.js
import legacy from '@vitejs/plugin-legacy'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    legacy({
      targets: ['defaults', 'not IE 11'],
    }),
  ],
})

查找插件

https://cn.vitejs.dev/guide/using-plugins.html#finding-plugins

指明模式调用

默认情况下插件在开发 (serve) 和生产 (build) 模式中都会调用。如果插件在服务或构建期间按需使用,请使用 apply 属性指明它们仅在 'build' 或 'serve' 模式时调用:

js
// vite.config.js
import typescript2 from 'rollup-plugin-typescript2'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    {
      ...typescript2(),
      apply: 'build',
    },
  ],
})

创造插件

https://cn.vitejs.dev/guide/using-plugins.html#building-plugins

构建生成版本

当需要将应用部署到生产环境时,只需运行 vite build 命令。

处理浏览器兼容性

用于生产环境的构建包会假设目标浏览器支持现代 JavaScript 语法。默认情况下,Vite 的目标是能够 支持原生 ESM script 标签、支持原生 ESM 动态导入 和 import.meta 的浏览器:

Chrome >=87
Firefox >=78
Safari >=14
Edge >=88

你也可以通过 build.target 配置项 指定构建目标,最低支持 es2015。

请注意,默认情况下 Vite 只处理语法转译,且 默认不包含任何 polyfill。

传统浏览器可以通过插件 @vitejs/plugin-legacy 来支持,它将自动生成传统版本的 chunk 及与其相对应 ES 语言特性方面的 polyfill。兼容版的 chunk 只会在不支持原生 ESM 的浏览器中进行按需加载。

公共基础路径

配置 base 选项。

库模式

部署静态站点

本地预览

首先确保有这两个脚本,先执行 build 脚本,打包产物会放在 dist 目录下。然后执行 preview 脚本,会在 dist 目录下起一个服务供浏览器预览。

json
{
  "scripts": {
    "build": "vite build",
    "preview": "vite preview"
  }
}

GitHub Pages

如何部署。

在 GitHub 上创建一个新的仓库(Repository)。请注意,这个仓库的名称必须为“your_username.github.io”,其中“your_username”是你的 GitHub 用户名。这是因为 GitHub Pages 的网址是基于你的用户名的。

在仓库中添加一个 index.html 文件,这将是你网站的主页。你也可以添加其他的 HTML、CSS、JavaScript 文件和图片等内容。

  1. 在 vite.config.js 中设置正确的 base。

如果你的仓库名为 <USERNAME>.github.io(http 链接为 https://github.com/<USERNAME>/<USERNAME>.github.io),那么 github pages 将会被部署在 https://<USERNAME>.github.io/ 上,你可以省略 base 使其默认为 '/'。

如果你的仓库名<USERNAME>.github.io(http 链接为 https://github.com/<USERNAME>/<REPO>),那么 github pages 将会被部署在 https://<USERNAME>.github.io/<REPO>/ 上,这时候设置 vite.config.js 的 base 为 '/<REPO>/'

  1. 进入仓库 settings,设置 github pages。可以设置选择部署的分支,部署的目录。部署目录只能选择根目录或 docs 目录。

环境变量和模式

在 vite.config.js 中,可以传入一个函数,返回配置对象。

函数参数是一个对象,有 command、mode 属性。

command 有两种值,serve、build,分别代表开发环境和生产环境。

mode 就是用户通过脚本 --mode 参数传递给 vite 的值。

在 Vite 的 API 中,在开发环境下 command 的值为 serve(在 CLI 中,vite dev 和 vite serve 是 vite 的别名),而在生产环境下为 build(vite build)

Released under the MIT License.