Code JavaScript fractionné

Le chargement de ressources JavaScript volumineuses a un impact important sur la vitesse des pages. Division... votre JavaScript en fragments plus petits et ne télécharger que ce qui est nécessaire pour qu'une page fonctionne au démarrage peut considérablement améliorer le chargement de votre page la réactivité, ce qui peut améliorer le nombre d'interactions avec les éléments suivants de votre page ; Peinture (INP).

Lorsqu'une page télécharge, analyse et compile des fichiers JavaScript volumineux, elle peut devenir qui ne répondent pas pendant certaines périodes. Les éléments de la page sont visibles, fait partie du code HTML initial d'une page avec un style CSS. Toutefois, comme le code JavaScript nécessaires pour alimenter ces éléments interactifs, ainsi que d'autres scripts chargés par la page, peuvent analyser et exécuter le code JavaScript pour qu'ils fonctionnent. La l'utilisateur peut avoir l'impression que l'interaction était significative retardés, voire complètement cassés.

Cela se produit souvent parce que le thread principal est bloqué pendant l'analyse du code JavaScript. et compilées sur le thread principal. Si ce processus prend trop de temps, il est possible que certains éléments de la page ne répondent pas assez rapidement aux entrées utilisateur. Une solution pour éviter cela consiste à ne charger que le code JavaScript nécessaire au fonctionnement de la page, tandis que reportant le chargement d'autres éléments JavaScript par la suite à l'aide d'une technique connue sous le nom de code le fractionnement. Ce module se concentre sur la dernière de ces deux techniques.

Réduire l'analyse et l'exécution JavaScript au démarrage grâce au fractionnement du code

Lighthouse génère un avertissement lorsque l'exécution de JavaScript prend plus de secondes et échoue lorsqu'elle prend plus de 3, 5 secondes. Utilisation excessive de JavaScript l'analyse et l'exécution constituent un problème potentiel à n'importe quel point de la page. car il peut augmenter le délai d'entrée d'une interaction. si le moment où l'utilisateur interagit avec la page coïncide avec le moment les principales tâches des threads responsables du traitement et de l'exécution de JavaScript sont en cours d'exécution.

En outre, l'exécution et l'analyse excessives de JavaScript sont particulièrement lors du chargement initial de la page, car il s'agit du point de la page cycle de vie que les utilisateurs sont très susceptibles d’interagir avec la page. En fait, Le temps de blocage total (TBT), qui est une métrique de réactivité de chargement, est fortement corrélé. avec INP, ce qui suggère que les utilisateurs ont une forte tendance à tenter d'interagir lors du chargement initial de la page.

L'audit Lighthouse qui indique le temps passé à exécuter chaque fichier JavaScript que les demandes de page sont utiles, car elles peuvent vous aider à identifier les scripts peuvent être candidats à la scission du code. Vous pouvez ensuite aller plus loin en à l'aide de l'outil de couverture disponible dans les outils pour les développeurs Chrome afin d'identifier le code JavaScript d'une page n'est plus utilisé lors de son chargement.

Le fractionnement de code est une technique utile qui permet de réduire le code JavaScript initial d'une page. charges utiles. Il vous permet de diviser un bundle JavaScript en deux parties:

  • Le code JavaScript requis au chargement de la page et ne peut donc être chargé à aucun autre moment en temps réel.
  • JavaScript restant pouvant être chargé ultérieurement, le plus souvent au moment où l'utilisateur interagit avec un élément interactif donné sur la page.

Le fractionnement du code peut être effectué à l'aide de la syntaxe import() dynamique. Ce contrairement aux éléments <script>, qui demandent une ressource JavaScript donnée. Au démarrage : demande une ressource JavaScript ultérieurement tout au long du cycle de vie de la page.

<ph type="x-smartling-placeholder">
document.querySelectorAll('#myForm input').addEventListener('blur', async () => {
  // Get the form validation named export from the module through destructuring:
  const { validateForm } = await import('/validate-form.mjs');

  // Validate the form:
  validateForm();
}, { once: true });

Dans l'extrait JavaScript précédent, le module validate-form.mjs est téléchargée, analysée et exécutée uniquement lorsqu'un utilisateur floute l'une des <input>. Dans ce cas, la ressource JavaScript chargée de qui pilote la logique de validation du formulaire n'intervient sur la page que lorsqu'elle a plus de chances d'être réellement utilisée.

Les bundles JavaScript tels que webpack, Parcel, Rollup et esbuild peuvent être configurés pour diviser les groupes JavaScript en fragments plus petits chaque fois qu'ils un appel import() dynamique dans votre code source s'affiche. La plupart de ces outils automatiquement. Toutefois, pour créer des versions en particulier, vous devez l'activer l'optimisation.

<ph type="x-smartling-placeholder">

Remarques utiles sur le fractionnement du code

Bien que le fractionnement du code soit une méthode efficace pour réduire les conflits dans le thread principal lors du chargement initial de la page, gardez à l'esprit quelques points si vous décidez pour identifier les opportunités de fractionnement du code dans votre code source JavaScript.

Si possible, utilisez un bundler

Les développeurs utilisent souvent des modules JavaScript pendant le de développement d'applications. C'est une excellente amélioration de l'expérience développeur améliore la lisibilité et la gestion du code. Cependant, certaines les caractéristiques de performance non optimales qui peuvent se produire lors de l'envoi de code JavaScript modules en production.

Surtout, vous devez utiliser un bundler pour traiter et optimiser votre source. du code, y compris les modules que vous souhaitez diviser. Les Bundlers sont très efficaces des optimisations au code source JavaScript, mais aussi est très efficace pour équilibrer les considérations de performances, telles que la taille du bundle par rapport au taux de compression. L'efficacité de la compression augmente avec la taille du bundle. mais les bundles essaient également de s'assurer que les lots ne sont pas trop volumineux de longues tâches dues à l'évaluation de scripts.

Les fournisseurs de services de bundle évitent également d'envoyer un grand nombre de modules dégroupés. sur le réseau. Les architectures qui utilisent des modules JavaScript ont tendance à avoir des les arborescences de modules complexes. Lorsque les arborescences de modules sont dégroupées, chaque module représente une requête HTTP distincte et l'interactivité dans votre application Web peuvent être retardées et non les modules. Bien qu'il soit possible d'utiliser le Indice de ressource <link rel="modulepreload"> pour charger les arborescences de modules volumineux le plus tôt possible Il est toujours préférable d'utiliser les bundles JavaScript point de vue.

Ne pas désactiver par inadvertance la compilation en flux continu

Le moteur JavaScript V8 de Chromium offre un certain nombre d'optimisations prêtes à l'emploi pour vous assurer que votre code JavaScript de production se charge aussi efficacement que possible. L'une de ces optimisations est connue sous le nom de compilation en flux continu : l'analyse incrémentielle du code HTML transmis au navigateur et compile des blocs de JavaScript lorsqu'ils arrivent du réseau.

Il existe plusieurs façons de procéder à la compilation en flux continu pour votre application Web dans Chromium:

  • Transformez votre code de production pour éviter d'utiliser des modules JavaScript. Bundlers transformer votre code source JavaScript en fonction d'une cible de compilation ; la cible est souvent spécifique à un environnement donné. V8 appliquera le streaming vers tout code JavaScript qui n'utilise pas de modules, et vous pouvez configurer votre bundler pour transformer le code de votre module JavaScript en syntaxe qui n'utilise pas les modules JavaScript et leurs fonctionnalités.
  • Si vous souhaitez envoyer des modules JavaScript en production, utilisez la classe .mjs . Que votre code JavaScript de production utilise ou non des modules, il existe Aucun type de contenu spécial pour JavaScript utilisant des modules plutôt que JavaScript ce qui n'est pas le cas. En ce qui concerne V8, vous désactivez les flux de données lorsque vous fournissez des modules JavaScript en production à l'aide de .js . Si vous utilisez l'extension .mjs pour les modules JavaScript, V8 peut vous assurer que la compilation par flux du code JavaScript basé sur des modules n'est pas cassées.

Ne laissez pas ces considérations vous dissuader d'utiliser le fractionnement du code. Coder la division est un moyen efficace de réduire les charges utiles JavaScript initiales pour les utilisateurs, mais en utilisant un bundler et en sachant comment préserver les flux de V8 de compilation, vous pouvez vous assurer que votre code JavaScript de production est aussi vite que possible pour les utilisateurs.

Démonstration de l'importation dynamique

Webpack

webpack est fourni avec un plug-in nommé SplitChunksPlugin, qui vous permet configurer la façon dont le bundler divise les fichiers JavaScript. webpack reconnaît à la fois les instructions import() dynamiques et import statiques. Le comportement SplitChunksPlugin peut être modifié en spécifiant l'option chunks dans ses configuration:

  • chunks: async est la valeur par défaut et fait référence aux appels import() dynamiques.
  • chunks: initial fait référence à des appels import statiques.
  • chunks: all couvre à la fois les importations import() dynamiques et statiques, ce qui vous permet pour partager des fragments entre les importations async et initial.

Par défaut, chaque fois que webpack rencontre une instruction import() dynamique. cette crée un bloc distinct pour ce module:

/* main.js */

// An application-specific chunk required during the initial page load:
import myFunction from './my-function.js';

myFunction('Hello world!');

// If a specific condition is met, a separate chunk is downloaded on demand,
// rather than being bundled with the initial chunk:
if (condition) {
  // Assumes top-level await is available. More info:
  // https://v8.dev/features/top-level-await
  await import('/form-validation.js');
}

La configuration webpack par défaut pour l'extrait de code précédent génère deux résultats. des fragments distincts:

  • Le fragment main.js, que webpack classe comme un fragment initial, qui inclut les modules main.js et ./my-function.js.
  • Le fragment async, qui ne comprend que form-validation.js (avec un élément hachage de fichier dans le nom de ressource, le cas échéant). Ce fragment n'est téléchargé si et quand condition est truthy.

Cette configuration vous permet de reporter le chargement du fragment form-validation.js jusqu'à c’est réellement nécessaire. Cela peut améliorer la réactivité au chargement en réduisant les l'évaluation lors du chargement initial de la page. Téléchargement et évaluation du script pour le fragment form-validation.js se produit lorsqu'une condition spécifiée est remplie, Dans ce cas, le module importé dynamiquement est téléchargé. Il peut s'agir, par exemple, condition où un polyfill n'est téléchargé que pour un navigateur particulier ou, comme dans Dans l'exemple précédent, le module importé est nécessaire pour une interaction de l'utilisateur.

D'autre part, modifier la configuration SplitChunksPlugin pour spécifier chunks: initial garantit que le code n'est divisé que sur les fragments initiaux. Il s'agit des fragments tels que ceux importés de manière statique ou répertoriés dans le fichier entry de webpack . Si l'on considère l'exemple précédent, le fragment obtenu serait combinaison de form-validation.js et main.js dans un seul fichier de script, ce qui peut nuire aux performances de chargement initial de la page.

Les options de SplitChunksPlugin peuvent également être configurées pour séparer les grands en plusieurs scripts plus petits, par exemple en utilisant l'option maxSize. pour demander à webpack de diviser les fragments en fichiers distincts s'ils dépassent la limite spécifié par maxSize. Diviser les fichiers de script volumineux en fichiers plus petits peut améliorer la réactivité de chargement, comme dans certains cas l'évaluation de scripts nécessitant une utilisation intensive du processeur ; le travail est divisé en tâches plus petites, qui sont moins susceptibles de bloquer sur des périodes plus longues.

De plus, la génération de fichiers JavaScript plus volumineux signifie également que les scripts plus de risques de subir l'invalidation du cache. Par exemple, si vous livrez un contenant à la fois du code de framework et d'application propriétaire, l'intégralité Le bundle peut être invalidé si seul le framework est mis à jour, mais rien d'autre dans la ressource groupée.

D'un autre côté, des fichiers de script plus petits augmentent la probabilité qu'un retour visiteur récupère les ressources du cache, ce qui accélère le chargement des pages sur visites répétées. Cependant, la compression est moins efficace pour les fichiers de petite taille. ce qui peut augmenter le délai aller-retour du réseau pour les chargements de page le cache du navigateur. Il faut veiller à trouver un équilibre entre la mise en cache l'efficacité, l'efficacité de la compression et le temps d'évaluation du script.

<ph type="x-smartling-placeholder">

démo webpack

<ph type="x-smartling-placeholder">

démonstration SplitChunksPlugin de webpack.

Tester vos connaissances

Quel type d'instruction import est utilisé lors de l'exécution du code le fractionnement ?

import() dynamique.
Valeur import statique.

Quel type d'instruction import doit se trouver en haut d'un module JavaScript et à aucun autre emplacement ?

import() dynamique.
Valeur import statique.

Lorsque vous utilisez SplitChunksPlugin dans webpack, quelle est la valeur différence entre un fragment async et un Fragment initial ?

Les fragments async sont chargés à l'aide d'import statiques et les fragments initial sont chargés import()
Les fragments async sont chargés à l'aide de la méthode dynamique import() et les fragments initial sont chargés à l'aide de fragments import

À suivre: Images à chargement différé et éléments <iframe>

Bien qu'il s'agisse généralement d'un type de ressource relativement coûteux, JavaScript n'est pas le seul type de ressource dont vous pouvez différer le chargement. Image et éléments <iframe> sont des ressources potentiellement coûteuses à part entière. Comme pour JavaScript, vous Vous pouvez différer le chargement des images et de l'élément <iframe> grâce au chargement différé. les uns des autres, ce qui est expliqué dans le module suivant de ce cours.