Skip to content

フレームワークの環境API

Experimental

環境APIは実験的です。 Vite 6の間、APIを安定させて、生態系を実験し、その上に構築します。 Vite 7の潜在的な破壊変化を伴うこれらの新しいAPIを安定させることを計画しています。

リソース:

フィードバックを私たちと共有してください。

環境とフレームワーク

暗黙のssr環境およびその他の非クライアント環境は、DEV中にデフォルトでRunnableDevEnvironment使用します。これにはランタイムがViteサーバーが実行されているものと同じである必要がありますが、これは同様にssrLoadModuleで動作し、SSR DevストーリーのHMRを移行および有効にすることができます。 isRunnableDevEnvironment機能で実行可能な環境をガードできます。

ts
export class RunnableDevEnvironment extends DevEnvironment {
  public readonly runner: ModuleRunner
}

class ModuleRunner {
  /**
   * 実行するURL。
   * ルートに対するファイルパス、サーバーパス、またはIDを受け入れます。
   * インスタンス化されたモジュールを返します(SSRLoadModuleと同じ)
   */
  public async import(url: string): Promise<Record<string, any>>
  /**
   * その他のモジュールランナーメソッド...
   */
}

if (isRunnableDevEnvironment(server.environments.ssr)) {
  await server.environments.ssr.runner.import('/entry-point.js')
}

WARNING

runner 、初めてアクセスすると熱心に評価されます。 Viteは、 runnerを呼び出してprocess.setSourceMapsEnabled作成した場合、または使用できない場合はError.prepareStackTraceオーバーライドすることにより、ソースマップサポートを有効にすることに注意してください。

デフォルトRunnableDevEnvironment

SSRセットアップガイドで説明されているミドルウェアモードで構成されたVITEサーバーが与えられた場合、環境APIを使用してSSRミドルウェアを実装しましょう。エラー処理は省略されています。

js
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { createServer } from 'vite'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

const server = await createServer({
  server: { middlewareMode: true },
  appType: 'custom',
  environments: {
    server: {
      // デフォルトでは、モジュールはViteサーバーと同じプロセスで実行されます
    },
  },
})

// TypeScriptまたは
// IsRunNableVenVenvironmentを使用して、ランナーへのアクセスを保護します
const environment = server.environments.node

app.use('*', async (req, res, next) => {
  const url = req.originalUrl

  // 1. index.htmlを読み取ります
  const indexHtmlPath = path.resolve(__dirname, 'index.html')
  let template = fs.readFileSync(indexHtmlPath, 'utf-8')

  // 2. Vite HTML変換を適用します。これにより、Vite HMRクライアントが注入されます。
  //    また、ViteプラグインからのHTML変換も適用します。グローバル
  //    @vitejs/プラグイン反応からのプリアンブル
  template = await server.transformIndexHtml(url, template)

  // 3.サーバーエントリをロードします。インポート(URL)は自動的に変換されます
  //    node.jsで使用できるESMソースコード!バンドルはありません
  //    必須であり、完全なHMRサポートを提供します。
  const { render } = await environment.runner.import('/src/entry-server.js')

  // 4.アプリHTMLをレンダリングします。これは、Entry-Server.jsのエクスポートを想定しています
  //     `render`関数は適切なフレームワークSSR APIを呼び出します、
  //    例えばReactdomserver.rendertostring()
  const appHtml = await render(url)

  // 5.アプリレンダリングされたHTMLをテンプレートに挿入します。
  const html = template.replace(`<!--ssr-outlet-->`, appHtml)

  // 6.レンダリングされたHTMLを送信します。
  res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
})

ランタイムアグノーシスSSR

RunnableDevEnvironment Viteサーバーと同じ実行時にコードを実行するためにのみ使用できるため、Viteサーバー(node.jsと互換性のあるランタイム)を実行できるランタイムが必要です。これは、RAW DevEnvironment使用してランタイム不可知論者にする必要があることを意味します。

FetchableDevEnvironment proposal

最初の提案には、 DevEnvironmentクラスにrunメソッドがあり、消費者はtransportオプションを使用してランナー側にインポートを呼び出すことができます。テスト中、APIが推奨を開始するほど普遍的ではないことがわかりました。現時点では、 FetchableDevEnvironment提案に関するフィードバックを探しています。

RunnableDevEnvironmentは、モジュールの値を返すrunner.import関数があります。ただし、この関数はRAW DevEnvironmentでは使用できず、ViteのAPIとユーザーモジュールを使用してコードを分離する必要があります。

たとえば、次の例では、ViteのAPIを使用してコードからユーザーモジュールの値を使用します。

ts
// ViteのAPIを使用したコード
import { createServer } from 'vite'

const server = createServer()
const ssrEnvironment = server.environment.ssr
const input = {}

const { createHandler } = await ssrEnvironment.runner.import('./entry.js')
const handler = createHandler(input)
const response = handler(new Request('/'))

// ----------------------------------------------
// ./entrypoint.js
export function createHandler(input) {
  return function handler(req) {
    return new Response('hello')
  }
}

コードがユーザーモジュールと同じランタイムで実行できる場合(つまり、node.js固有のAPIに依存しない)、仮想モジュールを使用できます。このアプローチは、ViteのAPIを使用してコードから値にアクセスする必要性を排除します。

ts
// ViteのAPIを使用したコード
import { createServer } from 'vite'

const server = createServer({
  plugins: [
    // `virtual:entrypoint`を処理するプラグイン
    {
      name: 'virtual-module',
      /* プラグインの実装 */
    },
  ],
})
const ssrEnvironment = server.environment.ssr
const input = {}

// コードを実行する各環境工場による露出機能を使用する
// それらが提供するものごとに各環境工場を確認してください
if (ssrEnvironment instanceof RunnableDevEnvironment) {
  ssrEnvironment.runner.import('virtual:entrypoint')
} else if (ssrEnvironment instanceof CustomDevEnvironment) {
  ssrEnvironment.runEntrypoint('virtual:entrypoint')
} else {
  throw new Error(`Unsupported runtime for ${ssrEnvironment.name}`)
}

// ----------------------------------------------
// 仮想:エントリポイント
const { createHandler } = await import('./entrypoint.js')
const handler = createHandler(input)
const response = handler(new Request('/'))

// ----------------------------------------------
// ./entrypoint.js
export function createHandler(input) {
  return function handler(req) {
    return new Response('hello')
  }
}

たとえば、ユーザーモジュールでtransformIndexHtml呼び出すには、次のプラグインを使用できます。

ts
function vitePluginVirtualIndexHtml(): Plugin {
  let server: ViteDevServer | undefined
  return {
    name: vitePluginVirtualIndexHtml.name,
    configureServer(server_) {
      server = server_
    },
    resolveId(source) {
      return source === 'virtual:index-html' ? '\0' + source : undefined
    },
    async load(id) {
      if (id === '\0' + 'virtual:index-html') {
        let html: string
        if (server) {
          this.addWatchFile('index.html')
          html = fs.readFileSync('index.html', 'utf-8')
          html = await server.transformIndexHtml('/', html)
        } else {
          html = fs.readFileSync('dist/client/index.html', 'utf-8')
        }
        return `export default ${JSON.stringify(html)}`
      }
      return
    },
  }
}

コードにnode.js APIが必要な場合、 hot.send使用して、ユーザーモジュールのViteのAPIを使用するコードと通信できます。ただし、このアプローチはビルドプロセス後も同じように機能しない場合があることに注意してください。

ts
// ViteのAPIを使用したコード
import { createServer } from 'vite'

const server = createServer({
  plugins: [
    // `virtual:entrypoint`を処理するプラグイン
    {
      name: 'virtual-module',
      /* プラグインの実装 */
    },
  ],
})
const ssrEnvironment = server.environment.ssr
const input = {}

// コードを実行する各環境工場による露出機能を使用する
// それらが提供するものごとに各環境工場を確認してください
if (ssrEnvironment instanceof RunnableDevEnvironment) {
  ssrEnvironment.runner.import('virtual:entrypoint')
} else if (ssrEnvironment instanceof CustomDevEnvironment) {
  ssrEnvironment.runEntrypoint('virtual:entrypoint')
} else {
  throw new Error(`Unsupported runtime for ${ssrEnvironment.name}`)
}

const req = new Request('/')

const uniqueId = 'a-unique-id'
ssrEnvironment.send('request', serialize({ req, uniqueId }))
const response = await new Promise((resolve) => {
  ssrEnvironment.on('response', (data) => {
    data = deserialize(data)
    if (data.uniqueId === uniqueId) {
      resolve(data.res)
    }
  })
})

// ----------------------------------------------
// 仮想:エントリポイント
const { createHandler } = await import('./entrypoint.js')
const handler = createHandler(input)

import.meta.hot.on('request', (data) => {
  const { req, uniqueId } = deserialize(data)
  const res = handler(req)
  import.meta.hot.send('response', serialize({ res: res, uniqueId }))
})

const response = handler(new Request('/'))

// ----------------------------------------------
// ./entrypoint.js
export function createHandler(input) {
  return function handler(req) {
    return new Response('hello')
  }
}

ビルド中の環境

CLIでは、 vite buildvite build --ssr呼び出すと、クライアントのみが構築され、SSRのみの環境が後方互換性のために構築されます。

builderundefinedでない場合(またはvite build --app呼び出すとき)、 vite build代わりにアプリ全体の構築にオプトインします。これは後に将来の専攻のデフォルトになります。 ViteBuilderインスタンスが作成され( ViteDevServerに相当するビルドタイム)、生産用に構成されたすべての環境を構築します。デフォルトでは、環境のビルドは、 environmentsレコードの順序を考慮して直列に実行されます。フレームワークまたはユーザーは、以下を使用して環境の構築方法をさらに構成できます。

js
export default {
  builder: {
    buildApp: async (builder) => {
      const environments = Object.values(builder.environments)
      return Promise.all(
        environments.map((environment) => builder.build(environment)),
      )
    },
  },
}

環境不可知論コード

ほとんどの場合、現在のenvironmentインスタンスは、コードが実行されているコンテキストの一部として利用可能であるため、 server.environmentsからアクセスする必要があるはずです。たとえば、内部プラグインフックはPluginContextの一部として環境が露出しているため、 this.environment使用してアクセスできます。プラグインの環境APIを参照して、環境認識プラグインを構築する方法について学びます。

Released under the MIT License. (dev)