Eine kurze Diskussion über verschiedene Möglichkeiten zur Implementierung einer Front-End-JS-Sandbox

Eine kurze Diskussion über verschiedene Möglichkeiten zur Implementierung einer Front-End-JS-Sandbox

Vorwort

Im Bereich der Mikro-Frontends ist Sandbox eine sehr wichtige Sache. Beispielsweise implementiert das Micro-Frontend-Framework Single-Spa keine JS-Sandbox. Wenn wir groß angelegte Micro-Frontend-Anwendungen erstellen, können leicht Variablenkonflikte auftreten, was ein großes Risiko für die Zuverlässigkeit der Anwendung darstellt. In Mikro-Frontends gibt es einige globale Objekte, die von allen Anwendungen gemeinsam genutzt werden müssen, z. B. Dokumente, Standorte und andere Objekte. Während der Entwicklung von Unteranwendungen arbeiten möglicherweise mehrere Teams daran und es ist schwierig, sie von der Verwendung globaler Variablen abzuhalten. Einige Seiten haben möglicherweise mehrere unterschiedliche Unteranwendungen, sodass wir mehrere Sandboxen unterstützen müssen. Jede Sandbox muss zudem die Möglichkeit zum Laden, Entladen und Wiederherstellen bieten.

iframe implementiert Sandbox

Im Frontend gibt es ein relativ wichtiges HTML-Tag-Iframe. Tatsächlich können wir das Iframe-Objekt verwenden, um das native Browserobjekt über contentWindow herauszunehmen. Dieses Objekt verfügt natürlich über alle Eigenschaften und ist von der Hauptanwendungsumgebung isoliert. Schauen wir uns den Code unten an

let iframe = document.createElement('iframe',{src:'about:blank'});
Dokument.body.appendChild(iframe);
const sandboxGlobal = iframe.contentWindow;

Hinweis: Nur ifame in derselben Domäne kann das entsprechende contentWindow abrufen. Die Quelle des Iframes ist auf about:blank eingestellt, wodurch sichergestellt werden kann, dass es sich in derselben Domäne befindet und keine Ressourcen geladen werden. Siehe Iframe-Quelle

Im Vorwort haben wir erwähnt, dass das Micro-Frontend neben einer isolierten Fensterumgebung tatsächlich einige globale Objekte gemeinsam nutzen muss. Derzeit können wir dazu einen Proxy verwenden. Schauen wir uns den Code unten an

Klasse SandboxWindow {
    /**
     * Konstruktor * @param {*} context Das zu teilende Objekt * @param {*} frameWindow Das Fenster des Iframes
     */
    Konstruktor(Kontext, Rahmenfenster) {
        
        gib einen neuen Proxy zurück (FrameWindow, {
            get(Ziel, Name) {
                if (Name im Kontext) { // Zuerst gemeinsam genutzte Objekte verwenden return context[Name];
                }
                Ziel [Name] zurückgeben;
            },
            setze(Ziel, Name, Wert) {
                if (Name im Kontext) { // Ändere den Wert des gemeinsam genutzten Objekts return context[Name] = Wert;
                }
                Ziel[Name] = Wert;
            }
        })
    }
}

// Variablen, die global geteilt werden müssen const context = { document:window.document, history:window.history }

// Eine Sandbox erstellen const newSandboxWindow = new SandboxWindow(context, sandboxGlobal); 

// Bestimmen Sie, ob das Objekt in der Sandbox dem globalen Objekt entspricht console.log('equal',newSandboxWindow.document === window.document)

newSandboxWindow.abc = '1'; //Eigenschaften zur Sandbox hinzufügen console.log(window.abc); //Eigenschaften global anzeigen console.log(newSandboxWindow.abc) //Eigenschaften in der Sandbox anzeigen

Lassen Sie es uns ausführen und die Ergebnisse ansehen

Oben können wir die Iframe-Sandbox verwenden, um die folgenden Funktionen zu erreichen:

  • Isolierung globaler Variablen wie setTimeout, location und verschiedener Versionen von react
  • Routing-Isolierung: Anwendungen können unabhängiges Routing implementieren oder globales Routing gemeinsam nutzen
  • Mehrere Instanzen, mehrere unabhängige Mikroanwendungen können gleichzeitig ausgeführt werden

Implementieren einer Sandbox mit der Diff-Methode

In Browsern, die keine Proxys unterstützen, können wir Sandboxing über Diff üben. Wenn die Anwendung ausgeführt wird, wird ein Snapshot-Fensterobjekt gespeichert und alle Eigenschaften des aktuellen Fensterobjekts werden in das Snapshot-Objekt kopiert. Wenn die Unteranwendung deinstalliert wird, wird das Fensterobjekt geändert, um einen Unterschied zu erstellen, und die unterschiedlichen Eigenschaften werden in einer ModifyMap gespeichert. Wenn es erneut gemountet wird, werden diese geänderten Eigenschaften hinzugefügt. Der Code lautet wie folgt:

Klasse DiffSandbox {
  Konstruktor(Name) {
    dieser.name = Name;
    this.modifyMap = {}; //Geänderte Eigenschaften speichern this.windowSnapshot = {};
  }
  aktiv() {
    // Aktiven Status der Sandbox zwischenspeichern this.windowSnapshot = {};
    für (const item in window) {
      this.windowSnapshot[Element] = Fenster[Element];
    }

    Objekt.Schlüssel(diese.modifyMap).fürJeden(p => {
      Fenster[p] = diese.modifyMap[p];
    })

  }

  inaktiv() {
    für (const item in window) {
      wenn (this.windowSnapshot[item] !== window[item]) {
        //Änderungen aufzeichnen this.modifyMap[item] = window[item];
        // Fenster wiederherstellen
        Fenster[Element] = dieses.FensterSnapshot[Element];
      }
    }
  }
}

const diffSandbox = neue DiffSandbox('diff sandbox');
diffSandbox.active(); // Sandbox-Fenster aktivieren.a = '1'
console.log('Sandbox öffnen:',window.a);
diffSandbox.inactive(); //Sandbox deaktivieren console.log('Sandbox deaktivieren:', window.a);
diffSandbox.active(); // Reaktivieren console.log('Erneut aktivieren', window.a);

Lassen Sie es uns ausführen und die Ergebnisse ansehen

Diese Methode kann auch keine mehreren Instanzen unterstützen, da alle Eigenschaften während der Laufzeit im Fenster gespeichert werden.

Implementierung einer Einzelinstanz-Sandbox basierend auf einem Proxy

In ES6 können wir Objekte über Proxys kapern. Der Basisdatensatz wird auch durch die Änderung des Fensterobjekts aufgezeichnet. Diese Datensätze werden gelöscht, wenn die Anwendung deinstalliert wird, und wiederhergestellt, wenn die Anwendung erneut aktiviert wird, um den Zweck der Simulation der Sandbox-Umgebung zu erreichen. Der Code lautet wie folgt

// Öffentliche Methode zum Ändern der Fenstereigenschaften const updateWindowProp = (prop, value, isDel) => {
    wenn (Wert === undefiniert || isDel) {
        Fenster löschen[Eigenschaft];
    } anders {
        Fenster[Eigenschaft] = Wert;
    }
}

Klasse ProxySandbox {

    aktiv() {
        // Stellen Sie die Sandbox basierend auf dem Datensatz this.currentUpdatedPropsValueMap.forEach((v, p) => updateWindowProp(p, v)) wieder her.
    }
    inaktiv() {
        // 1 Stellen Sie die während der Sandbox geänderten Eigenschaften auf die ursprünglichen Eigenschaften zurück. this.modifiedPropsMap.forEach((v, p) => updateWindowProp(p, v));
        // 2 Eliminieren Sie die während der Sandbox hinzugefügten globalen Variablen this.addedPropsMap.forEach((_, p) => updateWindowProp(p, undefined, true));
    }

    Konstruktor(Name) {
        dieser.name = Name;
        dieser.proxy = null;
        //Speichern Sie die neu hinzugefügten globalen Variablen this.addedPropsMap = new Map(); 
        //Speichern Sie globale Variablen, die während der Sandbox aktualisiert wurden. this.modifiedPropsMap = new Map();
        // Es gibt neue und geänderte globale Variablen, die verwendet werden, wenn die Sandbox aktiviert wird. this.currentUpdatedPropsValueMap = new Map();

        const { addedPropsMap, currentUpdatedPropsValueMap, modifiedPropsMap } = dies;
        const fakeWindow = Objekt.create(null);
        const proxy = neuer Proxy(fakeWindow, {
            setze(Ziel, Eigenschaft, Wert) {
                wenn (!window.hasOwnProperty(prop)) {
                    // Wenn im Fenster keine Eigenschaft vorhanden ist, zeichnen Sie sie in der neu hinzugefügten Eigenschaft auf. // Debugger;
                    hinzugefügtPropsMap.set(Eigenschaft, Wert);
                } sonst wenn (!modifiedPropsMap.has(prop)) {
                    // Wenn das aktuelle Fensterobjekt diese Eigenschaft hat und nicht aktualisiert wurde, zeichnen Sie den Anfangswert dieser Eigenschaft im Fenster auf. const originalValue = window[prop];
                    geändertePropsMap.set(Eigenschaft, Originalwert);
                }
                // Die geänderten Eigenschaften und die geänderten Werte aufzeichnen currentUpdatedPropsValueMap.set(prop, value);
                //Setze den Wert auf das globale Fenster updateWindowProp(prop, value);
                gibt true zurück;
            },
            bekomme(Ziel, Eigenschaft) {
                Fenster zurückgeben[Eigenschaft];
            },
        });
        dieser.proxy = Proxy;
    }
}


const newSandBox = neue ProxySandbox('Proxy-Sandbox');
const proxyWindow = newSandBox.proxy;
ProxyWindow.a = "1"
console.log('Sandbox öffnen:', proxyWindow.a, window.a);
newSandBox.inactive(); //Sandbox deaktivieren console.log('Sandbox deaktivieren:', proxyWindow.a, window.a);
newSandBox.active(); //Sandbox deaktivieren console.log('Sandbox reaktivieren:', proxyWindow.a, window.a);

Lassen Sie uns den Code ausführen und die Ergebnisse ansehen

Auf diese Weise kann immer nur eine Sandbox gleichzeitig aktiv sein. Andernfalls werden die Variablen im globalen Objekt von mehr als zwei Sandboxes aktualisiert, was zu Konflikten bei globalen Variablen führt.

Implementierung einer Multi-Instanz-Sandbox basierend auf einem Proxy

Im Einzelinstanzszenario ist unser fakeWindow ein leeres Objekt, das keine Funktion zum Speichern von Variablen hat. Die von der Mikroanwendung erstellten Variablen werden am Ende tatsächlich im Fenster bereitgestellt, wodurch die Anzahl der gleichzeitig aktivierbaren Mikroanwendungen begrenzt wird.

Klasse MultipleProxySandbox {

    aktiv() {
        dies.sandboxRunning = true;
    }
    inaktiv() {
        this.sandboxRunning = falsch;
    }

    /**
     * Konstruktor * @param {*} Name Sandbox-Name * @param {*} Kontext gemeinsamer Kontext * @returns 
     */
    Konstruktor(Name, Kontext = {}) {
        dieser.name = Name;
        dieser.proxy = null;
        const fakeWindow = Objekt.create({});
        const proxy = neuer Proxy(fakeWindow, {
            set: (Ziel, Name, Wert) => {
                wenn (this.sandboxRunning) {
                    wenn (Objekt.Schlüssel(Kontext).enthält(Name)) {
                        Kontext[Name] = Wert;
                    }
                    Ziel[Name] = Wert;
                }
            },
            get: (Ziel, Name) => {
                // Gemeinsam genutzten Objekten Priorität einräumen if (Object.keys(context).includes(name)) {
                    Kontext zurückgeben[Name];
                }
                Ziel [Name] zurückgeben;
            }
        })
        dieser.proxy = Proxy;
    }
}

const Kontext = { Dokument: Fenster.Dokument };

const newSandBox1 = neue MultipleProxySandbox('Proxy-Sandbox 1', Kontext);
newSandBox1.active();
const proxyWindow1 = newSandBox1.proxy;

const newSandBox2 = neue MultipleProxySandbox('Proxy-Sandbox 2', Kontext);
newSandBox2.active();
const proxyWindow2 = newSandBox2.proxy;
console.log('Sind gemeinsam genutzte Objekte gleich?', window.document === proxyWindow1.document, window.document === proxyWindow2.document);

proxyWindow1.a = „1“; // Den Wert von Proxy 1 festlegen proxyWindow2.a = „2“; // Den Wert von Proxy 2 festlegen window.a = „3“; // Den Wert des Fensters festlegen console.log(„Ausgabewert drucken“, proxyWindow1.a, proxyWindow2.a, window.a);


newSandBox1.inactive(); newSandBox2.inactive(); // Beide Sandboxen sind deaktiviert proxyWindow1.a = „4“; // Den Wert von Proxy1 festlegen proxyWindow2.a = „4“; // Den Wert von Proxy2 festlegen window.a = „4“; // Den Wert von Fenster festlegen console.log(„Der nach der Deaktivierung ausgegebene Wert“, proxyWindow1.a, proxyWindow2.a, window.a);

newSandBox1.active(); newSandBox2.active(); // Erneut aktivieren proxyWindow1.a = '4'; // Den Wert von Proxy 1 festlegen proxyWindow2.a = '4'; // Den Wert von Proxy 2 festlegen window.a = '4'; // Den Wert des Fensters festlegen console.log('Der nach der Deaktivierung ausgegebene Wert', proxyWindow1.a, proxyWindow2.a, window.a);

Durch Ausführen des Codes werden die folgenden Ergebnisse erzielt:

Auf diese Weise kann immer nur eine Sandbox gleichzeitig aktiviert werden, wodurch eine Sandbox mit mehreren Instanzen erreicht wird.

Abschluss

Die oben genannten Methoden sind die am häufigsten verwendeten Sandbox-Implementierungsmethoden für Mikro-Frontends. Wenn wir sie in der Produktion verwenden möchten, müssen wir viele Urteile und Einschränkungen treffen. Im nächsten Artikel werden wir uns ansehen, wie das Micro-Frontend-Framework Qiankun die Sandbox über den Quellcode implementiert. Der obige Code befindet sich auf GitHub. Um ihn anzuzeigen, gehen Sie bitte zu js-sandbox

siehe

iframe src
ES6-Proxy

Damit ist dieser Artikel über verschiedene Möglichkeiten zur Implementierung einer Front-End-JS-Sandbox abgeschlossen. Weitere relevante Inhalte zu JS-Sandboxen finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • Quickjs kapselt JavaScript-Sandbox-Details
  • JavaScript-Sandbox-Erkundung
  • Ein kurzer Vortrag über JavaScript Sandbox
  • Eine kurze Diskussion über die Node.js-Sandbox-Umgebung
  • Einrichten einer sicheren Sandbox-Umgebung für Node.js-Anwendungen
  • Beispiel für den Sandbox-Modus beim Schließen der JS-Implementierung
  • Beispielanalyse des JS-Sandbox-Modus
  • Sicherheits-Sandbox-Modus des JavaScript-Entwurfsmusters
  • WebWorker kapselt JavaScript-Sandbox-Details

<<:  Das neueste grafische Tutorial zur Installation von MySQL 8.0.16 Winx64 unter Win10

>>:  Detaillierte Erklärung der Verwendung des Linux-Befehls „tee“

Artikel empfehlen

So verwenden Sie das Vue-Router-Routing

Inhaltsverzeichnis 1. Beschreibung 2. Installatio...

Eine Fallstudie zur MySQL-Optimierung

1. Hintergrund Auf jeder OLTP-Datenbankinstanz vo...

CSS transparenter Rahmen Hintergrund-Clip-Magie

In diesem Artikel wird hauptsächlich die wunderba...

Implementierung der Vue-Anmeldefunktion

Inhaltsverzeichnis Vorne geschrieben Anmeldeübers...

Überwachen Sie die Größenänderung eines DOM-Elements über Iframe

Ein während des Entwicklungsprozesses häufig auft...

Detaillierte Einführung in die Grundkonzepte von JS

Inhaltsverzeichnis 1. Eigenschaften von JS 1.1 Mu...

Wachstumserfahrung eines Webdesigners

<br />Vorab muss ich sagen, dass ich ein abs...

Ein vollständiges Tutorial zur Verwendung der Axios-Kapselung in Vue

Vorwort Heutzutage wird in Projekten häufig die A...

Reines HTML+CSS, um einen Tippeffekt zu erzielen

In diesem Artikel wird hauptsächlich der durch re...

border-radius ist eine Methode zum Hinzufügen abgerundeter Ränder zu Elementen

border-radius:10px; /* Alle Ecken sind mit einem ...

So simulieren Sie Netzwerkpaketverlust und -verzögerung in Linux

netem und tc: netem ist ein Netzwerksimulationsmo...

InnerHTML verstehen

<br />Verwandte Artikel: innerHTML HTML DOM ...