ホームページ  >  記事  >  ウェブフロントエンド  >  vite が .env ファイルを解析する方法についての詳細な説明

vite が .env ファイルを解析する方法についての詳細な説明

青灯夜游
青灯夜游転載
2023-01-24 05:30:023093ブラウズ

vite が .env ファイルを解析する方法についての詳細な説明

vue フレームワークを使用してフロントエンド プロジェクトを開発する場合、デプロイ時に複数の環境をデプロイすることになりますが、開発環境、テスト環境、オンライン環境で呼び出されるインターフェースのドメイン名が異なることがよくあります。どうすれば区別できるでしょうか?それは環境変数とパターンを使用することです。

vite でビルドされた vue3 プロジェクトでは、ルート ディレクトリに .env.[mode] ファイルを作成して 1 つ以上のモードを定義できます。このファイルで定義されている変数は、このモードの環境変数です。 。環境変数を定義した後、import.meta.env.[変数名] を通じて環境変数を読み取ることができます。 [関連する推奨事項: vuejs ビデオ チュートリアル Web フロントエンド開発 ]

次に、2 つの質問について考えなければなりません。まず、vite はどのようにして .env を読み取るのかということです。ファイル? 定義された構成、次に、vite が .env ファイルで構成された環境変数を import.meta.env 環境変数にマウントする方法について説明します。今日のビデオで関連するソース コードを検討することでそれを理解できます。ようこそ、この記事を読んで学んでください。間違いがある場合は、お気軽に修正してください。

1. .env ファイルで定義された設定を読み取る方法

1.1 問題の焦点は何ですか?

まず、vite が .env ファイルで定義された構成をどのように読み取るかを見てみましょう。下図に示すように、プロジェクトのルート ディレクトリには、.env.development、.env.fat、.env.uat、.env.pro の 4 つのモード ファイルがあり、開発モードはローカル開発のデフォルトの開発環境に対応します。 、ファット モードはセルフテスト用の開発環境に対応し、uat モードはテスト チーム テスト用のプレリリース環境に対応し、プロ モードは顧客使用のためのオンライン環境とも呼ばれる運用環境に対応します。

では、関連モードでファイルを使用したいことを vite に知らせるにはどうすればよいでしょうか? vite または vite build コマンドを実行するときは、次の図に示すように、--mode または -m を使用して環境モードを設定できます (詳細についてはドキュメントを参照)。

## これは、vite が環境変数を定義するモード ファイルをどのように読み取るかを理解したい場合は、vite コマンドまたは vite build コマンドから始める必要があることを思い出させます。指示。

1.2 vite のコマンドはどこで定義されていますか?

vite の package.json ファイル (パス: vite/packages/vite/package.json) を確認してください:

いつ いつvite コマンドを使用して、bin ディレクトリにある vite.js ファイルを実行します。このファイルを見てください (パス: vite/packages/vite/bin/vite.js):

このコードの鍵は start メソッドを実行することであり、start メソッドはパッケージ化された cli.js ファイルをインポートすることがわかります。では、このパッケージ化されたファイルに対応する元のファイルはどれでしょうか? Vite はパッケージ化するときにロールアップを使用するので、ロールアップ構成ファイルを見てみましょう (パス: vite/packages/vite/rollup.config.ts):

上記のコードvite 関連のコマンドの定義がファイル /src/node/cli.ts にあることがわかります。

1.3 vite コマンドはどのように定義されますか?

1.3.1 Vite は cac を使用してコマンドを定義します

vite のコマンドがどのように定義されているかを見てみましょう (パス: vite/packages/) vite/src/node/cli.ts):
import { cac } from 'cac'

const cli = cac('vite')
cli
  .option(&#39;-m, --mode <mode>&#39;, `[string] set env mode`)
cli
  .command(&#39;[root]&#39;, &#39;start dev server&#39;) // default command
  .alias(&#39;serve&#39;) // the command is called &#39;serve&#39; in Vite&#39;s API
  .option(&#39;--port <port>&#39;, `[number] specify port`)
  .action(async (root: string, options: ServerOptions & GlobalCLIOptions) => {
    const { createServer } = await import(&#39;./server&#39;)
    try {
      const server = await createServer({
        root,
        base: options.base,
        mode: options.mode,
        configFile: options.config,
        logLevel: options.logLevel,
        clearScreen: options.clearScreen,
        optimizeDeps: { force: options.force },
        server: cleanOptions(options),
      })
  })

上記のコードに示すように、vite は主に

cac

コマンド ライン ツール ライブラリを使用してコマンドを定義します。ここで使用される cac について説明してください関連 API:

cac(name?): cac インスタンスの作成に使用され、name パラメーターはオプションです。
  • option(name, description, config): 構成項目を設定するために使用されます。
  • command(name, description, config?): cli コマンドを宣言します。コマンドに対して独立した設定項目とその実行アクションを設定できます。
  • command.alias(name): cli コマンドにエイリアスを与えます。
  • action(callback): コマンドによって実行されるアクションを指定します。

可以看出,当运行vite命令的时候会执行createServer方法,我们这里要注意参数mode就是我们运行命令时通过--mode 或者 -m指定的参数,下面来研究createServer方法。

1.3.2 createServer

看一下createServer方法(路径:createServervite/packages/vite/src/node/server/index.ts):

import { resolveConfig } from &#39;../config&#39;
export async function createServer(
  inlineConfig: InlineConfig = {},
): Promise<ViteDevServer> {
  const config = await resolveConfig(inlineConfig, &#39;serve&#39;)
}

可以看到createServer方法调用的是resolveConfig方法,下面看一下resolveConfig方法。

1.3.3 resolveConfig

resolveConfig方法的代码如下(路径;vite/packages/vite/src/node/config.ts):

import { loadEnv, resolveEnvPrefix } from &#39;./env&#39;
export async function resolveConfig(
  inlineConfig: InlineConfig,
  command: &#39;build&#39; | &#39;serve&#39;,
  defaultMode = &#39;development&#39;,
  defaultNodeEnv = &#39;development&#39;,
): Promise<ResolvedConfig> {
  const envDir = config.envDir
    ? normalizePath(path.resolve(resolvedRoot, config.envDir))
    : resolvedRoot
  const userEnv =
    inlineConfig.envFile !== false &&
    loadEnv(mode, envDir, resolveEnvPrefix(config))
  const resolvedConfig: ResolvedConfig = {
    command,
    mode,
    env: {
      ...userEnv,
      BASE_URL,
      MODE: mode,
      DEV: !isProduction,
      PROD: isProduction,
    },
  }
  const resolved: ResolvedConfig = {
    ...config,
    ...resolvedConfig,
  }
  return resolved
}

可以看到resolveConfig的主要工作:

  • 首先确定.env文件的路径

  • 然后调用loadEnv方法加载解析.env文件,将结果赋值给userEnv

  • 最后返回整个解析后的配置

我们看到这里的关键代码是loadEnv(mode, envDir, resolveEnvPrefix(config))下面我就重点看一下loadEnv方法。

1.3.4 loadEnv

loadEnv方法是vite中一个比较核心的方法,也作为vite对外提供的一个JavaScript API,用于加载 envDir 中的 .env 文件。

我们看一下loadEnv方法(路径:vite/packages/vite/src/node/env.ts):

import { parse } from &#39;dotenv&#39;
import { arraify, lookupFile } from &#39;./utils&#39;

export function loadEnv(
  mode: string,
  envDir: string,
  prefixes: string | string[] = &#39;VITE_&#39;,
): Record<string, string> {
  prefixes = arraify(prefixes)
  const env: Record<string, string> = {}
  const envFiles = [
    /** default file */ `.env`,
    /** local file */ `.env.local`,
    /** mode file */ `.env.${mode}`,
    /** mode local file */ `.env.${mode}.local`,
  ]

  const parsed = Object.fromEntries(
    envFiles.flatMap((file) => {
      const path = lookupFile(envDir, [file], {
        pathOnly: true,
        rootDir: envDir,
      })
      if (!path) return []
      return Object.entries(parse(fs.readFileSync(path)))
    }),
  )
  // only keys that start with prefix are exposed to client
  for (const [key, value] of Object.entries(parsed)) {
    if (prefixes.some((prefix) => key.startsWith(prefix))) {
      env[key] = value
    } else if (
      key === &#39;NODE_ENV&#39; &&
      process.env.VITE_USER_NODE_ENV === undefined
    ) {
      // NODE_ENV override in .env file
      process.env.VITE_USER_NODE_ENV = value
    }
  }

  return env
}

如上代码所示理解loadEnv方法注意以下几个方面:

  • 该方法接收三个参数,分别是模式、.env文件的路径还有环境变量的前缀。

  • 使用递归方法lookupFile找到.env文件的路径,使用fs.readFileSync读取文件。

  • 使用dotenv提供的方法解析.env文件内容。

关于dotenv可以学习川哥的文章,也可以看看笔者的源码共读语雀笔记。至此,我们了解了vite是如何读取.env文件中定义的环境变量了。下面我们研究第二个问题vite如何将.env中配置的环境变量挂载到import.meta.env环境变量上。

2.如何将变量挂载到import.meta.env环境变量上

2.1 vite的环境变量和import.meta

Vite 在一个特殊的 import.meta.env 对象上暴露环境变量,有一些在所有情况下都可以使用的内建变量:

import.meta.env.MODE: {string} 应用运行的模式。

import.meta.env.BASE_URL: {string} 部署应用时的基本 URL。他由base 配置项决定。

import.meta.env.PROD: {boolean} 应用是否运行在生产环境。

import.meta.env.DEV: {boolean} 应用是否运行在开发环境 (永远与 import.meta.env.PROD相反)。

import.meta.env.SSR: {boolean} 应用是否运行在 server 上。

详见环境变量。这里我们要解释一下import.meta。它是一个给JavaScript模块暴露特定上下文的元数据属性的对象。它包含了这个模块的信息,比如说这个模块的URL。详见import.meta 的MDN文档。需要注意不可以在模块的外部使用import.meta,如下图所示:

2.2 resolveConfig

在上文中我们已经研究了resolveConfig的代码,我们再来看以下此方法中的另一段代码(路径:vite/packages/vite/src/node/config.ts):

import {resolvePlugins,} from &#39;./plugins&#39;

export async function resolveConfig(
  inlineConfig: InlineConfig,
  command: &#39;build&#39; | &#39;serve&#39;,
  defaultMode = &#39;development&#39;,
  defaultNodeEnv = &#39;development&#39;,
): Promise<ResolvedConfig> {
  (resolved.plugins as Plugin[]) = await resolvePlugins(
    resolved,
    prePlugins,
    normalPlugins,
    postPlugins,
  )
}

这里调用了resolvePlugins,接收resolved对象,此对象中含有开发者所指定的模式以及.env文件中的环境变量。我们接着看一下resolvePlugins方法。

2.3 resolvePlugins

节选resolvePlugins方法如下(路径:vite/packages/vite/src/node/plugins/index.ts):

import { definePlugin } from &#39;./define&#39;

export async function resolvePlugins(
  config: ResolvedConfig,
  prePlugins: Plugin[],
  normalPlugins: Plugin[],
  postPlugins: Plugin[],
): Promise<Plugin[]> {
  return [
    //...
    definePlugin(config),
    //...
  ].filter(Boolean) as Plugin[]
}

resolvePlugins负责解析插件,这里面调用了definePlugin方法,我们看一下。

2.4 definePlugin

definePlugin的代码如下(路径:vite/packages/vite/src/node/plugins/define.ts):

const importMetaKeys: Record<string, string> = {}
const importMetaFallbackKeys: Record<string, string> = {}
if (isBuild) {
  const env: Record<string, any> = {
    ...config.env,
    SSR: !!config.build.ssr,
  }
  for (const key in env) {
    importMetaKeys[`import.meta.env.${key}`] = JSON.stringify(env[key])
  }
  Object.assign(importMetaFallbackKeys, {
    &#39;import.meta.env.&#39;: `({}).`,
    &#39;import.meta.env&#39;: JSON.stringify(config.env),
    &#39;import.meta.hot&#39;: `false`,
  })
}

这段代码的关键部分在于第8-10行的for循环,将.env文件中定义的环境变量挂在到了import.meta.env上。至此,如何也了解了vite是如何将环境变量挂在到import.meta.env环境变量上。

总结

通过阅读vite的源码,我们了解到vite处理.env文件时涉及到的两个关键问题:第一,vite如何读取.env文件中定义的配置;第二,vite如何将.env文件中配置的环境变量挂载到import.meta.env环境变量上。

对于第一问题,我们了解到vite使用cac定义命令,当执行vite命令并通过--mode或者-m选项指定模式的时候,vite会拿到mode, 然后vite会去项目目录下查找对应.env.[模式]的文件并读取其内容,然后通过dotenv的parse方法解析文件内容,将定义的环境变量整合到resolved中。

对于第二个问题,我们了解到vite的resolveConfig方法中会执行插件解析的方法resolvePlugins,而此方法又会调用definePlugin方法,在definePlugin方法中会完成将.env文件中定义的变量挂载到import.meta.env环境变量上。

(学习视频分享:vuejs入门教程编程基础视频

以上がvite が .env ファイルを解析する方法についての詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。