フロントエンドJSサンドボックスを実装するいくつかの方法についての簡単な説明

フロントエンドJSサンドボックスを実装するいくつかの方法についての簡単な説明

序文

マイクロフロントエンドの分野では、サンドボックスは非常に重要なものです。たとえば、マイクロフロントエンドフレームワークの single-spa は js サンドボックスを実装していません。大規模なマイクロフロントエンドアプリケーションを構築する場合、変数の競合が発生しやすく、アプリケーションの信頼性に大きなリスクをもたらします。マイクロ フロントエンドには、ドキュメント、場所、その他のオブジェクトなど、すべてのアプリケーション間で共有する必要があるグローバル オブジェクトがいくつかあります。サブアプリケーションの開発中は、複数のチームが作業する可能性があり、グローバル変数の使用を制限することは困難です。ページによっては複数の異なるサブアプリケーションが存在する場合があり、複数のサンドボックスをサポートする必要があり、各サンドボックスには読み込み、アンロード、および復元の機能が必要です。

iframeはサンドボックスを実装します

フロントエンドには、比較的重要な HTML タグ iframe があります。実際、iframe オブジェクトを使用して、contentWindow を通じてネイティブ ブラウザー オブジェクトを取り出すことができます。このオブジェクトは当然すべてのプロパティを持ち、メインのアプリケーション環境から分離されています。下のコードを見てみましょう

iframe を document.createElement('iframe',{src:'about:blank'}) に設定します。
document.body.appendChild(iframe);
const sandboxGlobal = iframe.contentWindow;

注: 同じドメイン内の iframe のみが対応するコンテンツウィンドウを取得できます。iframe の src は about:blank に設定されており、同じドメイン内にあるためリソースの読み込みは行われません。iframe src を参照してください。

序文で、マイクロ フロントエンドは、分離されたウィンドウ環境を持つことに加えて、いくつかのグローバル オブジェクトを共有する必要があることを述べました。現時点では、プロキシを使用してこれを実現できます。下のコードを見てみましょう

クラス SandboxWindow {
    /**
     * コンストラクタ * @param {*} context 共有するオブジェクト * @param {*} frameWindow iframeのウィンドウ
     */
    コンストラクタ(コンテキスト、フレームウィンドウ) {
        
        新しいProxy(frameWindow, { を返す
            get(ターゲット、名前) {
                if (name in context) { // 最初に共有オブジェクトを使用します return context[name];
                }
                ターゲット[名前]を返します。
            },
            set(ターゲット、名前、値) {
                if (name in context) { // 共有オブジェクトの値を変更します return context[name] = value;
                }
                ターゲット[名前] = 値;
            }
        })
    }
}

// グローバルに共有する必要がある変数 const context = { document:window.document, history:window.history }

// サンドボックスを作成します。const newSandboxWindow = new SandboxWindow(context, sandboxGlobal); 

// サンドボックス上のオブジェクトがグローバルオブジェクトと等しいかどうかを判断します console.log('equal',newSandboxWindow.document === window.document)

newSandboxWindow.abc = '1'; // サンドボックスにプロパティを追加 console.log(window.abc); // プロパティをグローバルに表示 console.log(newSandboxWindow.abc) // サンドボックス上のプロパティを表示

実行して結果を見てみましょう

上記のように、iframe サンドボックスを使用すると、次の機能を実現できます。

  • setTimeout、location、reactの異なるバージョンなどのグローバル変数の分離
  • ルーティングの分離: アプリケーションは独立したルーティングを実装するか、グローバルルーティングを共有することができます。
  • 複数のインスタンス、複数の独立したマイクロアプリケーションを同時に実行できます

diffメソッドを使用したサンドボックスの実装

プロキシをサポートしていないブラウザでは、diff を通じてサンドボックス化を実践できます。アプリケーションの実行中、スナップショット ウィンドウ オブジェクトが保存され、現在のウィンドウ オブジェクトのすべてのプロパティがスナップショット オブジェクトにコピーされます。サブアプリケーションがアンインストールされると、ウィンドウ オブジェクトが変更されて差分が作成され、異なるプロパティが modifyMap に保存されます。再度マウントされると、これらの変更されたプロパティが追加されます。コードは次のとおりです。

クラス DiffSandbox {
  コンストラクタ(名前) {
    this.name = 名前;
    this.modifyMap = {}; // 変更されたプロパティを保存します。this.windowSnapshot = {};
  }
  アクティブ() {
    // アクティブな状態のサンドボックスをキャッシュします this.windowSnapshot = {};
    for (ウィンドウ内のconst項目) {
      this.windowSnapshot[item] = window[item];
    }

    Object.keys(this.modifyMap).forEach(p => {
      ウィンドウ[p] = this.modifyMap[p];
    })

  }

  非アクティブ(){
    for (ウィンドウ内のconst項目) {
      if (this.windowSnapshot[item] !== window[item]) {
        //変更を記録する this.modifyMap[item] = window[item];
        // ウィンドウを復元
        window[item] = this.windowSnapshot[item];
      }
    }
  }
}

const diffSandbox = new DiffSandbox('diff サンドボックス');
diffSandbox.active(); // サンドボックスをアクティブにする window.a = '1'
console.log('サンドボックスを開く:',window.a);
diffSandbox.inactive(); //サンドボックスを非アクティブ化 console.log('サンドボックスを非アクティブ化:', window.a);
diffSandbox.active(); // 再アクティブ化 console.log('再度アクティブ化', window.a);

実行して結果を見てみましょう

このメソッドは、実行時にすべてのプロパティがウィンドウに保存されるため、複数のインスタンスもサポートできません。

プロキシに基づく単一インスタンスサンドボックスの実装

ES6 では、プロキシを介してオブジェクトをハイジャックできます。基本レコードは、ウィンドウ オブジェクトの変更を通じても記録されます。これらのレコードは、アプリケーションがアンインストールされると削除され、アプリケーションが再度アクティブ化されると復元され、サンドボックス環境をシミュレートする目的を達成します。コードは次のとおりです

// ウィンドウのプロパティを変更するパブリックメソッド const updateWindowProp = (prop, value, isDel) => {
    if (値 === 未定義 || isDel) {
        ウィンドウ[prop]を削除します。
    } それ以外 {
        window[prop] = 値;
    }
}

クラス ProxySandbox {

    アクティブ() {
        // レコードに基づいてサンドボックスを復元します this.currentUpdatedPropsValueMap.forEach((v, p) => updateWindowProp(p, v));
    }
    非アクティブ(){
        // 1 サンドボックス中に変更されたプロパティを元のプロパティに復元します this.modifiedPropsMap.forEach((v, p) => updateWindowProp(p, v));
        // 2 サンドボックス中に追加されたグローバル変数を削除します。this.addedPropsMap.forEach((_, p) => updateWindowProp(p, undefined, true));
    }

    コンストラクタ(名前) {
        this.name = 名前;
        this.proxy = null;
        //新しく追加されたグローバル変数を保存します。this.addedPropsMap = new Map(); 
        //サンドボックス中に更新されたグローバル変数を保存します。this.modifiedPropsMap = new Map();
        // サンドボックスがアクティブ化されたときに使用される新しいグローバル変数と変更されたグローバル変数があります。this.currentUpdatedPropsValueMap = new Map();

        const { addedPropsMap、currentUpdatedPropsValueMap、modifiedPropsMap } = this;
        const fakeWindow = Object.create(null);
        const proxy = 新しいProxy(fakeWindow, {
            set(ターゲット、プロパティ、値) {
                if (!window.hasOwnProperty(prop)) {
                    // ウィンドウにプロパティがない場合、新しく追加されたプロパティを // デバッガーに記録します。
                    追加されたPropsMap.set(prop, value);
                } そうでない場合 (!modifiedPropsMap.has(prop)) {
                    // 現在のウィンドウ オブジェクトにこのプロパティがあり、更新されていない場合は、ウィンドウのこのプロパティの初期値を記録します。const originalValue = window[prop];
                    プロパティを元の値に設定します。
                }
                // 変更されたプロパティと変更された値を記録します currentUpdatedPropsValueMap.set(prop, value);
                // グローバル ウィンドウに値を設定します updateWindowProp(prop, value);
                true を返します。
            },
            get(ターゲット、プロパティ) {
                window[prop]を返します。
            },
        });
        this.proxy = プロキシ;
    }
}


const newSandBox = 新しい ProxySandbox('プロキシサンドボックス');
const proxyWindow = newSandBox.proxy;
プロキシウィンドウ.a = '1'
console.log('サンドボックスを開きます:', proxyWindow.a, window.a);
newSandBox.inactive(); //サンドボックスを非アクティブ化 console.log('サンドボックスを非アクティブ化:', proxyWindow.a, window.a);
newSandBox.active(); //サンドボックスを非アクティブ化 console.log('サンドボックスを再アクティブ化:', proxyWindow.a, window.a);

コードを実行して結果を見てみましょう

この方法では、一度にアクティブにできるサンドボックスは 1 つだけです。そうでない場合、グローバル オブジェクトの変数が 2 つ以上のサンドボックスによって更新され、グローバル変数の競合が発生します。

プロキシに基づくマルチインスタンスサンドボックスの実装

単一インスタンスのシナリオでは、fakeWindow は変数を格納する機能を持たない空のオブジェクトです。マイクロアプリケーションによって作成された変数は、最終的にはウィンドウにマウントされるため、同時にアクティブ化できるマイクロアプリケーションの数が制限されます。

クラスMultipleProxySandbox {

    アクティブ() {
        this.sandboxRunning = true;
    }
    非アクティブ(){
        this.sandboxRunning = false;
    }

    /**
     * コンストラクタ * @param {*} name サンドボックス名 * @param {*} context 共有コンテキスト * @returns 
     */
    コンストラクタ(名前、コンテキスト = {}) {
        this.name = 名前;
        this.proxy = null;
        const fakeWindow = Object.create({});
        const proxy = 新しいProxy(fakeWindow, {
            設定: (ターゲット、名前、値) => {
                (this.sandboxRunning)の場合{
                    if (Object.keys(context).includes(name)) {
                        コンテキスト[名前] = 値;
                    }
                    ターゲット[名前] = 値;
                }
            },
            取得: (ターゲット、名前) => {
                // 共有オブジェクトを優先する if (Object.keys(context).includes(name)) {
                    context[name]を返します。
                }
                ターゲット[名前]を返します。
            }
        })
        this.proxy = プロキシ;
    }
}

const コンテキスト = { ドキュメント: window.document };

const newSandBox1 = new MultipleProxySandbox('プロキシサンドボックス1', context);
新しいサンドボックス1.アクティブ();
定数 proxyWindow1 = newSandBox1.proxy;

const newSandBox2 = new MultipleProxySandbox('プロキシサンドボックス2', コンテキスト);
新しいサンドボックス2.アクティブ();
const proxyWindow2 = newSandBox2.proxy;
console.log('共有オブジェクトは等しいですか?', window.document === proxyWindow1.document, window.document === proxyWindow2.document);

proxyWindow1.a = '1'; // プロキシ 1 の値を設定します proxyWindow2.a = '2'; // プロキシ 2 の値を設定します window.a = '3'; // ウィンドウの値を設定します console.log('出力値を印刷', proxyWindow1.a, proxyWindow2.a, window.a);


newSandBox1.inactive(); newSandBox2.inactive(); // 両方のサンドボックスが非アクティブ化されています proxyWindow1.a = '4'; // proxy1 の値を設定します proxyWindow2.a = '4'; // proxy2 の値を設定します window.a = '4'; // ウィンドウの値を設定します console.log('非アクティブ化後に印刷された値', proxyWindow1.a, proxyWindow2.a, window.a);

newSandBox1.active(); newSandBox2.active(); // 再度アクティブ化 proxyWindow1.a = '4'; // プロキシ 1 の値を設定 proxyWindow2.a = '4'; // プロキシ 2 の値を設定 window.a = '4'; // ウィンドウの値を設定 console.log('非アクティブ化後に印刷された値', proxyWindow1.a, proxyWindow2.a, window.a);

コードを実行すると、次の結果が得られます。

この方法では、一度にアクティブ化できるサンドボックスは 1 つだけなので、マルチインスタンス サンドボックス化が実現します。

結論

上記はマイクロフロントエンドでよく使われるサンドボックス実装方法ですが、本番環境で使用する場合は多くの判断と制約が必要になります。次の記事では、マイクロフロントエンドフレームワーク Qiankun がソースコードを通じてサンドボックスをどのように実装するかを見ていきます。上記のコードはgithubにあります。表示するにはjs-sandboxにアクセスしてください。

参照する

iframe ソース
ES6 プロキシ

フロントエンド JS サンドボックスを実装するいくつかの方法についての記事はこれで終わりです。より関連性の高い JS サンドボックス コンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Quickjs は JavaScript サンドボックスの詳細をカプセル化します
  • JavaScript サンドボックスの探索
  • JavaScript Sandboxについての簡単な説明
  • Node.jsサンドボックス環境についての簡単な説明
  • Node.js アプリケーション用の安全なサンドボックス環境の設定
  • JS実装クロージャにおけるサンドボックスモードの例
  • JS サンドボックス モードの例の分析
  • JavaScript デザインパターン セキュリティ サンドボックス モード
  • WebWorkerはJavaScriptサンドボックスの詳細をカプセル化します

<<:  Linux teeコマンドの使い方の詳しい説明

>>:  MySQL 8.0.16 圧縮パッケージのインストールと設定方法のグラフィックチュートリアル

推薦する

効率的なMySQLページングの詳細な説明

序文通常、大量のデータを扱う MySQL クエリには「ページング」戦略が採用されます。ただし、ページ...

AngularとIonicのライフサイクルとフック関数を素早く理解するための記事

目次角度成し遂げる呼び出し順序知らせイオニックionic はページのライフサイクルをどのように処理し...

JS でシンプルなデータ監視を実装する方法

目次概要最初のステップステップ2なぜ別の _data が必要なのでしょうか?データにもう少しデータを...

ページ内にマーキーとフラッシュが共存する場合の競合解決

競合の主な症状は、FLASH ボタンがジャンプし続け、不安定になり、Web ページの外観と通常のアク...

JavaScript配列をツリー構造に変換する方法

1. 需要バックエンドは、フロントエンドがツリー構造(重複データなし)に変換するためのデータを提供し...

期間限定フラッシュセール機能を実現するJavaScriptタイマー

この記事では、期間限定フラッシュセール機能を実装するためのJavaScriptの具体的なコードを参考...

TypeScript の基本型の紹介

目次1. 基本タイプ2. オブジェクトタイプ2.1 配列2.2 タプル2.3 オブジェクト3. 型推...

CSS での三角形の描画と巧妙な応用例の詳細な説明

鉛Web ページ上の一般的な三角形の一部は、画像やフォント アイコンにする必要なく、CSS を使用し...

CentOS7仮想マシンで固定IPアドレスを設定する方法

私の開発環境は、VMWare 仮想マシンに CentOS をインストールし、ホスト ファイルにインタ...

HTML 選択ボックスのプレースホルダーの作成に関する問題

テキスト入力でプレースホルダーを使用していますが、問題なく動作します。しかし、選択ボックスにはプレー...

Nginxホットデプロイメントの実装

目次セマフォNginx ホットデプロイメント上記のブログ投稿に従ってください。ファイアウォールをオフ...

MySql 8.0.11 のインストール プロセスと Navicat とのリンク時に発生する問題の概要

私のシステムとソフトウェアのバージョンは次のとおりです。システム環境: win7、64ビットMySQ...

5分でWebRTCビデオチャットを構築する

前回の記事では、Ubuntu 上の webrtc ベースの多人数ビデオチャット サービスの詳細なコー...

iframe の多層ネスト、無制限のネスト、高度に適応したソリューション

ページ A、B、C の 3 つがあります。ページ A にはページ B が含まれ、ページ B にはペー...

MySQL の replace と replace into の詳細な例 into_Mysql

MySQL の replace と replace into はどちらも頻繁に使用される関数です。...