Progressive Web Apps – Wer braucht noch eine native App?



Beispiele zum Einstieg

Progressive Web Apps sind schon weiter verbreitet wie man denkt. Auch große, innovative Unternehmen Twitter, Airbnb, Spotify oder Tinder setzen auf Progressive Web Apps.

Abb. 1: Eine Auswahl von Progressive Web Apps [1]


Wer sich ein tolles Beispiel anschauen möchte, dem empfehle ich https://riorun.theguardian.com/ (auf mobile) zu testen. Nach einiger Zeit erscheint ein Popup, das fragt, ob man die App zum Startbildschirm hinzufügen möchte. Bestätigt man diese Abfrage, wird die PWA im Hintergrund auf dem Gerät installiert und ist ab sofort wie jede andere App auf dem Gerät verfügbar.

Abb. 2: Rio Run App von the Guardian [2]

Weitere tolle PWAs finden sich auf den Übersichtsseiten von:
https://pwa.rocks/
https://progressivewebapproom.com/

Historische Entwicklung

Kurz nach der Einführung des ersten iPhones hatte Steve Jobs schon die nächste Vision für sein Schmuckstück. Für ihn war nicht das Gerät an sich, viel mehr der Browser, der Hafen zum Tor der neuen Welt. Seiner Vision nach, sollte ein Smartphone bestenfalls nur noch einen Browser beinhalten. Entwickler können Apps über das Web bereitstellen, die sich vollständig in Safari integrieren lassen und sich selbstständig im Hintergrund updaten. [3]. Die Idee ist gut, allerdings, typisch Jobs, ihrer Zeit voraus. Die Erfolgsgeschichte des iPhones zwang Apple an den Rand der Kapazitäten, so dass vermutet werden kann, dass Apple schlichtweg keine Zeit und Ressourcen hatte das Thema PWA weiter zu verfolgen. Mit der Masse an Apps, die in den Store drang, hatte man mehr als genug zu tun. Die ersten, die nach Steve Jobs das Thema wieder aufgegriffen haben, waren Mozilla, mit der Veröffentlichung des FirefoxOS im Jahr 2013. Ein Betriebssystem, das es ermöglicht Web Apps als native Apps auf dem Endgerät laufen zu lassen. [3] In den Jahren 2006-2013 hat sich auch sonst viel getan: Endgeräte haben unfassbar an Performance gewonnen, die Webentwicklung hat JQuery und PHP hinter sich gelassen. Neue Möglichkeiten, neue Frameworks und ausgefuchste CSS-Kniffe ermöglichen seitdem eine völlig neue Form der User Experience im Web. Und Web? An wen denken wir da im Web? Genau, Google! Und Google hat ein Problem: Auch wenn sie selbst einen App Store betreiben und sich Kniffe haben einfallen lassen, wie sich Apps indexieren lassen, ist eine native App nicht in der Form auswertbar für ihre Suchmaschine, wie der Content einer Website. So fordert Alex Russells, Chrome Engineer bei Google, im Jahr 2015, die Einführung von Progressive Web Apps (PWA), eine Kombination aus aktueller Web Technologie mit den modernsten Möglichkeiten des Browsers [3]. Mit geschicktem Marketing und einer großen User-base überzeugte Google nun auch Apple wieder an dem Thema zu arbeiten, die 2017 mit der Unterstützung vom PWAs in Safari gleichzogen, komplettiert von Microsoft, dem schlafenden Giganten, der 2018 vollen Support für PWAs im Edge Browser verkündete [4]. Somit ist das große Trio vollends am Start und einer Erfolgsgeschichte von PWAs steht nichts mehr im Wege!

Was bedeutet Progressive Web App?

Vom Naming her leitet sich Progressive Web App von Web Apps mit Progressive Enhancement ab. Aber wo liegt da die Innovation? Bei der Entwicklung einer Web App denkt man heute an große Frameworks wie Angular, React, Vue und neueste Browser APIs wie die Geolocation API und viele mehr. Das ist auch bei einer PWA alles einsetzbar, denn Achtung: Eine PWA unterliegt keinem Framework und schließt auch kein Framework aus. Die Limitierung besteht lediglich durch die Gegebenheiten des Browsers.

Progressive Enhancement, zu deutsch progressive Verbesserung, beschreibt den Content-First Ansatz. Ziel dieser Optimierung ist es, dass der First-Meaningful-Paint beim Aufbau einer Seite möglichst früh geschieht. Sprich Content zuerst und weitere Layer wie Skripte, Style und Multimedia Files, werden nach und nach, je nach Netzwerkverbindung geladen und aufgebaut. Das ist aber keine Neuheit, die im Zuge von PWAs entstanden ist, sondern ein Ergebnis der letzten Jahre der Webentwicklung, Suchmaschinen- und User Experience-Optimierung.

Zusammengefasst lässt sich sagen, der Begriff Progressive Web App weist nicht auf die wahre Innovation hin. Web Apps, moderne Browser APIs und progressiver Aufbau wurden nicht mit PWAs erfunden. Frances Berriman, einer der Mitbegründer bei Google geht sogar so weit zu sagen: “The name isn’t for you… The name is for your boss, for your investor, for your marketeer.” [5] 

Worin liegt die Innovation?

Um auf die wahre Innovation hinter PWAs zu kommen, müssen wir zunächst die aktuellen Probleme in der Bereitstellung von Web Apps und nativen Apps betrachten. Web Apps sind eine tolle Sache, aber auf der geschäftlichen Seite fragt man sich, wie man den Benutzer an den Service binden kann. Soll man den Benutzer dazu auffordern, Lesezeichen im Browser zu machen, Social Media Aktivitäten zu verfolgen oder soll man eine weitere App entwickeln, die im Store ausgeliefert wird? Das alles erfordert zusätzliche Motivation beim Benutzer, den Content oder Service zu konsumieren und, auch wenn kein Medienbruch erfolgt, liegt an jeder Stelle der Customer Journey eine kleine Abbruchrate (Bounce Rate) vor. Es gibt auch Hinweise die bestätigen, dass die Downloadzahlen aus den App Stores immer kleiner werden, wobei der Traffic im Internet weiter zunimmt [6]. Dort ist der User und dort will er auch abgeholt werden. Was ist also naheliegender, als den Content, den man sowieso schon über das Web bereitstellt, für den Kunden dauerhaft zur Verfügung zu stellen? Ohne überflüssige Zwischenschritte in der Customer Journey? Das lässt sich zwar mit Web Apps realisieren, aber wenn der Benutzer keine Internetverbindung hat, klingelt auch bei den Entwicklern nichts in der Kasse.
Auch native Apps bringen ihre Probleme auf der geschäftlichen Seite mit. Hohe Entwicklungskosten und straffe Anforderungen der Store Betreiber, sorgen regelmäßig für Frust und lange Nächte beim Release des nächsten Updates. Payment-Optionen werden vordiktiert, undurchsichtige Indexierungen der Store Einträge erlauben keine freien Produktplatzierungen und Marketing. Wie soll man da aus der Masse an Apps herausstechen und sein eigenes Produkt sinnvoll bewerben? Von den Gebühren die Apple und Google beim App-Kauf abkassieren, mal ganz abgesehen. Neben der undurchsichtigen Indexierung ist auch die Auffindbarkeit außerhalb der App Stores problematisch. Zwar lassen sich Apps inzwischen in Suchmaschinen auffinden, werden aber vom Content her nicht so von Web Crawler erfasst, wie herkömmliche Websites. Keine direkte Indexierung bedeutet eine lange Customer Journey bis ein Kunde das Produkt entdeckt und das bedeutet hohe Kosten. 

Diesen Punkten versucht eine PWA entgegenzuwirken. Eine PWA kann online und offline, barrierefreie konsumiert werden. Der Zwang einer App-Installation ist nicht erforderlich, der Kunde kann den Content zunächst über das Web auffinden und konsumieren und sich jederzeit impulsiv für eine App-Installation entscheiden. 

Technik & Funktionsweise

Eine PWA funktioniert grundsätzlich wie eine gewöhnliche Web App. Man kann jede Web App als Basis nehmen, erweitert um Manifest, Service Worker und App Icon. Wichtig zu wissen ist, dass eine PWA nur mit https funktioniert und sie sich auch nur installieren lässt, wenn die Verbindung über https gesichert wurde. Dieser Zwang ist ein willkommener Vorteil, der ein bisschen Sicherheit ins World Wide Web bringt. 

Die nachfolgende Übersicht zeigt einen schematischen Aufbau und Zusammenhänge. In den nachfolgenden Abschnitten schauen wir uns die einzelnen Elemente im Detail an.

Abb. 3: Funktionsweise und Aufbau von PWAs [9]


Manifest

Das Manifest liefert die, für die Installation der App, notwendigen Metadaten. Es muss per Link-Element in den Head des HTML-Dokuments eingebunden werden. In Json formatiert liefert es u.a.:

  • App Name
  • App Beschreibung
  • App Icon in versch. Auflösungen für versch. Endgeräte und Browser
  • Informationen bzgl. des Urhebers
  • App-Scope
  • Start Modus

Eine vollständige Liste aller mögliche Attribute findet sich hier:
https://developer.mozilla.org/de/docs/Web/Manifest

App Scope

Die Property “start_url” beschreibt den Scope der App innerhalb der Domain. Über diesen Parameter wird auch definiert welche Seite beim Start der App angezeigt werden soll.

Start Modus

Die Property “display” beschreibt den Start-Modus der App. Die nachfolgende Bilderreihe zeigt die Auswirkungen der einzelnen Optionen. Mit der Standalone-Option kann man den Look einer nativen App perfekt imitieren. Über den Parameter Theme Color lässt sich zusätzlich die Gestaltung der Statusleiste beeinflussen.

Abb. 4: Browser Modes [7]

Service Worker

Der Service Worker ist der Hintergrunddienst einer PWA. Er ist verwandt mit dem Web Worker, läuft in einem eigenen Thread und erlaubt keine direkte DOM-Manipulation, sondern nur die Kommunikation über eine definierte Schnittstellen. 

Er ist auch bei geschlossener Anwendung lauffähig, legt sich schlafen und erwacht bei eintreffenden Informationen! Mit seiner Hilfe wird eine PWA offlinefähig. Seine Aufgabe ist es, alle Requests die aus dem eigenen Scope ins Netzwerk geschickt werden, abzufangen und zu beurteilen, ob er mögliche Anfragen aus dem eigenen Cache beantworten kann oder nicht. Technisch betrachtet fungiert er quasi als Pseudo-Proxy. In welchem Umfang Requests ins Netzwerk geschickt werden oder aus dem eigenen Cache beantwortet werden, liegt im ermessen des Entwicklers. So ist über das Install-Event des Service Workers, dass bei App-Installation getriggert wird, ein vollständiger Download des App-Contents möglich, was die App damit gänzlich offlinefähig machen würde. Über die Wake-Up Funktion werden Push Notifications auf dem Endgerät ermöglicht, womit man der User Experience einer nativen App wieder einen großen Schritt näher kommen kann. 

Weitere Informationen und eine Übersicht von möglichen Events auf die gelauscht werden kann, findet man unter:
https://developer.mozilla.org/de/docs/Web/API/Service_Worker_API/
Using_Service_Workers


Skandal – Sind Service Worker die neuen Cookies?

Das eine App einen Hintergrunddienst benötigt, ist aus der nativen App- Entwicklung betrachtet, nichts ungewöhnliches. Async-Tasks, Services, Background Tasks, etc. sind jedem Android- oder iOS-Developer ein Begriff. In der Entwicklung von gewöhnlichen Websites (keine Web Apps), sind Hintergrunddienste allerdings eher selten von Bedarf. Der Benutzer weiß inzwischen durch Aufklärungsmaßnahmen, dass der Besuch einer Website in seinem Browser Spuren hinterlässt, sei es Cache, Cookies usw. ABER und jetzt kommt der Skandal: Viele Benutzer wissen nicht (!), dass ihr Browser ebenso durch Service Worker belastet wird, die sich wie eine Zecke in den Browser schleichen, jederzeit aus dem Schlaf erwecken lassen und die die Performance des ganzen Gerätes durch Hintergrunddienste beeinträchtigen können! Wenn man den Artikel bis hierher verfolgt hat, könnte man nun meinen, das Service Worker nur für PWAs eingesetzt werden, aber das Web war schon immer dafür bekannt, dass man sämtliche Tricks ausnutzt, die irgendwie möglich sind. Ich kann an dieser Stelle nur jedem Chrome-Benutzer, egal ob auf Mobile oder Desktop empfehlen, mit dem nachfolgenden Link zu überprüfen, wieviele Service Worker sich unwissend in den eigenen Browser geschlichen haben. Eine vergleichbare Schnittstelle zum überprüfen der registrierten Service Worker bieten andere Browser derzeit nicht an, obwohl sie sie unterstützen! Über Service Worker lässt sich beispielsweise das heimliche Crypto Mining auf Kosten von unwissenden Benutzern realisieren.

Check:  chrome://serviceworker-internals

Push Notifications

Eine Push Notification, die über eine installierte PWA gesendet wurde, lässt sich nicht von einer nativen Notification unterscheiden. Auch jede Referenz auf den zugrundeliegenden Browser wird verborgen. Notifications lassen sich über den Service Worker triggern, der wiederum von App oder Server dazu angestoßen wird.

Abb. 5: PWA Push Notifications vs. native Notifications [9]

Splash Screen

Um dem Start einer App eine bessere User Experience zu verpassen, hat man sich entschlossen, dass eine PWA mit einem Splash Screen startet, während im Hintergrund der Content aufgebaut wird. So wird auf geschickte Art und Weise performance suggeriert und langweilt den Benutzer nicht mit einem Whitescreen. Splash Screens lassen sich nicht an- oder ausschalten, aber customizen. Per Default sind App Icon und App Name (Android) gesetzt.

Abb. 6: Splash Screen auf Android (links) und iOS (rechts) [8]

Optimize & Debug

Wer eine PWA programmieren und das beste an Performance herauskitzeln will, der findet in den Developer Tools des Chrome Browsers das beste Hilfsmittel. Über die Audit-Tests startet der Browser auf Knopfdruck eine Reihe an Tests, die eine PWA in Bezug auf Vollständigkeit (PWA-Anforderungen), Performance, Best Practises, Accessibility und Semantik (SEO) hin bewertet. Auf einer Skala von 1-10 wird ein Scoring ermittelt und aufgelistet wo Verbesserungspotential besteht.

Abb. 7: Audits in der Chrome Developer Console [9]

Exkurs – Browser APIs

Um sich bei Funktionalität und User Experience einer nativen App anzunähern, liefern moderne Browser heute schon jede Menge APIs, die man vielleicht gar nicht kennt. Darunter auch einige hardwarenahe Schnittstellen, die häufig noch gar keinen Gebrauch finden, aber die Zukunft mitprägen können. Um sicherzustellen, dass entsprechende APIs auch schon in weit verbreiteten Browsern implementiert sind, bietet sich an, diese vor Implementierung, mit Hilfe des Can I Use – Services zu prüfen.

Eine Auswahl eher unbekannter APIs:

  • Sensors API
    • Ambient Light, Proximity, Accelerometer, Magnetometer, Gyroskop
  • Battery Manager API
  • Web Payments API
  • Gamepad API
  • Geolocation (GPS) API
  • Vibration API
  • Web Speech API (Text-To-Speech, Speech-To-Text, Grammar)
  • Bluetooth API
  • Push Notification API
  • USB Device API
  • WebVR API
  • Indexed Database API
  • File System API


PWA vs. Native

Wir wir bisher erfahren haben, ermöglichen moderne Browser schon mehr Features, wie man vielleicht vermuten mag. Wenn man betrachtet, dass PWAs erst seit 2018 von allen großen Browsern unterstützt werden, kann man schlussfolgern, dass das ganze Thema noch in den Kinderschuhen steckt. Aber bereits jetzt, durch die herausragende Performance-Steigerung mobiler Endgeräte in den letzten Jahren, sind tolle Anwendungen im mobile Web möglich, so dass native Apps aus Kostengründen wohl immer unattraktiver werden, sofern keine native Performance für das Produkt erforderlich ist. Auch der PWA-Standard wird sich in den nächsten Jahren weiterentwickeln und immer mehr Möglichkeiten bieten. Es bahnt sich vielleicht eine spannende Welle der Veränderungen an, die den Mobile-App-Markt durcheinander bringen könnte!

Zum Abschluss eine Übersichtsmatrix die zur Entscheidungsfindung hilfreich sein kann. Die meisten Punkte erhält die Umsetzungsart, die am besten abschneidet.

Web AppPWANative App
Speicherbedarf auf dem Endgerät◼️◼️◼️
Offlinefähigkeit◼️◼️◼️◼️
Auffindbarkeit in Suchmaschinen◼️◼️◼️◼️◼️
Installation, Updates, Wartung, Versionierung◼️◼️◼️◼️◼️
Performance, Ladezeiten◼️◼️◼️
Sicherheit(kann)◼️◼️(kann)
Entwicklungskosten◼️◼️◼️
Natürlicher Traffic◼️◼️◼️◼️◼️
Hardwarenahe Features◼️◼️◼️◼️
User Experience(kann)(kann)◼️◼️


In 10 Schritten zur ersten PWA

Hier noch einen Quickguide zur ersten Progressive Web App:

  1. Web App programmieren
  2. Sicher stellen, dass die Kommunikation zum Server ausschließlich über https läuft
  3. Web Manifest anlegen mit mindestens:
    • Scope
    • Start URL
    • App Icon und App Name
  4. Web Manifest im Head des HTML-Dokuments einbinden
  5. ServiceWorker.js (o.ä.) in root Folder der PWA anlegen
  6. Service Worker im JS der Web App registrieren
  7. ServiceWorker.js je nach Bedarf implementieren
  8. Mit Audits testen ob alle empfohlenen Qualitätskriterien einer PWA erreicht sind
  9. Deployen 🙂



Quellenverzeichnis

[1] Great examples of progressive web apps in one room
https://progressivewebapproom.com/
Zugegriffen am 05.08.2019, 22:08 Uhr

[2] Rio Run App von the Guardian,
https://riorun.theguardian.com/
Zugegriffen am 05.08.2019, 22:08 Uhr

[3] Wikipedia
https://en.wikipedia.org/wiki/Progressive_web_applications
Zugegriffen am 05.08.2019, 22:08 Uhr

[4] Welcoming Progressive Web Apps to Microsoft Edge and Windows 10
https://blogs.windows.com/msedgedev/2018/02/06/welcoming-progressive-web-apps-edge-windows-10/
Zugegriffen am 05.08.2019, 22:08 Uhr

[5] Naming Progressive Web Apps
https://fberriman.com/2017/06/26/naming-progressive-web-apps/
Zugegriffen am 05.08.2019, 22:08 Uhr

[6] Why Build Progressive Web Apps
https://developers.google.com/web/ilt/pwa/why-build-pwa
Zugegriffen am 05.08.2019, 22:08 Uhr

[7]  Progressive Web App Challenges
https://www.slideshare.net/grigs/progressive-web-app-challenges
Zugegriffen am 05.08.2019, 22:08 Uhr

[8] Progressive Web App Splash Screens
https://medium.com/@applification/progressive-web-app-splash-screens-80340b45d210
Zugegriffen am 05.08.2019, 22:08 Uhr

[9] Eigene Leistung

JavaScript Performance optimization with respect to the upcoming WebAssembly standard

Written by Tim Tenckhoff – tt031 | Computer Science and Media

1. Introduction

Speed and performance of the (worldwide) web advanced considerably over the last decades. With the development of sites more heavily reliant on JavaScript (JS Optimization, 2018), the consideration of actions to optimize the speed and performance of web applications grows in importance. This blogpost aims to summarize practical techniques to enhance the performance of JavaScript applications and provides a comparative outlook regarding the upcoming WebAssembly standard.

Let´s jump into hyperspeed…

  1. Introduction
    1. JavaScript 
    2. Why optimize? 
  2. How can the JavaScript Speed and Performance be increased?
    1. Interaction with Host Objects
    2. Dependency Management
    3. Event Binding
    4. Efficient Iterations
    5. Syntax 
  3. Is WebAssembly replacing JavaScript?
  4. References

JavaScript

Back in the days, in 1993 a company called Netscape was founded in America. Netscape aimed to exploit the potential of the burgeoning World Wide Web and created their own web browser Netscape Navigator (Speaking JavaScript 2014). As Netscape realized in 1995, that the web needed to become more dynamic, they decided to develop a scripting language with a syntax similar to Java’s to rule out other existing languages. In May 1995, the prototype of this language was written by the freshly hired software developer Brendan Eich within 10 days. The initial name of the created code was Mocha, which was later changed by the marketing to LiveScript. In December 1995, it was finally renamed to JavaScript to benefit from Java’s popularity (Speaking JavaScript 2014).

Today, every time the functionality of a web page exceeds to just display static content, e.g. by timely content updates, animated graphics, an interactive map or the submission of an input formular, JavaScript is probably involved to solve the given complexity. Thus it is the third layer cake of the standard web technologies, which adds dynamic behavior to an existing markup structure (e.g. defining paragraphs, headings, and data tables in HTML), customized with a definition of style rules (e.g. defining paragraphs, headings, and data tables in CSS). If a web browser loads a webpage, the JavaScript code is executed by the browser’s engine, after the HTML and CSS have been assembled and rendered into a web page (JavaScript 2019). This ensures that the content of the required page is already in place before the JavaScript code starts to run, as seen in Figure 1.

Figure 1: The execution of HTML, CSS and JavaScript in the Browser (JS Optimization, 2018)

The script language often abbreviated as JS includes a curly-bracket syntax, dynamic typing, object orientation, and first-class functions. Initially only implemented in and for web browsers on the client side, today’s JavaScript engines are now embedded in many other types of host software, including web servers (e.g. NodeJS), databases, or other non-web use-cases (e.g. PDF software).

Why optimize?

Speaking about the performance optimization in networks, most developers think about it in terms of the download and execution cost – sending more bytes of JavaScript code takes longer, depending on the users internet connection (JS Optimization 2018). Therefore, it can be said beforehand, that it generally makes sense to reduce the transferred network traffic, especially in areas where the available network connection type of users might not be 4G or Wi-Fi. But one of JavaScript’s heaviest costs regarding the performance is also the parse/compile time. The Google Chrome browser visualizes the time spend in these phases in the performance panel as seen in Figure 2. But why is it even important to optimize this time?

Figure 2: Parse and compile time of JavaScript (JS Optimization 2018)

The answer is, that if more time is spent parsing/compiling, there might be a significant delay in the user’s interaction with the website. The longer it takes to parse and compile the code, the longer it takes until a site becomes interactive. According to a comparison in an article by Google Developer Addy Osmani, JS is more likely to negatively impact a pages interactivity than other equivalently sized resources (JS Optimization 2018). As an example seen in Figure 3, the resource processing time of 170kb JavaScript bytes and the same amount of JPEG bytes require the same amount of 3,4 seconds network transmission time. But as the resource processing time to decode the image (0,064 seconds) and rasterize paint in the image (0,028 seconds) are relatively low, it takes much longer to parse the JavaScript code (~2 seconds) and execute it (~1,5 seconds). Due the fact that website users differ not only regarding their provided network connection, but also in the hardware they have, older hardware may also have a bad influence on increased execution time. This shows how much more JS can potentially delay the interactivity of a website due to parse, compile and execution costs and proves the need for optimization.

Figure 3: The difference between JavaScript and JPEG resource processing (JS Optimization 2018)

2. How can the JavaScript Speed and Performance be increased?

The question appearing at this point is the following: What needs to be practically done during the development of JavaScript to optimize websites or web applications regarding speed and interactivity. The depths of the world wide web expose several blogs and articles about this kind of optimization. The following section aims to sum up found techniques categorized by the underlying performance problem:

Interaction with Host Objects

The interaction with “evil” host objects needs to be avoided as Steven de Salas says in his blog (25 Techniques 2012)

As described before, JavaScript code is compiled by the browser’s scripting engine to machine code – offering an extraordinary increase in performance and speed. However, the interaction with host objects (in the browser) outside this native JavaScript environment raises a loss in performance, especially if these host objects are screen-rendered DOM (Document Object Model) objects (25 Techniques 2012). To prevent this, the interaction with these evil host objects needs to be minimized. Let’s take a look at how this can be done.

Rotation via CSS

A first useful approach is the usage of CSS-classes for DOM animations or interactions. Unlike JavaScript code, solutions like e.g. CSS3 Transitions, @keyframes, :before, :after are highly optimized by the browser (25 Techniques 2012). As shown above, the rotation of the small white square can either be animated by addressing the id via document.getElementById(“rotateThisJS”), triggered by a button starting the rotate() function below…

Figure 4: Rotation via JavaScript (How not to…)

…or by adding a CSS class (as seen in Figure 5) to the <div> element which works much more efficiently, especially if the respective website contains several animations. Except from animations, CSS is also able to handle interactions like e.g. hovering elements, thus it can be said, that the way JavaScript is optimized this time is not to use it in certain cases.

Figure 5: Animation via CSS

Speaking of selectors to pick DOM elements, the usage of jQuery allows a highly specific selection of elements based on tag names, classes and CSS (25 Techniques 2012). But according to the online blog article by Steven de Salas, it is important to be aware that this approach involves the potential of several iterations through underlying DOM elements to find the respective match. He states that this can be improved by picking nodes by ID. An example can be seen in Figure 6:

Figure 6: Different ways of DOM element selection

What also increases the (DOM interaction) performance, is to store references to browser objects during instantiation. If it can be expected, that the respective website is not going to change after instantiation, references to the DOM structure should be stored initially, when the page is created not only when they are needed. It is generally a bad idea to instantiate references DOM objects over and over again. That’s why it is rather advisable to create few references to objects during instantiation which are needed several times (25 Techniques 2012). If no reference to a DOM object has been stored and needs to be instantiated within a function, a local variable containing a reference to the required DOM object can be created. This speeds up the iteration considerably as the local variable is stored in the fastest and most accessible part of the stack (25 Techniques 2012).

The general amount of DOM-elements is also a criteria with respect to the performance. As the time, used for changes in the DOM is proportional to the complexity of the rendered HTML, this should also be considered as an important performance factor (Speeding up 2010).

Another important aspect regarding the DOM interaction is to batch (style) changes. Every DOM change causes the browser to do a re-rendering of the whole UI (25 Techniques 2012). Therefore, it should be avoided to apply each style change separately. The ideal approach to prevent this, is to do changes in one step, for example by adding a CSS class. The different approaches can be seen in Figure 7 below.

Figure 7: How to batch changes in DOM

Additionally it is recommended to build DOM elements separately before adding them to a website. As said before, every DOM requires a re-rendering. If a part of the DOM is built “off-line” the impact of appending it in one go is much smaller (25 Techniques 2012). Another approach is to buffer DOM content in scrollable <div> elements and to remove elements from the DOM that are not displayed on the screen, for example, outside the visible area of a scrollable <div>. These nodes are then reattached if necessary (Speeding up 2010).

Dependency Management

Figure 8: Checking the dependency Management of www.stuttgart.de

Looking at different pages in the www, e.g. the cities’s website of Stuttgart, it can be observed that the screen rendering is delayed for the user until all script dependencies are fully loaded. As seen in Figure 8, some dependencies cause the delayed download of other dependencies that in return have to wait for each other. To solve this problem the active management and reduction of the dependency payload are a core part of performance optimization.

One approach to do so, is to reduce the general dependency on libraries to a minimum (25 Techniques 2012). This can be done by using as much in-browser technology as possible. For example the usage of document.getElementById(‘element-ID’) instead of using (and including) the jQuery library. Before adding a library to the project, it makes sense to evaluate whether all the included features are needed, or if single features can be extracted from the library and added separately. If this is the case, it is of course important to check whether the adopted code is subject to a license – to credit and acknowledge the author is recommended in any case (25 Techniques 2012).

Another important approach is the combination of multiple JavaScript files to bundled ones. The reason behind this is, that one network request with e.g. 10kb of data is transferred much faster than 10 requests with 1kb each (25 Techniques 2012). This difference is caused by lower bandwidth usage and network latency of the single request. To save additional traffic, the combined bundle files can also be minified and compressed afterwards. Minification tools, such as UglifyJS or babel-minify remove comments and whitespacing from the code. Compression tools as e.g. gzip or Brotli are able to compress text-based resources to smaller memory size. (JS Optimization 2018)

A further way to optimize the dependency management, is the usage of a post-load dependency manager for libraries and modules. Tools like Webpack or RequireJS allow, that the layout and frame of a website appears before all of the content is downloaded, by post-loading the required files in the background (JS Optimization 2018). This gives users a few extra seconds to familiarise themselves with the page (25 Techniques 2012).

By maximizing the usage of caching, the browser downloads the needed dependencies only at the first call and otherwise accesses the local copy. This can be done by manually adding eTags to files that need to be cached, and putting *.js files to cache into static URI locations. This communicates the browser to prefer the cached copy of scripts for all pages after the initial one (25 Techniques 2012).

Event Binding

To create interactive and responsive web applications, event binding and handling is an essential part. However, event bindings are hard to track due to their ‘hidden’ execution and can potentially cause performance degradation e.g. if they are fired repeatedly (25 Techniques 2012). Therefore it is important to keep track of the event execution throughout various use cases of the developed code to make sure that events are not fired multiple times or bind unnecessary resources (25 Techniques 2012).

To do so, it is especially important to pay attention to event handlers that fire in quick repetition. Browser events such as e.g. ‘mouse move’ and ‘resize’ are executed up to several hundred times each second. Thus, it is important to ensure that an event handler that reacts to one of these events can complete in less than 2-3 milliseconds (25 Techniques 2012). The box below visualizes the amount of events that are fired when the mouse is moved over an element.

hover me!

Another important point that needs to be taken care of, is the event unbinding. Every time an event handler is added to the code, it makes sense to consider the point when it is no longer needed and to make sure that it stops firing at this point. (Speeding up 2010) This avoids performance slumps through handlers that are bound multiple times, or events firing when they are no longer needed. One good approach to prevent this, is the usage of once-off execution constructs like jQuery.one() or manually adding/coding the unbind behavior at the right place and time (25 Techniques 2012). The example below, shows the usage of jQuery.one – an event binding on each p element that is fired exactly once and unbinds itself afterwards. The implementation of this example can be seen in Figure 9.

Click the boxes to trigger a jQuery.one event!
Fired once. Also fired once. This is also fired only once.
Figure 9: The usage of jQuery.one()

A last important part of the event binding optimization is to consider and understand the concept of event bubbling. A blog article by Alfa Jango describes the underlying difference between .bind(), .live(), and .delegate() events (Event Bubbling 2011).

Figure 10: Propagation of a click event through the DOM (Event Bubbling 2011)

Figure 10 shows what happens if e.g e a link is clicked that fires the click event on the link element, which triggers functions that are bound to that element’s click event: The click event propagates up the tree, to the next parent element and then to each ancestor element that the click event was triggered on one of the descendent elements (Event Bubbling 2011). Knowing this, the difference between the jQuery functions bind(), live() and delegate() can be explained:

.bind()

jQuery scans the entire document for all $(‘a’) and binds the alert function to each of these click events.

.live()

jQuery binds the function to the $(document) tag including the parameters ‘click’ and ‘a’. If the 
event is fired, it checks if both parameters are true, then executes the function.

.delegate()

Similar to .live(), but binds the handler to a specific element, not the document.root.

The article says that .delegate() is better than .live(). But why?

$(document).delegate(‘a’, ‘click’, function() { blah() });

$(‘a’).live(‘click’, function() { blah() });

According to the blog entry (Event Bubbling 2011), delegate() can be preferred for two reasons:

Speed: $(‘a’) first scans for all a elements and saves them as objects, this consumes space and is therefore slower.

Flexibility: live() is linked to the object set of $(‘a’) elements, although it actually acts at the $(document) level..

Efficient Iterations

The next topic is the implementation of efficient iterations. As seen in Figure 11 below, the execution time for string operations grows exponentially during long iterations (String Performance 2008). This shows why iterations can often be the reason for performance flaws. Therefore it always makes sense to get rid of unnecessary loops, or calls inside of loops (25 Techniques 2012) .

Figure 11: Comparative String Performance Analysis (String Performance 2008)

One technique to avoid unnecessary loops, is to use JavaScript indexing. Native JavaScript objects can be used to store quick-lookup indexes to other objects, working in a similar way to how database indexes work (25 Techniques 2012). As seen in Figure 12 below it also speeds up finding objects by using e.g. the name as an index. This is highly optimized and avoids long search iterations.

Figure 12: Finding Objects by JavaScript indexing

Additionally it is always a good idea to use native JavaScript array functions such as push(), pop() and shift(), especially working with arrays. These functions also have a small overhead and are closely connected to their assembly language counterparts (25 Techniques 2012).

The difference between reference and primitive value types, also comes up in terms of efficient iterations. Primitive types such as String, Boolean or Integer are copied if they are handed over to a function. Reference types, such as Arrays, Objects or Dates are handed over as a light-weight reference. This knowledge should be considered if a reference is handed over to a function, running in an iteration: Obviously, it is better to avoiding frequent copying of primitive types and pass lightweight references to these functions.

Syntax

A final option to consider for optimization is the usage of correct JavaScript syntax. Writing simple function patterns without being familiar to advanced native ECMAScript can lead to inefficient code. It is recommendable to try to stay up to date and learn how to apply these constructs.

One easy example (25 Techniques 2012) regarding this, is to prefer the usage of native, optimized constructs over self-written algorithms: Functions as e.g. Math.floor()or new Date().getTime() for timestamps don’t need to be rewritten. The operator === instead of == provides an optimized, faster type-based comparison (25 Techniques 2012). Furthermore, the switch statement can be used instead of long if-then-else blocks to provide an advantage during compilation, to name just a few examples.

3. Is WebAssembly replacing JavaScript?

On the 17th of June 2015, Brendan Eich (the creator of JavaScript) announced a new project that aims to make it easier to compile projects written in languages like C and C++ to run in browsers and other web related JavaScript environments (Why we need Web Assembly). The developing team consists of members from Apple, Google, Microsoft, Mozilla and others collective under the name of the W3C WebAssembly Community Group (Why we need Web Assembly). A blog article by Eric Elliot comments on these release announcements and states that „the future of the web platform looks brighter than ever“ (What is WebAssembly?).

But what exactly is WebAssembly? Elliot further explains that WebAssembly, often shortened as WASM, is a new language format. The code defines an AST (Abstract Syntax Tree) represented in a binary format that can be edited/developed as readable text. The instruction format has been built to compile languages such as C, C++, Java, Python and Rust and allows the deployment on the web and in server applications. Through WebAssembly it is now possible to run the respective code on the web at a native speed (WASM replace JS 2018). But WASM is also an improvement to JavaScript: The performance critical code that needs to be optimized can be implemented in WASM and imported like a standard JavaScript module (What is WebAssembly?). The blog entry by Elliot additionally explains that WASM is also an improvement for browsers. Through the fact, that browsers will be able to understand the binary code that can be compressed to smaller files than currently used Javascript files, smaller payloads would lead to faster delivery and make websites run faster (What is WebAssembly?).

But listing all these advantages, does WebAssembly have the potential to replace JavaScript in the nearest future? And is WebAssembly compilation really so much faster? A blog article from Winston Chen compares the performance of WebAssembly vs JavaScript and comes to a surprising result (WASM vs JS 2018). The performed experiment involved several implementations of matrix multiplications as a simple and computationally intensive way to compare JavaScript and C++ (in WASM). In summary it can be said that JavaScript performed better than WebAssembly on smaller array sizes and WebAssembly outperformed JavaScript on larger ones. Chen concludes, that outgoing from his results, JavaScript is still the best option for most web applications. According to him, Web Assembly was therefore “best used for computationally intense web applications, such as web games” (WASM vs JS 2018).

Among different articles throughout the internet, dealing with the question whether WASM is going to replace JavaScript, it is not possible to find a precise answer or prediction. But Brendan Eich, the creator of JavaScript himself, finds a relatively clear answer to the question if he was trying to KILL JavaScript (by developing WASM): “…We’re not killing JavaScript. I don’t think it’s even possible to kill JavaScript”(Why we need wasm 2015). The JavaScript ecosystem currently supports all major browser and most developers write libraries and frameworks in it (e.g. React, Bootstrap or Angular)(WASM replace JS 2018). In order to overtake JavaScript, any competitor (as WASM) would need to provide replacement options for all these libraries. Furthermore, it is not easily feasible to replace the existing code base of JavaScript based projects. With growing popularity in calculation intense projects as browser-based games, WebAssembly can possibly decrease the market share of JavaScript, but is not able to replace these already existing JS applications. It can be said, that the native speed improvements of WebAssembly are rather a complementation of the existing JavaScript features (WASM replace JS 2018). By using both, (e.g. by using WebAsembly run alongside JS using WASM JavaScript APIs) developers can benefit from the flexibility of JS and the native speed advantages of WASM in combination. Wrapping this up, the creator was right and is very unlikely that WASM is going to overtake JavaScript – still the single, dominating language of the web.

4. References

Speaking JavaScript 2014, Axel Rauschmeyer, Speaking JavaScript: An In-Depth Guide for Programmers
Chapter 4. How JavaScript Was Created
[Accessed 24 July 2019].

WebAssembly 2015, Eric Elliot, What is WebAssembly? [Online]
Available at: https://medium.com/javascript-scene/what-is-webassembly-the-dawn-of-a-new-era-61256ec5a8f6
[Accessed 28 July 2019]. 

Why we need wasm 2015, Eric Elliot, Why we Need WebAssembly [Online]
Available at: https://medium.com/javascript-scene/why-we-need-webassembly-an-interview-with-brendan-eich-7fb2a60b0723
[Accessed 28 July 2019]. 

WASM replace JS 2018, Vaibhav Shah, Will WebAssembly replace JavaScript? [Online]
Available at: https://dev.to/vaibhavshah/will-webassembly-replace-javascript-or-will-wasm-make-javascript-more-valuable-in-future-5c6e
[Accessed 28 July 2019]. 

WASM vs JS 2018, Chen Winston, Performance Testing Web Assembly vs JavaScript [Online]
Available at: https://medium.com/samsung-internet-dev/performance-testing-web-assembly-vs-javascript-e07506fd5875
[Accessed 27 July 2019]. 

25 Techniques 2012, Steven de Salas, 25 Techniques for Javascript Performance Optimization [Online]
Available at: https://desalasworks.com/article/javascript-performance-techniques/
[Accessed 27 July 2019].

JS Optimization 2018, Addy Osmani, JavaScript Start-up Optimization [Online]
Available at: https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/javascript-startup-optimization/
[Accessed 27 July 2019].

JavaScript 2019, Chris David Mills (MDN web docs), What is JavaScript? [Online]
Available at: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/what_is_javascript/
[Accessed 27 July 2019].

Speeding up 2010, Yahoo Developer Network, Best Practices for Speeding Up Your Web Site [Online]
Available at: https://developer.yahoo.com/performance/rules.html#min_dom/
[Accessed 27 July 2019].

String Performance 2008, Tom Trenka, String Performance: an Analysis [Online]
Available at: https://www.sitepen.com/blog/string-performance-an-analysis/
[Accessed 27 July 2019].

Event Bubbling 2011 Alfa Jango, THE DIFFERENCE BETWEEN JQUERY’S .BIND(), .LIVE(), AND .DELEGATE() [Online]
Available at: https://www.alfajango.com/blog/the-difference-between-jquerys-bind-live-and-delegate/
[Accessed 24 July 2019].