使用 import.meta.env 可以在很多方面帮助你:
- 根据 Quasar 模式(SPA/PWA/Cordova/Electron)区分运行时逻辑
- 根据是开发构建还是生产构建来区分运行时逻辑
- 在构建时基于终端环境变量添加标志
关于客户端代码与后端代码的术语说明
本页中我们会使用 客户端代码 和 后端代码 这两个术语:
- 后端代码指的是 /quasar.config 文件以及用户无法直接访问的代码,如 SSR Web 服务器或 SSR 服务端代码。
- 客户端代码指的是会发送到用户浏览器的代码,如 /src、/src-pwa、/src-electron、SSR 客户端等。
Quasar CLI 提供的值
import.meta.env.<name> | 类型 | 含义 |
|---|---|---|
QUASAR_DEV | Boolean | 代码运行在开发模式 |
QUASAR_PROD | Boolean | 代码运行在生产模式 |
QUASAR_DEBUG | Boolean | 代码运行在开发模式,或生产模式下设置了 --debug 标志 |
QUASAR_CLIENT | Boolean | 代码运行在客户端(非服务端) |
QUASAR_SERVER | Boolean | 代码运行在服务端(非客户端) |
QUASAR_MODE | String | Quasar CLI 模式(spa、pwa 等) |
QUASAR_<MODE>_MODE | Boolean | 代码运行在 <MODE> Quasar 模式下。例如:QUASAR_ELECTRON_MODE |
QUASAR_TARGET | String | Cordova/Capacitor 模式下为 ios 或 android,BEX 模式下为 chrome 或 firefox |
用法
Javascript / Typescript
if (import.meta.env.QUASAR_DEV) {
console.log(`I'm on a development build`)
}
// import.meta.env.MODE 是
// "quasar dev/build -m <mode>" 中的 <mode>
//(如果未指定 -m 参数,默认为 'spa')
if (import.meta.env.QUASAR_MODE === 'electron') {
// ...
}
// 或者使用:
if (import.meta.env.QUASAR_ELECTRON_MODE) {
// ...
}编译你的网站/应用时,依赖 import.meta.env 的 if () 分支会被求值,如果表达式为 false,它们会从文件中被移除。例如:
if (import.meta.env.QUASAR_DEV) {
console.log('dev')
} else {
console.log('build')
}
// 使用 "quasar dev" 运行时结果为:
console.log('dev')
// 使用 "quasar build" 运行时结果为:
console.log('build')注意上面的 if 在编译时被求值并完全剥离,从而产生更小的包体积。
你还可以将上面学到的内容与动态导入结合使用:
if (import.meta.env.QUASAR_MODE === 'electron') {
import('my-fancy-npm-package').then(package => {
// 注意下面的 "default",这是你访问 npm 导入包导出内容的属性
package.default.doSomething()
})
}
// 或者:
if (import.meta.env.QUASAR_ELECTRON_MODE) {
// ...
}/index.html 文件
<%= importMetaEnv.MY_ENV_VAR_OR_DEFINE %>
<!-- 或者使用简写方式: -->
%MY_ENV_VAR_OR_DEFINE%
<% if (importMetaEnv.MY_ENV_VAR_OR_DEFINE) { %>Wow!<% } %>添加到 import.meta.env
通过 /quasar.config 文件
你可以通过 /quasar.config 文件添加自定义定义:
build: {
/**
* 定义全局常量替换。条目在开发时定义为全局变量,
* 在构建时进行静态替换。
*
* 这会传递给 Vite 和 Rolldown 自身的 "define" 选项,
* 后者使用 Oxc 的 "define" 功能来执行替换。
*
* 所有非字符串值会被 Quasar CLI 自动 JSON.stringify。
* 这确保了 Vite 和 Rolldown 构建之间的一致性,并避免 Rolldown 失败。
*
* @example { __APP_VERSION__: JSON.stringify('v1.0.0') }
* @example { 'import.meta.env.APP_VERSION': JSON.stringify('v1.0.0') }
* @example { __API_URL__: 'window.__backend_api_url' }
*/
define?: Record<
string,
string | number | boolean | undefined | null | any[] | Record<string, any>
>;
/**
* `define` 的语法糖。定义全局常量替换,
* 会自动转换为带有 `import.meta.env` 前缀的 "define" 条目,
* 并且已经过 JSON 字符串化。
*
* @example { SOME_DEFINE: 'my-string' } 会转换为 { 'import.meta.env.SOME_DEFINE': '"my-string"' }
* @example { VERSION: 22 } 会转换为 { 'import.meta.env.VERSION': '22' }
*/
defineEnv?: Record<
string,
string | number | boolean | undefined | null | any[] | Record<string, any>
>;
}export default defineConfig(ctx => {
return {
// ...
build: {
// 从 quasar.config 文件向 UI 代码传递数据
define: {
'import.meta.env.API': JSON.stringify(
ctx.dev ? 'https://dev.api.com' : 'https://prod.api.com'
),
'import.meta.env.VERSION': JSON.stringify(22)
},
// 或者使用 defineEnv 的语法糖:
defineEnv: {
API: ctx.dev ? 'https://dev.api.com' : 'https://prod.api.com',
VERSION: 22
}
}
}
})然后在你的网站/应用中,你可以访问 import.meta.env.API,它会根据开发或生产构建类型指向上面两个链接之一。import.meta.env.VERSION 也同样可用。
TIP
build.define 和 build.defineEnv 之间有一个根本区别。build.defineEnv 是 build.define 的语法糖:
- 它会自动转换为 build.define 语法,在 key 前添加 “import.meta.env.” 前缀并对值进行 JSON 字符串化。
- build.define 还可以用于注入非 “import.meta.env” 的定义。例如:
build: {
define: {
__APP_VERSION__: JSON.stringify('v1.0.0')
}
}
// 然后直接使用 __APP_VERSION__从命令行
我们可以从终端设置变量。
MY_API=api.com QCLI_MY_API=api.com quasar build// 客户端代码:
console.log(import.meta.env.QCLI_MY_API) // "api.com"
console.log(import.meta.env.MY_API) // undefined
// 后端代码
console.log(import.meta.env.QCLI_MY_API) // "api.com"
console.log(import.meta.env.MY_API) // "api.com"我们可以将其与来自终端的值结合使用:
# 在终端设置环境变量
MY_API=api.com quasar build
MY_API=api.com quasar dev// 然后在 /quasar.config 文件中获取它
build: {
defineEnv: {
API: ctx.dev
? 'https://dev.' + import.meta.env.MY_API
: 'https://prod.' + import.meta.env.MY_API
}
}从 dotenv 文件
# 不会暴露给客户端代码,但会暴露给后端;
# 因为它不以 QCLI_ 前缀开头:
SOME_VAR=content
# 这会嵌入到后端和客户端代码中,
# 因为它以 QCLI_ 前缀开头:
QCLI_SOME_VAR=contentimport.meta.env.SOME_VAR // ❌ 在客户端代码中不可用
import.meta.env.QCLI_SOME_VAR // ✅ 在客户端代码中可用你的 dotenv 文件可以包含引用(跨 dotenv 文件,但顺序很重要):
PASSWORD="s1mpl3"
# 引用上面的 PASSWORD:
DB_PASS=$PASSWORD
# 引用来自终端的变量;
# 假设我们在终端中定义了 SOME_VALUE:
MY_VAR=$SOME_VALUE错误用法
如果你以错误的方式访问变量或配置有误,可能会在浏览器控制台中看到 X is undefined 错误。
// 在 /quasar.config 文件中:
build: {
defineEnv: {
FOO: 'hello',
}
}
// 在你的应用中:
console.log(import.meta.env.FOO) // ✅
console.log(import.meta.env.foo) // ❌ 区分大小写
console.log(import.meta.env.F0O) // ❌ 变量名拼写错误(中间的 o 是数字 0)
console.log(import.meta.env.BAR) // ❌ 未在配置中定义对于 /src-ssr、/src-electron 等(Quasar 模式目录)中的代码,还有额外的限制:
const { FOO } = import.meta.env // ❌ 不允许解构或类似操作
console.log(import.meta.env.FOO) // ✅ 只能替换这样的直接使用
function getEnv(name) {
return import.meta.env[name] // ❌ 无法分析动态使用
}
console.log(import.meta.env) // ❌ 会输出 {}构建 import.meta.env 的顺序
你可能已经注意到,import.meta.env 是从多个输入源构建的。你可能会遇到覆盖定义的情况。为了明确,以下是考虑的顺序:
- Dotenv 文件
- 终端变量
- /quasar.config > build > define
- /quasar.config > build > defineEnv
类型推断
Quasar CLI 会自动推断并维护 process.env(终端变量)、dotenv 文件、/quasar.config > define 和 defineEnv 的类型。
| 示例值 | 推断类型 | 替换为 |
|---|---|---|
| true | boolean | true |
| 42 | number | 42 |
| null | null | null |
| hello | string | "hello" |
| [1,2,3] | string | "[1,2,3]" |
| {some:‘obj’} | string | "{some:obj}" |
以下是输入/配置和输出(你的代码)的示例:
# 我们在调用 quasar 命令时声明一些 process env 变量:
FLAG_BOOL=true FLAG_STR=HelloWorld FLAG_NULL=null FLAG_ARR=[1,2,3] FLAG_OBJ={key:'val'} quasar devconsole.log(
import.meta.env.FLAG_BOOL, // true
typeof import.meta.env.FLAG_BOOL // boolean
);
console.log(
import.meta.env.FLAG_STR, // "HelloWorld"
typeof import.meta.env.FLAG_STR // string
);
console.log(
import.meta.env.FLAG_NULL, // null
import.meta.env.FLAG_NULL === null // is true
);
console.log(
import.meta.env.FLAG_ARR, // "[1,2,3]"
typeof import.meta.env.FLAG_ARR // string
);
console.log(
import.meta.env.FLAG_OBJ, // "{key:'val'}"
typeof import.meta.env.FLAG_OBJ // string
);TypeScript 的 IntelliSense 支持
Quasar CLI 会考虑 process.env(终端变量)、dotenv 文件和 /quasar.config > build.define & build.defineEnv 来自动注入类型。你的项目目录中不需要 env.d.ts 文件。
TIP
你可以从 /.quasar/quasar.d.ts 文件中查看自动注入的类型。这个文件是自动生成的,请不要手动修改,因为它会在下次 quasar 命令调用时被覆盖。
但是,有些情况 Quasar CLI 无法自动处理:
- 来自 dotenv 文件的定义,仅用于 /quasar.config 文件本身,而不用于你的应用。
- 动态的 process.env 变量。假设你有一个 /package.json 脚本:“dev:xyz”: “XYZ=true quasar dev”;如果你只在这个特定调用中定义了 XYZ,那么在其他没有 XYZ=true 的调用中,Quasar CLI 将无法知道可能存在的 XYZ 定义,因此不会注入它。
对于上述情况,你可以自行定义:
/**
* 为你的自定义变量添加类型(Quasar CLI 未自动添加的),
* 以避免 TypeScript 错误,例如动态的 process.env 变量
* 或仅为 /quasar.config 文件本身配置的 dotenv 文件中的定义。
*
* @example
* interface ImportMetaEnv {
* readonly MY_VAR: string;
* readonly MY_OTHER_VAR: string;
* }
*/
interface ImportMetaEnv {}对于自动类型推断和声明不适用于你特定情况的场景,你可以通过 /quasar.config 文件指示 Quasar CLI 忽略它,然后在 /env.d.ts 中自行声明:
build: {
env: {
/**
* 忽略这些变量的自动推断和类型声明;
* 变量可以来自 process.env(终端变量)、
* dotenv 文件或 quasar.config > build > define/defineEnv。
*
* 使用代码中转换后的完整变量名。
* @example 'all' 忽略所有变量
* @example ['import.meta.env.OTHER_VAR', '__SOME_VAR__'] 忽略特定变量。
* @default []
*/
ignoreType: ['import.meta.env.MY_SPECIAL_VAR']
}
}更多关于 dotenv 文件
.env 文件(读作 “dotenv”)是一个简单的文本文件,用于存储软件项目的环境变量。你可以将敏感信息或配置设置放在这个文件中,而不是直接硬编码到源代码里。
其中定义的变量会被转换为 import.meta.env.<VAR_NAME> 并在构建时被替换。
为什么使用 .env 文件?
- 安全性:它可以保护敏感数据的安全,如数据库密码、API 密钥和密钥令牌。因为 .env 文件不纳入版本控制(如 GitHub),你的密钥不会暴露给公众或团队中的所有人。
- 可移植性:它允许你的应用根据环境(开发、测试或生产)表现不同,而无需修改代码。你只需为每个环境替换 .env 文件即可。
- 简洁性:它将配置集中到一个易于阅读的文件中。
Dotenv 文件名
以下文件会被自动检测和使用(顺序很重要):
.env # 在所有情况下加载
.env.local # 仅在开发时加载,被 git 忽略…其中"被 git 忽略"假设项目目录是在此包发布后创建的默认目录,否则请将 .env.local 添加到你的 /.gitignore 文件中。
你可以配置 Quasar CLI 来考虑更多文件,你将在后续章节中了解到。
文件格式
.env 文件使用简单的 KEY=VALUE 格式。等号周围没有空格,通常只有在值包含空格时才需要引号。
# 数据库配置
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=super_secret_password
DB_PORT=5432
# API 密钥
STRIPE_API_KEY=sk_test_123456789
NODE_ENV=development暴露给客户端代码
出于安全考虑,并且只暴露明确意图的变量,Quasar CLI 会过滤掉不以 QCLI_ 前缀开头的变量名(对于暴露给客户端的代码),同时保留所有变量给后端代码(例如:SSR 服务端代码,客户端无法直接查看)。这个前缀可以通过 /quasar.config 文件更改。
# 不会暴露给客户端代码,但会暴露给后端;
# 因为它不以 QCLI_ 前缀开头:
SOME_VAR=content
# 这会嵌入到后端和客户端代码中,
# 因为它以 QCLI_ 前缀开头:
QCLI_SOME_VAR=contentimport.meta.env.SOME_VAR // ❌ 在客户端代码中不可用
import.meta.env.QCLI_SOME_VAR // ✅ 在客户端代码中可用Dotenv 的黄金法则
永远不要将你的 .env 文件提交到版本控制中。
你必须立即将 .env 添加到项目的 .gitignore 文件中。如果你不小心将包含真实 API 密钥的 .env 文件推送到公共仓库,机器人可以在几秒钟内抓取这些密钥并利用它们。
相反,你可能想在项目目录中包含一个 .env.example 或 .env.template 文件。这个文件包含键名但留空值或填入虚拟数据,向其他开发者展示项目运行需要哪些变量:
DB_HOST=
DB_USER=
DB_PASSWORD=
DB_PORT=
STRIPE_API_KEY=
NODE_ENV=配置 Dotenv 文件
build: {
env: {
/**
* 出于安全原因,只有带此前缀的变量(来自 env 文件和 Node.js process.env)
* 才会暴露给发送到客户端的代码。
* 客户端应用代码包括 Electron main 和 preload 脚本,
* 因为它们也会被发送到客户端。
*
* 暴露给客户端应用代码的变量不应包含敏感信息,如 API 密钥。
*
* 避免将其设置为 'QUASAR_',以免与 Quasar 自身的环境变量冲突。
*
* 将其设置为空字符串将默认使用默认值(QCLI_)。
*
* @default 'QCLI_'
*/
clientPrefix?: string | string[];
/**
* 设置此前缀将过滤暴露给后端代码(如 SSR 服务端)的
* env 文件变量和 Node.js process.env 变量。
*
* 避免将其设置为 'QUASAR_',以免与 Quasar 自身的环境变量冲突。
*
* @default ''
*/
backendPrefix?: string | string[];
/**
* Quasar CLI 查找 .env* 文件的目录。
* 可以是绝对路径或相对于项目根目录的相对路径。
*
* @default appPaths.appDir
*/
folder?: string | string[];
/**
* 要加载的额外 .env* 文件。
* 每个条目可以是绝对路径或相对于
* quasar.config > build > folder 的相对路径。
*
* @example ['.env.somefile', '../.env.someotherfile']
*/
file?: string | string[];
/**
* 过滤暴露给应用代码的 env 文件变量和 Node.js process.env 变量。
* 这不影响直接分配给 quasar.config > build > define 属性的值。
*/
filter?: (
env: Record<string, string>,
type: "client" | "backend"
) => Record<string, string>;
/**
* 忽略这些变量的自动推断和类型声明;
* 变量可以来自 process.env(终端变量)、
* dotenv 文件或 quasar.config > build > define/defineEnv。
*
* 使用代码中转换后的完整变量名。
* @example 'all' 忽略所有变量
* @example ['import.meta.env.OTHER_VAR', '__SOME_VAR__'] 忽略特定变量。
*/
ignoreType?: "all" | string[];
}
}记住你可以使用 build > env > filter 函数进一步过滤不需要的键,甚至修改键的值:
build: {
env: {
filter (
originalEnv,
type // "client" | "backend"
) {
const newEnv = {}
for (const key in originalEnv) {
if (/* ...决定是否包含... */) {
newEnv[ key ] = originalEnv[ key ]
}
}
// 记得返回处理后的 env
return newEnv
}
}
}/quasar.config 文件本身的 Dotenv 文件
Quasar CLI 会检测 dotenv 文件并将其用于 /quasar.config 文件本身。但是,如果你想配置此行为,需要编辑你的 /package.json:
"quasarCli": {
"quasarConfEnv": {
/**
* 设置此前缀将过滤暴露的 env 文件变量和
* Node.js process.env 变量。
*
* 避免将其设置为 'QUASAR_',以免与 Quasar 自身的环境变量冲突。
*
* @default ''
*/
"prefix": "",
/**
* Quasar CLI 查找 .env* 文件的目录。
* 可以是绝对路径或相对于项目根目录的相对路径。
*
* @default 项目根目录
*/
"folder": "."
/**
* 要加载的额外 .env* 文件。
* 每个条目可以是绝对路径或相对于
* quasar.config > build > folder 的相对路径。
*
* @example ['.env.somefile', '../.env.someotherfile']
*/
"file": []
}
}在 /quasar.config 文件中的用法,假设你在 /.env 中定义了 MY_BOOL=true:
import { defineConfig } from '#q-app'
export default defineConfig(ctx => {
return {
build: {
filenameBasedRouting: import.meta.env.MY_BOOL // 已自动推断为 boolean 类型
}
}
})HMR(热模块重载)
添加/删除/修改 dotenv 文件(默认文件和配置的文件)或 /quasar.config 的 build.define/defineEnv 会立即生效,因此你不需要重启 Quasar CLI 开发服务器。