Fermer

février 22, 2019

Comment éviter le blocage DOM par le stockage local et d'autres coupables


Les programmes JavaScript s'exécutent sur un seul thread du navigateur et dans des environnements d'exécution tels que Node.js. Lorsque le code s'exécute dans un onglet du navigateur, tout le reste s'arrête: commandes de menu, téléchargements, rendu, mises à jour DOM et même animations GIF.

Cela est rarement évident pour l'utilisateur car le traitement est rapide, par petits morceaux. Par exemple: on clique sur un bouton qui déclenche un événement qui exécute une fonction qui effectue un calcul et met à jour le DOM. Une fois terminé, le navigateur est libre de gérer le prochain élément de la file d'attente de traitement.

Le code JavaScript ne peut pas attendre que quelque chose se produise; Imaginez la frustration si une application gelait à chaque fois qu'elle faisait une demande Ajax. Le code JavaScript fonctionne donc à l'aide d'événements et de rappels: un processus au niveau du navigateur ou du système d'exploitation est invité à appeler une fonction spécifique lorsqu'une opération est terminée et que le résultat est prêt.

Dans l'exemple suivant, une fonction de gestionnaire est exécutée lorsqu'un bouton Il se produit un événement click qui anime un élément en appliquant une classe CSS. Lorsque cette animation est terminée, un rappel anonyme supprime la classe:

 // déclenche un événement lorsque l'utilisateur clique sur un bouton
document.getElementById ('clickme'). addEventListener ('click', handleClick);

// gérer l'événement de clic de bouton
fonction handleClick (e) {

  // obtenir un élément à animer
  let sprite = document.getElementById ('sprite');
  si (! sprite) retourne;

  // supprime la classe 'animate' à la fin de l'animation
  sprite.addEventListener ('animationend', () => {
    sprite.classList.remove ('animate');
  });

  // ajoute la classe 'animate'
  sprite.classList.add ('animate');
}

ES2015 a fourni des promesses et ES2017 a introduit asynchrone / pour attendre afin de faciliter le codage, mais les rappels sont toujours utilisés sous la surface. Pour plus d'informations, reportez-vous à « Le contrôle de flux dans JS moderne ».

Blocage de bandits

Malheureusement, certaines opérations JavaScript seront toujours synchrones, notamment:

Le stylo suivant montre un envahisseur qui utilise une combinaison d'animation CSS pour se déplacer et de JavaScript pour agiter les membres. L'image de droite est un GIF animé de base. Appuyez sur le bouton write avec la session par défaut 100 000 . Opérations de stockage :

Voir le stylo
Animation de blocage de DOM
de SitePoint ( @SitePoint )
au CodePen .

Les mises à jour DOM sont bloquées pendant cette opération. L'envahisseur va s'arrêter ou bégayer dans la plupart des navigateurs. L'animation GIF animée fera une pause dans certains. Les appareils plus lents peuvent afficher un avertissement "script ne répondant pas" .

Il s'agit d'un exemple compliqué, mais il montre comment les opérations de base peuvent être affectées par des opérations de base.

Web Workers

La solution aux processus à long terme est les travailleurs du Web . Celles-ci permettent à l'application de navigateur principal de lancer un script en arrière-plan et de communiquer à l'aide d'événements de message. Par exemple:

 // main.js
// les travailleurs Web sont-ils pris en charge?
if (! window.Worker) retourne;

// lance le script de travail web
let myWorker = new Worker ('myworker.js');

// message reçu de myWorker
myWorker.onmessage = e => {
  console.log ('mon utilisateur a envoyé:', e.data);
}

// envoyer un message à myWorker
myWorker.postMessage ('bonjour');

Le script de travail Web:

 // myworker.js
// commence quand un message est reçu
message = e => {
  console.log ('mon-travailleur a reçu:', e.data);
  // ... processus de longue durée ...
  // retour du message
  postMessage ('résultat');
};

Un travailleur peut même inviter d'autres travailleurs à imiter des opérations complexes, ressemblant à des threads. Cependant, les travailleurs sont intentionnellement limités et un travailleur ne peut pas accéder directement au DOM ou au localStorage (cela aurait pour effet de rendre JavaScript multitâche et de casser la stabilité du navigateur.) donc envoyé sous forme de chaînes, ce qui permet de transmettre des objets codés JSON mais pas des nœuds DOM.

Les travailleurs peuvent accéder à certaines propriétés de la fenêtre à des sockets Web et à IndexDB – mais ils n'amélioreraient pas l'exemple présenté. au dessus de. Dans la plupart des cas, les travailleurs sont utilisés pour des calculs longs, tels que le traçage de rayons, le traitement d'images, l'extraction de bitcoins, etc.

(Node.js propose des processus enfants similaires à les travailleurs Web ont des options pour exécuter des exécutables écrits dans d’autres langues.)

Animations accélérées par le matériel

La plupart des navigateurs modernes ne bloquent pas les animations CSS accélérées par le matériel qui s’exécutent dans leur propre calque.

L'exemple ci-dessus déplace l'envahisseur en modifiant la gauche-marge . Cette propriété et des propriétés similaires, telles que left et width permettent au navigateur de refondre et de repeindre le document entier à chaque étape de l'animation.

L'animation est plus efficace lorsque vous utilisez la transformation . et / ou propriétés d'opacité . Ceux-ci placent effectivement l'élément dans une couche de composition distincte de sorte qu'il puisse être animé séparément par le GPU.

Cliquez sur la case à cocher de l'accélération matérielle pour que l'animation devienne immédiatement plus fluide. Maintenant, essayez une autre sessionStorage en écriture; l'envahisseur continuera à se déplacer même si le GIF animé s'arrête. Notez que le mouvement des membres sera toujours en pause car il est contrôlé par JavaScript.

Stockage en mémoire

La mise à jour d’un objet en mémoire est considérablement plus rapide que l’utilisation d’un mécanisme de stockage qui écrit sur le disque. Sélectionnez le type de stockage objet dans le stylo ci-dessus et appuyez sur pour écrire . Les résultats varieront, mais ils devraient être environ 10 fois plus rapides qu'une opération équivalente sessionStorage .

La mémoire est volatile: fermer l'onglet ou naviguer à l'extérieur entraîne la perte de toutes les données. Un bon compromis consiste à utiliser des objets en mémoire pour améliorer les performances, puis à stocker les données en permanence à des moments opportuns – par exemple, lorsque la page est déchargée:

 // obtenir les données précédemment sauvegardées
var store = JSON.parse (localStorage.getItem ('store'));

// initialise un magasin vide
if (! store ||! store.initialized) {
  magasin = {
    initialisé: vrai,
    nom d'utilisateur: 'anonymous'
    score: 0,
    meilleur: {score: 1000, nom d'utilisateur: 'Alice'}
  }
};

// enregistrer dans localStorage lors du déchargement de la page
window.addEventListener ('décharger', () => {
  localStorage.setItem ('magasin', JSON.stringify (magasin));
});

Les jeux ou les applications d'une seule page peuvent nécessiter des options plus complexes. Par exemple, les données sont enregistrées lorsque:

  • aucune activité de l'utilisateur (événements de souris, de toucher ou de clavier) n'a duré plusieurs secondes
  • une partie est suspendue ou l'onglet de l'application est en arrière-plan (voir la page API de visibilité )
  • il y a une pause naturelle – par exemple, lorsque le lecteur meurt, termine un niveau, passe d'un écran à un autre, etc.

Performance Web

Les performances Web sont un sujet d'actualité. Les développeurs sont moins limités par les limites du navigateur et les utilisateurs s'attendent à des performances rapides, similaires à celles d'un système d'exploitation.

Effectuez le moins de traitement possible et le DOM ne sera jamais visiblement bloqué. Heureusement, il existe des options dans les situations où des tâches de longue durée ne peuvent être évitées.

Les utilisateurs et les clients peuvent ne jamais remarquer vos optimisations de vitesse, mais ils se plaindront toujours lorsque l'application ralentira!






Source link