Tutoriel pour découvrir les principes d'une application web progressive : application avec la boite à outils GWT

Si vous faites ou vous avez fait du développement Web durant les dernières années, vous avez probablement dû entendre le terme « Progressive Web Application » une tonne de fois, et vous allez probablement en entendre parler davantage dans les années à venir. Vous vous êtes demandé quelle est exactement la définition d'une PWA, comment en identifier une, et comment en développer. Selon le dictionnaire, le terme « progressive » est associé à quelque chose qui s'améliore au fil du temps, mais comment cela s'applique-t-il à une application Web ? Nous ne savons pas vraiment. PWA semble être un mot inventé par Google pour faire le buzz, et ne semble pas correspondre à ce que les PWA sont vraiment. Les PWA ont été définies par Alex Russel comme « des sites Web qui ont pris les bonnes vitamines ». Pour simplifier, nous allons commencer par dire que les PWA sont des applications Web qui se fondent dans leur environnement : elles peuvent jouer parfaitement le rôle d'applications natives en mode mobile, mais aussi le rôle d'applications Web régulières.

Pour réagir au contenu de cet article, un espace de dialogue vous est proposé sur le forum. Commentez Donner une note à l'article (5)

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Le pourquoi des PWA

Si avez plusieurs versions (IOS, Android, Web…) d'une même application, vous avez probablement dû constater l'effort requis pour maintenir et faire évoluer toutes les versions. Avec les PWA, il n'y a qu'une seule application qui fonctionne pour toutes les plates-formes, et qui est accessible à partir d'un lien dans le navigateur. Les PWA doivent en principe être conçues en utilisant une approche « Mobile First ». Elles peuvent être installées, mais marchent aussi bien comme des sites réguliers. Google a créé un site web dédié aux PWA et présente différents cas d'entreprises qui ont profité de la conversion de leurs applications grâce à des PWA.

II. Les caractéristiques des PWA

Dans une de ses présentations, Rob Dodson, un developer advocate chez Google, a mis en évidence les différentes caractéristiques d'une PWA :

  • responsive : s'adapte aux dispositifs ;
  • se charge rapidement : optimisée pour se charger ou se peindre rapidement ;
  • utilisable hors-ligne : l'utilisation des services workers pour la mise en cache de l'application et de son contenu afin de permettre l'utilisation de l'application en mode hors connexion ou lorsque la connectivité réseau est lente ;
  • installable : l'application peut être installée dans l'écran d'accueil (comme une application native) ;
  • engageante : l'application informe l'utilisateur des mises à jour, des nouvelles… en utilisant les notifications push.

Maintenant que nous savons à quoi ressemble une application Web progressive, nous pouvons commencer à chercher les différents outils qui peuvent nous aider à rendre notre application GWT progressive. Le but de ce tutoriel n'est pas de répéter ce qui a déjà été dit sur les PWA, mais de l'appliquer dans le contexte d'une application GWT.

II-A. Recettes GWT pour les PWA

II-A-1. Responsive

Pour rendre votre application GWT responsive, plusieurs options s'offrent à vous. Si vous avez des compétences de conception, vous pouvez faire votre propre code GWT et CSS. Sinon, Bootstrap pour GWT est la première chose qui vient à l'esprit. Une autre alternative est le framework GWTMaterialDesign qui fournit des éléments « material design » prêts à être utilisés dans votre application. Enfin, gwt-polymer-elements, la version GWT de Polymer, fournit également des composants Web responsive, et peut être pratique dans la conception de la construction d'une application responsive.

II-A-2. Chargement rapide

Pour réduire le temps de la première peinture, il y a un certain nombre de choses qui peuvent être faites. Tout d'abord, le Code Splitting peut être utilisé pour réduire la taille du fichier du module GWT. Il divise le module en fragments permettant au module GWT de télécharger uniquement ceux nécessaires au premier démarrage. Deuxièmement, la méthode de l'app shell, comme définie par les spécifications, peut être appliquée à une application GWT. Cela peut être fait en identifiant les éléments et données statiques dans le code Java de l'application et en les mettant directement dans le point d'entrée .html.

Par exemple :

une pratique courante parmi les développeurs GWT est de laisser le .html vide et d'initialiser les vues depuis l'application :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
<body>                                  

</body>
``` 

```java
AppMainView view = AppMainView();

RootPanel.get().add(view);

Bien qu'il n'y ait rien de mal avec cette pratique, elle peut causer le ralentissement du temps de chargement de l'application, car le fichier .js du module aura plus d'instructions. Comme remède, nous pouvons essayer d'identifier tous les éléments statiques dans nos vues et de les mettre dans le .html, puis nous pouvons charger les vues individuelles depuis le code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
<body>

   <div id="appShell">
       <img src="logo.png"></img>
       <div id="menu">
       </div>
       <div id="mainContent">
       </div>
   </div>

</body>
 
Sélectionnez
1.
2.
3.
4.
5.
6.
//...
 MenuView menu = new MenuMeview();
 ContentView content = new ContentView();

 RootPanel.get("menu").add(menu);
 RootPanel.get("mainContent").add(content);

Cela est, bien sûr, un exemple simplifié. Nous avons vu jusqu'à présent comment le code splitting et la méthode app shell peut réduire le temps de chargement d'une application GWT. L'attribut async du tag script offert par HTML5 peut aussi rendre le chargement de la page plus rapide. L'attribut async indique au navigateur de ne pas se bloquer pendant que le script se charge et s'exécute. Ce n'est pas vraiment spécifique à GWT, mais on sait que la taille d'un module GWT peut être considérable, et donc cela peut s'avérer utile.

 
Sélectionnez
1.
<script type="text/javascript" language="javascript" src="polymerstarter/polymerstarter.nocache.js" async />

II-A-3. Utilisable hors-ligne

Cela peut être fait en utilisant principalement les services workers du navigateur. Il n'y a pas de bibliothèques officielles GWT pour interagir avec les services workers. Même la bibliothèque gwt-polymer-elements n'enveloppe pas les Platinum Elements qui sont les éléments Polymer destinés à interagir avec les services workers. Les utilisateurs GWT devront écrire du JavaScript manuellement pour mettre en œuvre le mécanisme de mise en cache pour les ressources de l'application. JSNI ou Jsinterop peuvent être utilisés pour interagir avec le navigateur et appeler des services workers. Le script des SW qui définit les événements de mise en cache doit être dans un script séparé et donc, pour l'instant, il n'est pas évident de mélanger à la fois le code du SW et le code du module GWT d'application dans le même fichier. La seule tâche qui peut être faite à partir de GWT est l'enregistrement du SW. Nous allons démontrer cela dans la section suivante. Il est également important de noter que les SW ne sont pas disponibles sur tous les navigateurs, vous pouvez trouver plus de détails dans la documentation de l'API de Mozilla.

Pour plus de détails sur la façon de cacher les données, les ressources et les données d'une application en utilisant les SW, Google fournit quelques recommandations utiles : https://developers.google.com/web/fundamentals/getting-started/your-first-progressive-web-app/step-04?hl=en.

II-A-4. Installable

Cette recette n'est pas spécifique à GWT. Pour rendre une application Web installable, vous devez rajouter un fichier JSON appelé « app manifest » et le relier au point d'entrée .html :

 
Sélectionnez
1.
  <link rel="manifest" href="manifest.json">

Vous pouvez consulter les spécifications de W3C pour les instructions sur la façon d'écrire le manifest. Vous pouvez également utiliser cet outil en ligne : http://brucelawson.github.io/manifest/ qui génère votre manifest pour vous, mais votre application doit déjà être en ligne pour pouvoir l'utiliser. Vous pouvez soit utiliser une bannière pour demander à l'utilisateur d'installer l'application, soit le laisser le faire manuellement à partir des options du navigateur. Cependant, ce serait bien d'avoir un outil qui permette de générer le manifest à partir du code GWT.

#4 Engageante :

Encore une fois, il n'y a pas de bibliothèque de notification push officielle pour GWT. Cela peut être un appel à la communauté GWT pour combler cette lacune. Jusque-là, les utilisateurs peuvent utiliser soit JSNI, soit Jsinterop pour interagir avec le service worker responsable des notifications push.

III. Application Demo

Pour illustrer les caractéristiques ci-dessus, nous avons construit une application en utilisant gwt-polymer-elements et gwty-leaflet. L'application affiche les cartes préférées de l'utilisateur.

Code source: https://github.com/gwidgets/gwt-pwa-demo live: https://gwt-pwa-demo.herokuapp.com/pwademo.html

En utilisant Polymer, notre application est responsive par défaut.

Image 1
Image 2

Pour réduire le temps de chargement de l'application, nous avons tout d'abord identifié tout le HTML statique que nous avons mis dans le point d'entrée .html: https://github.com/gwidgets/gwt-pwa-demo/blob/master/src/main/webapp/pwademo.html.

On a utilisé Polymer elemental pour interagir avec les éléments du DOM. Par exemple :

 
Sélectionnez
1.
2.
  PaperMenuLEement paperMenu = (PaperMenuElement) Polymer.getDocument().getElementById("paperMenu");
  paperMenu.select("paris");

Et nous avons aussi mis en place du code splitting, parce que nous n'avons qu'une carte par section, donc nous avons seulement besoin de charger la carte de la section affichée lorsque la page est chargée :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
loadStartupMap();

// les autres cartes ne sont pas chargées au démarrage, mais lorsque l'iron-selector sélectionne une section
ironPages.addEventListener("iron-select", e -> {
             
if(ironPages.getSelected().equals("london") && !londonMapInitialized) { 
                
         // Some code splitting to reduce initial module size
        GWT.runAsync(new RunAsyncCallback(){
            @Override
            public void onFailure(Throwable reason) {
                Document.get().getElementById("londonMap").setInnerHTML("Could not load this map, please try again later");
            }

            @Override
            public void onSuccess() {
                Maps.initializeLondonMap();                        
            }
        });

        londonMapInitialized = true;
    }
});

Nous avons également rajouté un app manisfest pour permettre à l'application de s' installer :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
{
  "name": "Favorite Maps PWA",
  "short_name": "Favorite Maps PWA",
  "icons": [{
        "src": "image/mapicon.png",
        "sizes": "144x144",
        "type": "image/png"
      }],
  "start_url": "/pwademo.html",
  "display": "standalone",
  "background_color": "#3E4EB8",
  "theme_color": "#2E3AA1"
}

Pour terminer, nous avons ajouté les classes JsInterop pour inscrire le travailleur de service :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
if (Navigator.serviceWorker != null) {
    Navigator.serviceWorker.register("service-worker/sw.js").then(new Function<JavaScriptObject, JavaScriptObject>() {
        @Override
        public JavaScriptObject call(JavaScriptObject arg) {
            GWT.log("registred service worker successfully");
            return null;
        }
    });
} else {
    GWT.log("service worker unavailable in this browser");
}

Et nous avons créé un travailleur de service dans un script nommé sw.js que nous avons rajouté aux ressources de l'application :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
var cacheName = 'GWT-PWA';  
var filesToCache = [  
                     '/gwt-pwa/pwademo.html',  
                     '/gwt-pwa/pwademo.css',  
                     '/gwt-pwa/styles/app-theme.html',  
                     '/gwt-pwa/styles/shared-styles.html',  
                     '/gwt-pwa/leaflet/leaflet.js',  
                     '/gwt-pwa/leaflet/leaflet.css',
                     '/gwt-pwa/image/mapicon.png',
                      '/gwt-pwa/pwademo/pwademo.nocache.js'];

self.addEventListener('install', function(e) {  
    console.log('[ServiceWorker] Install');  
  e.waitUntil(  
      caches.open(cacheName).then(function(cache) {  
        console.log('[ServiceWorker] Caching app shell');  
      return cache.addAll(filesToCache);  
    })  
  );  
});


self.addEventListener('activate', function(e) {  
    console.log('[ServiceWorker] Activate');  
    e.waitUntil(  
        caches.keys().then(function(keyList) {  
          return Promise.all(keyList.map(function(key) {  
            console.log('[ServiceWorker] Removing old cache', key);  
            if (key !== cacheName) {  
                return caches.delete(key);  
            }  
        }));  
      })  
    );  
});

self.addEventListener('fetch', function(e) {  
    console.log('[ServiceWorker] Fetch', e.request.url);  
    e.respondWith(  
        caches.match(e.request).then(function(response) {  
          return response || fetch(e.request);  
      })  
    );  
});

Le script installe et active le service worker. Il permet également au SW de s'abonner à l'événement « fetch » qui se déclenche avant chaque requête. Basé sur son état actuel, le SW décide soit d'utiliser le cache local, soit d'aller chercher la ressource en utilisant le réseau.

Après le chargement de l'application, la section cache dans les outils dev de Google Chrome affiche les éléments mis en cache.

Image 3

Si nous désactivons la connexion réseau sur Google Chrome, on remarque qu'on peut toujours naviguer dans l'application :

Image 4

Si nous jetons un coup d'œil dans la section requête réseau dans les outils de développement de Chrome, on remarque que les ressources de notre application sont servies à partir du service worker :

Image 5

Comme il s'agit d'une application de démonstration, nous n'avons pas configuré de notification push, car cela nécessite l'installation d'un serveur Push.

Nous avons installé l'application dans l'écran d'accueil d'un téléphone Android, et nous avons obtenu le résultat montré sur les figures suivantes :

Image 6
Image 7

IV. Conclusion

Les PWA représentent un nouveau paradigme dans le monde Web. Certains prédisent qu'elles vont remplacer les applications natives dans les années à venir. Nous savons que les développeurs GWT utilisent Phonegap pour convertir leur application Web en une application mobile native, et peut-être qu'avec les PWA, ils n'auront plus à le faire. Nous avons vu dans ce tutoriel comment GWT peut être utilisé pour construire une PWA, en utilisant des bibliothèques telles que Polymer. Il n'y a jusqu'à présent pas de bibliothèques GWT pour interagir avec les service workers du navigateur, et donc il faudra attendre que ce vide soit comblé par la communauté GWT.

V. Remerciements

Cet article a été publié avec l'aimable autorisation d' Zakaria Amine. L'article original est disponible à cette adresse : Progressive Web apps recipes for GWT.

Nous tenons à remercier ced pour la relecture orthographique attentive de cet article et Mickael Baron pour la mise au gabarit.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2016 Zakaria Amine. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.