ホームページ ウェブフロントエンド jsチュートリアル Node.js と TypeScript を使用して Nginx のようなスケーラブルなリバース プロキシ サーバーを構築する

Node.js と TypeScript を使用して Nginx のようなスケーラブルなリバース プロキシ サーバーを構築する

Jan 05, 2025 am 08:48 AM

インスピレーション

今日のマイクロサービス アーキテクチャでは、リバース プロキシは、受信リクエストを管理し、さまざまなバックエンド サービスにルーティングする上で重要な役割を果たしています。

Building a Scalable Reverse Proxy Server like Nginx with Node.js and TypeScript

リバース プロキシは、アプリケーションの Web サーバーの前に配置され、クライアント マシンからのリクエストをインターセプトします。これには、負荷分散、セキュリティの向上につながるオリジンサーバーの IP アドレスの隠蔽、キャッシュ、レート制限など、多くの利点があります。

分散型マイクロサービス アーキテクチャでは、単一のエントリ ポイントが必要です。 Nginx などのリバース プロキシ サーバーは、このようなシナリオに役立ちます。サーバーの複数のインスタンスを実行している場合、効率的なリクエスト ルーティングの管理と確保は困難になります。この場合、Nginx のようなリバース プロキシが完璧なソリューションです。ドメインを Nginx サーバーの IP アドレスに指定すると、Nginx は、各インスタンスで処理される負荷を処理しながら、構成に従って受信リクエストをいずれかのインスタンスにルーティングします。

Nginx はどのようにして優れているのでしょうか?

Nginx がどのようにして非常に高い信頼性と速度で大規模なリクエストをサポートできるかを詳しく説明した Nginx の記事「Nginx アーキテクチャ」を一読することをお勧めします。

つまり、Nginx にはマスター プロセスと多数のワーカー プロセスがあります。キャッシュ ローダーやキャッシュ マネージャーなどのヘルパー プロセスもあります。マスタープロセスとワーカープロセスはすべての重い作業を実行します。

  • マスター プロセス: 構成を管理し、子プロセスを生成します。
  • キャッシュ ローダー/マネージャー: 最小限のリソースでキャッシュのロードとプルーニングを処理します。
  • ワーカー プロセス: 接続、ディスク I/O、およびアップストリーム通信を管理し、ノンブロッキングかつ独立して実行します。

ワーカー プロセスは複数の接続をノンブロッキングで処理し、コンテキストの切り替えを減らします。これらはシングルスレッドで独立して実行され、キャッシュやセッション データなどの共有リソースに共有メモリを使用します。このアーキテクチャは、Nginx がコンテキスト スイッチの数を減らし、ブロッキング マルチプロセス アーキテクチャよりも高速に速度を上げるのに役立ちます。

これからインスピレーションを得て、マスター プロセスとワーカー プロセスの同じ概念を使用し、ワーカー プロセスごとに数千の接続を処理できる独自のイベント駆動型リバース プロキシ サーバーを実装します。

プロジェクトのアーキテクチャ

リバース プロキシの実装は、次の重要な設計原則に従っています。

  1. 構成主導: すべてのプロキシの動作は YAML 構成ファイルで定義されるため、ルーティング ルールを簡単に変更できます。
  2. 型安全性: TypeScript と Zod スキーマにより、構成の有効性と実行時の型安全性が保証されます。
  3. スケーラビリティ: Node.js クラスター モジュールにより、複数の CPU コアを利用してパフォーマンスを向上させることができます。
  4. モジュール性: 構成、サーバー ロジック、スキーマ検証のための個別のモジュールによる懸念の明確な分離。

プロジェクトの構造

├── config.yaml           # Server configuration
├── src/
│   ├── config-schema.ts  # Configuration validation schemas
│   ├── config.ts         # Configuration parsing logic
│   ├── index.ts         # Application entry point
│   ├── server-schema.ts # Server message schemas
│   └── server.ts        # Core server implementation
└── tsconfig.json        # TypeScript configuration
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

主要コンポーネント

  1. config.yaml: ポート、ワーカー プロセス、アップストリーム サーバー、ヘッダー、ルーティング ルールなどのサーバーの構成を定義します。
  2. config-schema.ts: Zod ライブラリを使用して検証スキーマを定義し、構成構造が正しいことを確認します。
  3. server-schema.ts: マスター プロセスとワーカー プロセス間で交換されるメッセージ形式を指定します。
  4. config.ts: YAML 構成ファイルを解析および検証するための関数を提供します。
  5. server.ts: クラスターのセットアップ、HTTP 処理、リクエスト転送などのリバース プロキシ サーバー ロジックを実装します。
  6. index.ts: エントリ ポイントとして機能し、コマンドライン オプションを解析してサーバーを開始します。

構成管理

設定システムは YAML を使用します。仕組みは次のとおりです:

server:
    listen: 8080          # Port the server listens on.
    workers: 2            # Number of worker processes to handle requests.
    upstreams:            # Define upstream servers (backend targets).
        - id: jsonplaceholder
          url: jsonplaceholder.typicode.com
        - id: dummy
          url: dummyjson.com
    headers:              # Custom headers added to proxied requests.
        - key: x-forward-for
          value: $ip      # Adds the client IP to the forwarded request.
        - key: Authorization
          value: Bearer xyz  # Adds an authorization token to requests.
    rules:                # Define routing rules for incoming requests.
        - path: /test
          upstreams:
              - dummy     # Routes requests to "/test" to the "dummy" upstream.
        - path: /
          upstreams:
              - jsonplaceholder  # Routes all other requests to "jsonplaceholder".

ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

受信リクエストはルールに照らして評価されます。リバース プロキシは、パスに基づいて、リクエストを転送する上流サーバーを決定します。

構成の検証 (config-schema.ts)

Zod を使用して、構成検証のための厳密なスキーマを定義します。

import { z } from "zod";

const upstreamSchema = z.object({
    id: z.string(),
    url: z.string(),
});

const headerSchema = z.object({
    key: z.string(),
    value: z.string(),
});

const ruleSchema = z.object({
    path: z.string(),
    upstreams: z.array(z.string()),
});

const serverSchema = z.object({
    listen: z.number(),
    workers: z.number().optional(),
    upstreams: z.array(upstreamSchema),
    headers: z.array(headerSchema).optional(),
    rules: z.array(ruleSchema),
});

export const rootConfigSchema = z.object({
    server: serverSchema,
});

export type ConfigSchemaType = z.infer<typeof rootConfigSchema>;
ログイン後にコピー
ログイン後にコピー

構成の解析と検証 (config.ts)

config.ts モジュールは、構成ファイルを解析および検証するためのユーティリティ関数を提供します。

import fs from "node:fs/promises";
import { parse } from "yaml";
import { rootConfigSchema } from "./config-schema";

export async function parseYAMLConfig(filepath: string) {
    const configFileContent = await fs.readFile(filepath, "utf8");
    const configParsed = parse(configFileContent);
    return JSON.stringify(configParsed);
}

export async function validateConfig(config: string) {
    const validatedConfig = await rootConfigSchema.parseAsync(
        JSON.parse(config)
    );
    return validatedConfig;
}
ログイン後にコピー
ログイン後にコピー

リバース プロキシ サーバー ロジック (server.ts)

サーバーは、スケーラビリティのために Node.js クラスター モジュールを利用し、リクエストの処理のために http モジュールを利用します。マスター プロセスはリクエストをワーカー プロセスに分散し、ワーカー プロセスがリクエストを上流のサーバーに転送します。 server.ts ファイルを詳しく調べてみましょう。このファイルには、リバース プロキシ サーバーのコア ロジックが含まれています。各コンポーネントを分析し、それらがどのように連携してスケーラブルなプロキシ サーバーを作成するかを理解します。

サーバー実装は、Node.js のクラスター モジュールを使用したマスターワーカー アーキテクチャに従います。この設計により、次のことが可能になります。

  • 複数の CPU コアを利用する
  • リクエストを同時に処理します
  • 高可用性を維持する
  • リクエスト処理を分離する
  1. マスタープロセス:

    • ワーカープロセスを作成します
    • 受信したリクエストをワーカー全体に分散します
    • ワーカープールを管理します
    • ワーカーのクラッシュと再起動を処理します
  2. ワーカープロセス:

    • 個々の HTTP リクエストを処理する
    • リクエストをルーティング ルールと照合します
    • リクエストを上流サーバーに転送します
    • 応答を処理してクライアントに送り返す

マスタープロセスのセットアップ

├── config.yaml           # Server configuration
├── src/
│   ├── config-schema.ts  # Configuration validation schemas
│   ├── config.ts         # Configuration parsing logic
│   ├── index.ts         # Application entry point
│   ├── server-schema.ts # Server message schemas
│   └── server.ts        # Core server implementation
└── tsconfig.json        # TypeScript configuration
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

マスター プロセスはワーカーのプールを作成し、環境変数を通じて各ワーカーに設定を渡します。これにより、すべてのワーカーが同じ構成にアクセスできるようになります。

リクエスト配布

server:
    listen: 8080          # Port the server listens on.
    workers: 2            # Number of worker processes to handle requests.
    upstreams:            # Define upstream servers (backend targets).
        - id: jsonplaceholder
          url: jsonplaceholder.typicode.com
        - id: dummy
          url: dummyjson.com
    headers:              # Custom headers added to proxied requests.
        - key: x-forward-for
          value: $ip      # Adds the client IP to the forwarded request.
        - key: Authorization
          value: Bearer xyz  # Adds an authorization token to requests.
    rules:                # Define routing rules for incoming requests.
        - path: /test
          upstreams:
              - dummy     # Routes requests to "/test" to the "dummy" upstream.
        - path: /
          upstreams:
              - jsonplaceholder  # Routes all other requests to "jsonplaceholder".

ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

マスター プロセスは、単純なランダム分散戦略を使用してリクエストをワーカーに割り当てます。このアプローチは、ラウンドロビン アルゴリズムや最小接続アルゴリズムほど洗練されていませんが、ほとんどのユース ケースで適切な負荷分散を提供します。リクエスト分散ロジック:

  • プールからワーカーをランダムに選択します
  • ワーカー間でバランスの取れたワークロードを作成します
  • 従業員が不在の可能性がある特殊なケースに対処します

ワーカープロセスリクエストロジック

各ワーカーはメッセージをリッスンし、リクエストをルーティング ルールと照合して、適切な上流サーバーに転送します。

import { z } from "zod";

const upstreamSchema = z.object({
    id: z.string(),
    url: z.string(),
});

const headerSchema = z.object({
    key: z.string(),
    value: z.string(),
});

const ruleSchema = z.object({
    path: z.string(),
    upstreams: z.array(z.string()),
});

const serverSchema = z.object({
    listen: z.number(),
    workers: z.number().optional(),
    upstreams: z.array(upstreamSchema),
    headers: z.array(headerSchema).optional(),
    rules: z.array(ruleSchema),
});

export const rootConfigSchema = z.object({
    server: serverSchema,
});

export type ConfigSchemaType = z.infer<typeof rootConfigSchema>;
ログイン後にコピー
ログイン後にコピー

マスター プロセスは、Node.js IPC (プロセス間通信) を使用して、必要なすべてのリクエスト情報を含む標準化されたメッセージ ペイロードを構築し、Zod スキーマを使用してメッセージ構造を検証することにより、ワーカーと通信します。

ワーカーは実際のリクエストの処理とプロキシを処理します。各ワーカー:

  • 環境変数から設定をロードします
  • Zod スキーマを使用して構成を検証します
  • 構成の独自のコピーを維持します

ワーカーは次の方法で上流サーバーを選択します:

  • ルールから適切なアップストリーム ID を見つける
  • アップストリームサーバー構成の検索
  • 上流サーバーの存在を検証しています

リクエスト転送メカニズム:

  • 上流サーバーへの新しい HTTP リクエストを作成します
  • 応答データをストリーミングします
  • レスポンスボディを集約します
  • マスタープロセスに応答を送り返します

サーバーの実行

サーバーを実行するには、次の手順に従います:

  1. プロジェクトをビルドします:
import fs from "node:fs/promises";
import { parse } from "yaml";
import { rootConfigSchema } from "./config-schema";

export async function parseYAMLConfig(filepath: string) {
    const configFileContent = await fs.readFile(filepath, "utf8");
    const configParsed = parse(configFileContent);
    return JSON.stringify(configParsed);
}

export async function validateConfig(config: string) {
    const validatedConfig = await rootConfigSchema.parseAsync(
        JSON.parse(config)
    );
    return validatedConfig;
}
ログイン後にコピー
ログイン後にコピー
  1. サーバーを起動します:
if (cluster.isPrimary) {
    console.log("Master Process is up ?");
    for (let i = 0; i < workerCount; i++) {
        const w = cluster.fork({ config: JSON.stringify(config) });
        WORKER_POOL.push(w);
        console.log(Master Process: Worker Node spinned: ${i});
    }

    const server = http.createServer((req, res) => {
        const index = Math.floor(Math.random() * WORKER_POOL.length);
        const worker = WORKER_POOL.at(index);

        if (!worker) throw new Error("Worker not found.");

        const payload: WorkerMessageSchemaType = {
            requestType: "HTTP",
            headers: req.headers,
            body: null,
            url: ${req.url},
        };
        worker.send(JSON.stringify(payload));

        worker.once("message", async (workerReply: string) => {
            const reply = await workerMessageReplySchema.parseAsync(
                JSON.parse(workerReply)
            );

            if (reply.errorCode) {
                res.writeHead(parseInt(reply.errorCode));
                res.end(reply.error);
            } else {
                res.writeHead(200);
                res.end(reply.data);
            }
        });
    });

    server.listen(port, () => {
        console.log(Reverse Proxy listening on port: ${port});
    });
}
ログイン後にコピー
  1. 開発モード:
const server = http.createServer(function (req, res) {
    const index = Math.floor(Math.random() * WORKER_POOL.length);
    const worker = WORKER_POOL.at(index);

    const payload: WorkerMessageSchemaType = {
        requestType: "HTTP",
        headers: req.headers,
        body: null,
        url: ${req.url},
    };
    worker.send(JSON.stringify(payload));
});
ログイン後にコピー

Building a Scalable Reverse Proxy Server like Nginx with Node.js and TypeScript

上のスクリーンショットでは、1 つのマスター ノードと 2 つのワーカー プロセスが実行されていることがわかります。リバース プロキシ サーバーはポート 8080 でリッスンしています。
config.yaml ファイルには、jsonplaceholder と dummy という 2 つの上流サーバーを記述します。サーバーに届くすべてのリクエストを jsonplaceholder にルーティングしたい場合は、ルールを次のようにします:

├── config.yaml           # Server configuration
├── src/
│   ├── config-schema.ts  # Configuration validation schemas
│   ├── config.ts         # Configuration parsing logic
│   ├── index.ts         # Application entry point
│   ├── server-schema.ts # Server message schemas
│   └── server.ts        # Core server implementation
└── tsconfig.json        # TypeScript configuration
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

同様に、/test エンドポイントへのリクエストをダミーの上流サーバーにルーティングする必要がある場合は、ルールを次のように設定します。

server:
    listen: 8080          # Port the server listens on.
    workers: 2            # Number of worker processes to handle requests.
    upstreams:            # Define upstream servers (backend targets).
        - id: jsonplaceholder
          url: jsonplaceholder.typicode.com
        - id: dummy
          url: dummyjson.com
    headers:              # Custom headers added to proxied requests.
        - key: x-forward-for
          value: $ip      # Adds the client IP to the forwarded request.
        - key: Authorization
          value: Bearer xyz  # Adds an authorization token to requests.
    rules:                # Define routing rules for incoming requests.
        - path: /test
          upstreams:
              - dummy     # Routes requests to "/test" to the "dummy" upstream.
        - path: /
          upstreams:
              - jsonplaceholder  # Routes all other requests to "jsonplaceholder".

ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

これをテストしてみましょう!

Building a Scalable Reverse Proxy Server like Nginx with Node.js and TypeScript

うわー、すごいですね! localhost:8080 に移動していますが、応答として、jsonplaceholder.typicode.com のホームページを受信したことがわかります。エンド ユーザーは、別のサーバーからの応答が表示されていることさえ知りません。このため、リバース プロキシ サーバーが重要です。同じコードを実行する複数のサーバーがあり、それらのすべてのポートをエンド ユーザーに公開したくない場合は、抽象化レイヤーとしてリバース プロキシを使用します。ユーザーは、非常に堅牢で高速なサーバーであるリバース プロキシ サーバーにアクセスし、要求をルーティングするサーバーを決定します。

ここで localhost:8080/todos にアクセスして、何が起こるか見てみましょう。

Building a Scalable Reverse Proxy Server like Nginx with Node.js and TypeScript

私たちのリクエストは再び jsonplaceholder サーバーにリバース プロキシされ、解決された URL: jsonplaceholder.typicode.com/todos から JSON レスポンスを受け取りました。

通信の流れ

完全なリクエスト フローを視覚化してみましょう:

クライアントがリクエストを送信 → マスタープロセス
マスタープロセス → 選択されたワーカー
ワーカー → 上流サーバー
上流サーバー → ワーカー
ワーカー → マスタープロセス
マスタープロセス → クライアント

パフォーマンスに関する考慮事項

マルチプロセス アーキテクチャにより、いくつかのパフォーマンス上の利点が得られます。

  1. CPU 使用率: ワーカー プロセスは、利用可能なハードウェア リソースを利用して、さまざまな CPU コアで実行できます。
  2. プロセスの分離: 1 つのワーカーでクラッシュしても他のワーカーに影響を与えず、信頼性が向上します。
  3. 負荷分散: リクエストをランダムに分散することで、単一のワーカーが過負荷になるのを防ぎます。

今後の改善点

現在の実装は機能しますが、次のように拡張できます。

  1. 負荷分散の改善: ラウンドロビンや最小接続など、より高度なアルゴリズムを実装します。
  2. ヘルスチェック: 上流サーバーの定期的なヘルスチェックを追加します。
  3. キャッシュ: 応答キャッシュを実装して、上流サーバーの負荷を軽減します。
  4. メトリクス: 監視用にプロメテウススタイルのメトリクスを追加します。
  5. WebSocket サポート: WebSocket 接続を処理するためにプロキシを拡張します。
  6. HTTPS サポート: SSL/TLS 終了機能を追加します。

まとめ

リバース プロキシ サーバーをゼロから構築するのは、最初は恐ろしいように思えるかもしれませんが、これまで調べてきたように、やりがいのある経験です。 Node.js クラスター、TypeScript、YAML ベースの構成管理を組み合わせることで、Nginx からインスピレーションを得たスケーラブルで効率的なシステムを作成しました。

この実装にはまだ改善の余地があり、負荷分散、キャッシュ、WebSocket サポートの改善などは検討すべきアイデアのほんの一部です。しかし、現在の設計は、さらなる実験と拡張のための強力な基盤を確立します。ここまで進めていただければ、リバース プロキシについてさらに深く掘り下げたり、ニーズに合わせたカスタム ソリューションの構築を開始したりできるようになりました。

私の作品につながりたい場合、または私の作品をもっと見たい場合は、私の GitHub や LinkedIn をチェックしてください。
このプロジェクトのリポジトリはここにあります。

ご意見、フィードバック、改善のアイデアをお聞かせください。読んでいただきありがとうございます。コーディングを楽しんでください。 ?

以上がNode.js と TypeScript を使用して Nginx のようなスケーラブルなリバース プロキシ サーバーを構築するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

フロントエンドのサーマルペーパーレシートのために文字化けしたコード印刷に遭遇した場合はどうすればよいですか? フロントエンドのサーマルペーパーレシートのために文字化けしたコード印刷に遭遇した場合はどうすればよいですか? Apr 04, 2025 pm 02:42 PM

フロントエンドのサーマルペーパーチケット印刷のためのよくある質問とソリューションフロントエンド開発におけるチケット印刷は、一般的な要件です。しかし、多くの開発者が実装しています...

javascriptの分解:それが何をするのか、なぜそれが重要なのか javascriptの分解:それが何をするのか、なぜそれが重要なのか Apr 09, 2025 am 12:07 AM

JavaScriptは現代のWeb開発の基礎であり、その主な機能には、イベント駆動型のプログラミング、動的コンテンツ生成、非同期プログラミングが含まれます。 1)イベント駆動型プログラミングにより、Webページはユーザー操作に応じて動的に変更できます。 2)動的コンテンツ生成により、条件に応じてページコンテンツを調整できます。 3)非同期プログラミングにより、ユーザーインターフェイスがブロックされないようにします。 JavaScriptは、Webインタラクション、シングルページアプリケーション、サーバー側の開発で広く使用されており、ユーザーエクスペリエンスとクロスプラットフォーム開発の柔軟性を大幅に改善しています。

誰がより多くのPythonまたはJavaScriptを支払われますか? 誰がより多くのPythonまたはJavaScriptを支払われますか? Apr 04, 2025 am 12:09 AM

スキルや業界のニーズに応じて、PythonおよびJavaScript開発者には絶対的な給与はありません。 1. Pythonは、データサイエンスと機械学習でさらに支払われる場合があります。 2。JavaScriptは、フロントエンドとフルスタックの開発に大きな需要があり、その給与もかなりです。 3。影響要因には、経験、地理的位置、会社の規模、特定のスキルが含まれます。

JavaScriptは学ぶのが難しいですか? JavaScriptは学ぶのが難しいですか? Apr 03, 2025 am 12:20 AM

JavaScriptを学ぶことは難しくありませんが、挑戦的です。 1)変数、データ型、関数などの基本概念を理解します。2)非同期プログラミングをマスターし、イベントループを通じて実装します。 3)DOM操作を使用し、非同期リクエストを処理することを約束します。 4)一般的な間違いを避け、デバッグテクニックを使用します。 5)パフォーマンスを最適化し、ベストプラクティスに従ってください。

Shiseidoの公式Webサイトのように、視差スクロールと要素のアニメーション効果を実現する方法は?
または:
Shiseidoの公式Webサイトのようにスクロールするページを伴うアニメーション効果をどのように実現できますか? Shiseidoの公式Webサイトのように、視差スクロールと要素のアニメーション効果を実現する方法は? または: Shiseidoの公式Webサイトのようにスクロールするページを伴うアニメーション効果をどのように実現できますか? Apr 04, 2025 pm 05:36 PM

この記事の視差スクロールと要素のアニメーション効果の実現に関する議論では、Shiseidoの公式ウェブサイト(https://www.shisido.co.co.jp/sb/wonderland/)と同様の達成方法について説明します。

JavaScriptの進化:現在の傾向と将来の見通し JavaScriptの進化:現在の傾向と将来の見通し Apr 10, 2025 am 09:33 AM

JavaScriptの最新トレンドには、TypeScriptの台頭、最新のフレームワークとライブラリの人気、WebAssemblyの適用が含まれます。将来の見通しは、より強力なタイプシステム、サーバー側のJavaScriptの開発、人工知能と機械学習の拡大、およびIoTおよびEDGEコンピューティングの可能性をカバーしています。

JavaScriptを使用して、同じIDを持つArray要素を1つのオブジェクトにマージする方法は? JavaScriptを使用して、同じIDを持つArray要素を1つのオブジェクトにマージする方法は? Apr 04, 2025 pm 05:09 PM

同じIDを持つ配列要素をJavaScriptの1つのオブジェクトにマージする方法は?データを処理するとき、私たちはしばしば同じIDを持つ必要性に遭遇します...

Console.log出力の違い結果:なぜ2つの呼び出しが異なるのですか? Console.log出力の違い結果:なぜ2つの呼び出しが異なるのですか? Apr 04, 2025 pm 05:12 PM

Console.log出力の違いの根本原因に関する詳細な議論。この記事では、Console.log関数の出力結果の違いをコードの一部で分析し、その背後にある理由を説明します。 �...

See all articles