Reaktivität mit JavaScript Proxy

CSS JavaScript

Demo Code auf GitHub

Was ist ein JavaScript Proxy?

Mit dem JavaScript Proxy können Daten-Objekte mit einem "Vermittlungs-Objekt" erweitert werden. Der Proxy kann dabei bestimmte Operationen abfangen die auf das Objekt und dessen Properties angewendet werden. Dazu zählen zum Beispiel das Lesen einer Eigenschaft (get) oder das Verändern einer Eigenschaft (set). Die Funktionalität lässt sich nutzen um zum Beispiel reaktive Objekte zu erzeugen, wie man es aus bekannten JavaScript-Framework wie React oder Vue kennt. Nur dass man mit Proxy kein Framework benötigt. Es funktioniert nativ im Browser.


let proxy = new Proxy(target, handler);
  • target ist dabei das Objekt auf welches der Proxy angewendet wird.
  • handler enthält Methoden für die Manipulation des Objektes. Hier wird definiert wie das Objekt (target) behandelt werden soll.

Anwendungsbeispiel


let product = {
    id: 1,
    name: 'T-Shirt',
    size: 'L',
    price: 9.99,
    stock: 10,
};

let proxy = new Proxy(product, {
    get(target, property) {
        console.log(`read property: ${property} with value: ${target[property]}`);
        return target[property];
    },
    set(target, property, value) {
        console.log(`write property: ${property} with value: ${value} and old value: ${target[property]}`);
        target[property] = value;
        return true;
    },
});

Wir definieren ein JavaScript-Objekt product mit den Eigenschaften id, name, size, price und stock. Dann wenden wir einen Proxy auf dieses Objekt an. Jedes Mal wenn eine Eigenschaft von product ausgelesen oder verändert wird, fängt der Proxy diesen Vorgang ab und gibt uns eine Ausgabe in die Konsole.

Wenn eine Eigenschaft von product verändert wird, greift die Set-Methode des Proxy und zeigt eine Ausgabe in der Browser-Konsole


product.stock = 12;

Das Gleiche Prinzip gilt auch für das Auslesen der Property-Werte. Wenn zum Beispiel per console.log() ein Property-Wert ausgelesen wird, greift die Proxy-Methode get.


console.log(product.stock);

Der Zugriff auf eine mit Proxy ausgewiesene Methode wird als Trap bezeichnet (engl. für Falle). Der Proxy agiert praktisch als Mittelsmann zwischen dem Datenobjekt und den Zugriffen darauf. Damit lassen sich grundlegende Operationen manipulieren, wie Property-Zuweisungen, Funktions- und Objektzugriffe. Es lassen sich auf Validierungen umsetzen.


let proxy = new Proxy(product, {
    get(target, property) {
        if ('stock' === property) {
            if (!Number.isInteger(value)) {
                throw new TypeError('The stock value is not an integer');
            }
            console.warn('Sorry the product is out of stock');
        }
        return target[property];
    },
});

Die Zuweisung product.stock = 'test'; wirft nun eine Exception: "Uncaught TypeError: The stock value is not an integer". Diese können wir nutzen um eine bessere Datenintegrität zu geährlisten.

JavaScript Proxy für Reaktivität nutzen

Jetzt fragt man sich natürlich wie man das Ganze für Reaktivität nutzen kann. Das reaktive Objekt soll andere Komponenten der Anwendung automatisch informieren, wenn sich bestimmte Werte ändern. Man spricht dabei von einer reaktiven Anwendung oder data-driven-Anwendung. Sobald sich an den Daten etwas ändert, werden bestimmte Teile der Anwendung über die Änderung informiert und die Komponente kann zum Beispiel neu gerendert werden. Wir nutzen dafür ein Event-Mechanismus den wir bereits in diesem Tutorial beschrieben haben: Ein EventBus mit JavaScript erstellen


let proxy = new Proxy(product, {
    get(target, property) {
        EventBus.trigger('PROPERTY_GET', {
            data: {
                property: property,
                value: target[property]
            }
        });
        return target[property];
    },
    set(target, property, value) {
        EventBus.trigger('PROPERTY_SET', {
            data: {
                property: property,
                value: value,
                oldValue: target[property]
            }
        });
        target[property] = value;
        return true;
    },
});

Immer wenn auf eine Property von product zugegriffen wird, wird das Event PROPERTY_GET getriggert. Wenn eine Property geändert wird, feuert das Event PROPERTY_SET. Andere Komponenten können darauf hören.


EventBus.on('PROPERTY_SET', ({ data }) => {
    console.log(`PROPERTY_SET: ${data.property} => ${data.value}`);
    if ('price' === data.property) {
        alert(`The product price has been changed: ${data.value}`);
    }
});

Events logisch nutzen

Neben dem Lesen und Schreiben von Properties, kann das Event-Prinzip auch für logische Operationen genutzt werden. Beispielsweise kann geprüft werden ob ein Produkt nicht mehr verfügbar ist (out of stock). Komponenten in unserer Anwendung können darauf reagieren und zum Beispiel eine Warnmeldung anzeigen. Es können auch mehrere Komponenten gleichzeitig auf das Event reagieren.


let proxy = new Proxy(product, {
    set(target, property, value) {
        ...

        if (property === 'stock') {
            if (0 === target[property]) {
                EventBus.trigger('PRODUCT_OUT_OF_STOCK', {
                    data: {
                        property: property,
                        value: value,
                        oldValue: target[property]
                    }
                });
            }
        }

        return true;
    },
});

Mehr Lesen



Artikel teilen