为什么捐赠
API 浏览器
升级指南
创建新项目
quasar.config 配置文件
从 Webpack 项目转换
浏览器兼容性
TypeScript 支持
目录结构
命令列表
CSS 预处理器
使用 VueRouter 进行页面路由
懒加载 - 代码分割
资源处理
Boot 文件
预取特性
API 代理
配置 Vite
处理 import.meta.env
使用 Pinia 管理状态
代码检查与格式化
测试与审计
开发移动应用
Ajax 请求
开放开发服务器到公网
联系站长
Quasar CLI with Vite - @quasar/app-vite
import.meta.env 与 dotenv 文件支持

使用 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_DEVBoolean代码运行在开发模式
QUASAR_PRODBoolean代码运行在生产模式
QUASAR_DEBUGBoolean代码运行在开发模式,或生产模式下设置了 --debug 标志
QUASAR_CLIENTBoolean代码运行在客户端(非服务端)
QUASAR_SERVERBoolean代码运行在服务端(非客户端)
QUASAR_MODEStringQuasar CLI 模式(spapwa 等)
QUASAR_<MODE>_MODEBoolean代码运行在 <MODE> Quasar 模式下。例如:QUASAR_ELECTRON_MODE
QUASAR_TARGETStringCordova/Capacitor 模式下为 iosandroid,BEX 模式下为 chromefirefox

用法

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 文件添加自定义定义:

/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>
  >;
}
/quasar.config 示例

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.definebuild.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 文件

// 然后在 /quasar.config 文件中获取它
build: {
  defineEnv: {
    API: ctx.dev
      ? 'https://dev.' + import.meta.env.MY_API
      : 'https://prod.' + import.meta.env.MY_API
  }
}

从 dotenv 文件

/.env

# 不会暴露给客户端代码,但会暴露给后端;
# 因为它不以 QCLI_ 前缀开头:
SOME_VAR=content

# 这会嵌入到后端和客户端代码中,
# 因为它以 QCLI_ 前缀开头:
QCLI_SOME_VAR=content
使用示例

import.meta.env.SOME_VAR // ❌ 在客户端代码中不可用
import.meta.env.QCLI_SOME_VAR // ✅ 在客户端代码中可用

你的 dotenv 文件可以包含引用(跨 dotenv 文件,但顺序很重要):

/.env

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 是从多个输入源构建的。你可能会遇到覆盖定义的情况。为了明确,以下是考虑的顺序:

  1. Dotenv 文件
  2. 终端变量
  3. /quasar.config > build > define
  4. /quasar.config > build > defineEnv

类型推断

Quasar CLI 会自动推断并维护 process.env(终端变量)、dotenv 文件、/quasar.config > define 和 defineEnv 的类型。

示例值推断类型替换为
truebooleantrue
42number42
nullnullnull
hellostring"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 dev
在你的代码中

console.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 定义,因此不会注入它。

对于上述情况,你可以自行定义:

示例:/env.d.ts

/**
 * 为你的自定义变量添加类型(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 格式。等号周围没有空格,通常只有在值包含空格时才需要引号。

/.env

# 数据库配置
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=content
使用示例

import.meta.env.SOME_VAR // ❌ 在客户端代码中不可用
import.meta.env.QCLI_SOME_VAR // ✅ 在客户端代码中可用

Dotenv 的黄金法则

永远不要将你的 .env 文件提交到版本控制中。

你必须立即将 .env 添加到项目的 .gitignore 文件中。如果你不小心将包含真实 API 密钥的 .env 文件推送到公共仓库,机器人可以在几秒钟内抓取这些密钥并利用它们。

相反,你可能想在项目目录中包含一个 .env.example.env.template 文件。这个文件包含键名但留空值或填入虚拟数据,向其他开发者展示项目运行需要哪些变量:

/.env.template

DB_HOST=
DB_USER=
DB_PASSWORD=
DB_PORT=
STRIPE_API_KEY=
NODE_ENV=

配置 Dotenv 文件

/quasar.config 文件

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 函数进一步过滤不需要的键,甚至修改键的值:

/quasar.config 文件

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:

/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

/quasar.config 文件示例

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 开发服务器。