フロントエンド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 圧縮パッケージのインストールと設定方法のグラフィックチュートリアル

推薦する

Reactフックとzarmコンポーネントライブラリ構成に基づいてh5フォームページを開発するためのサンプルコード

最近、React Hooks を zarm コンポーネント ライブラリと組み合わせて使用​​し、js...

Linux に ASPNET.Core3.0 ランタイムをインストールするためのサンプル コード

# 以下の例は x64 ビット ランタイム v3.0.0 用です mkdir /runtimes ...

Windows Server 2008 64ビット MySQL5.6 インストール不要版 設定方法図

1 公式ウェブサイトから MySQL 5.6 バージョンの圧縮パッケージmysql-5.6.36-w...

MySQL 8.0 の非表示列に対する基本操作

目次01 非表示の列を作成する02 非表示の列に対する基本操作03 非表示の列メタデータ04 主キー...

MysqlチューニングExplainツールの詳細な説明と実践的な演習(推奨)

MySQL チューニング ツールの詳細な説明と実践的な演習の説明 ツールの紹介の説明 分析例の説明...

kubernetes1.5.2 から kubernetes1.10 にアップグレードする際の主要な設定変更記録

この記事では、kubernetes1.5.2 から kubernetes1.10 にアップグレードす...

1時間で学ぶMySQLの基礎

目次MySQL を使い始めるMySQL 管理6. MySQL サーバーを起動および停止します。 7....

Tomcat9 のダウンロード、インストール、設定 + Eclipse への統合に関する詳細なチュートリアル

トムキャット公式サイトtomcatはローカルサーバーと同等であり、Webページを開くことができます設...

MySQL 5.7 をバイナリモードでインストールし、Linux でシステムを最適化する手順

この記事では主に、MySQL バイナリ パッケージのインストール/起動/シャットダウンのプロセスを紹...

HTML検証 HTML検証

HTML 検証はHTML 検証を指します。これは、HTML ドキュメントを分析し、標準の HTML ...

Docker データボリュームの一般的な操作コードの例

開発者が Dockerfile を使用してイメージをビルドする場合は、イメージをビルドするときにデー...

JavaScript はドラッグ可能なモーダルボックスを実装します

この記事では、ドラッグ可能なモーダルボックスを実装するためのJavaScriptの具体的なコードを参...

ボタンの 4 つのクリック応答方法の概要

ボタンは頻繁に使用されます。ここでは、イベント処理メソッドを整理し、実装方法が多数あることを発見しま...

CentOS 6.x のインストール時に発生するエラー「ディスク sda に BIOS RAID メタデータが含まれています」の解決方法

今日、CentOS6.2 をインストールしていたところ、ハード ドライブの検出段階を通過できませんで...

MySQL5.7 シングルインスタンス自動起動サービスの設定プロセス

1.MySQLのバージョン [root@clq システム]# mysql -v MySQL モニター...