À propos de l'auteur
Leonardo Losoviz est le créateur de PoP un framework pour la construction de sites Web modulaires basés sur PHP et les guidons, et optimisé par WordPress. Il habite à Kuala…
En savoir plus sur Leonardo
Une utilisation excessive du code CSS ou JS en ligne, par opposition à la fourniture de code via des ressources statiques, peut nuire aux performances du site. Dans cet article, nous allons apprendre à charger du code dynamique via des fichiers statiques, en évitant les inconvénients liés à une surcharge de code en ligne.
L'insertion est le processus consistant à inclure le contenu des fichiers directement dans le document HTML: les fichiers CSS peuvent être insérés dans un élément de style
et les fichiers JavaScript peuvent être insérés dans un script
. element:
En imprimant le code déjà dans la sortie HTML, l'inlining évite les requêtes de blocage du rendu et exécute le code avant le rendu de la page. En tant que tel, il est utile pour améliorer les performances perçues du site (c.-à-d. Le temps nécessaire pour qu'une page devienne utilisable.) Par exemple, nous pouvons utiliser le tampon de données livré immédiatement lors du chargement du site. (environ 14 Ko) pour intégrer les styles critiques y compris les styles de contenu au-dessus du pli (comme avait été créé sur le site précédent de Smashing Magazine ), ainsi que la taille et la largeur des polices et hauteurs pour éviter un re-rendu saccadé de la présentation lorsque le reste des données est fourni.
Toutefois, lorsqu'il est trop utilisé, le code en ligne peut également avoir des effets négatifs sur les performances du site: Comme le code n'est pas en cache, le même le contenu est envoyé au client à plusieurs reprises et il ne peut pas être pré-mis en cache par le biais de Service Workers, ni mis en cache et accessible à partir d'un réseau de distribution de contenu. En outre, les scripts en ligne sont considérés comme non sécurisés lors de la mise en œuvre d'une stratégie de sécurité du contenu (CSP). Il constitue ensuite une stratégie judicieuse pour intégrer les parties critiques de CSS et de JS qui accélèrent le chargement du site, tout en évitant le plus possible sinon.
Dans le but d’éviter l’insertion, dans cet article, nous verrons comment convertir en ligne. code en ressources statiques: au lieu d’imprimer le code dans la sortie HTML, nous l’enregistrons sur le disque (ce qui crée effectivement un fichier statique) et ajoutons la balise ou correspondante pour charger le fichier. Commençons. ! Lectures recommandées : La sécurité de WordPress en tant que processus
Quand éviter l'inline
Il n'y a pas de recette magique pour déterminer si un code doit être en ligne ou non, cependant, cela peut être assez évident quand un code ne doit pas être en ligne: quand il implique une grande quantité de code et quand il n'est pas nécessaire immédiatement. Les sites WordPress insèrent les modèles JavaScript pour rendre le Media M anager (accessible sur la page de la médiathèque sous / wp-admin/upload.php
), en train d’imprimer une quantité appréciable de code:
Déclencher la création de fichiers statiques [19659017] Si le contenu (celui à aligner) provient d’un fichier statique, il n’ya pas grand chose à faire à part demander simplement ce fichier statique au lieu d’inclure le code.
Pour le code dynamique, il faut cependant planifier comment. / Quand générer le fichier statique avec son contenu. Par exemple, si le site propose des options de configuration (telles que la modification du jeu de couleurs ou de l'image d'arrière-plan), quand le fichier contenant les nouvelles valeurs doit-il être généré? Nous avons les possibilités suivantes pour créer les fichiers statiques à partir du code dynamique:
- Sur demande Lorsqu'un utilisateur accède au contenu pour la première fois:
- Lors du changement Lorsque le code source est source ( par exemple une valeur de configuration) a changé.
Considérons d’abord sur demande. La première fois qu'un utilisateur accède au site, par exemple via /index.html
le fichier statique (par exemple header-colors.css
) n'existe pas encore, il doit donc l'être généré alors. La séquence d'événements est la suivante:
- L'utilisateur demande
/index.html
;
- Lors du traitement de la demande, le serveur vérifie si le fichier
header-colors.css
existe. Comme il ne le fait pas, il obtient le code source et génère le fichier sur le disque;
- Il renvoie une réponse au client, y compris tag
- Le navigateur récupère toutes les ressources incluses dans la page, y compris
header-colors.css
;
- À ce moment-là, ce fichier existe et est donc affiché.
Toutefois, la séquence des événements peut également être différente, ce qui conduit à un résultat peu satisfaisant. Par exemple:
- L'utilisateur demande
/index.html
;
- Ce fichier est déjà mis en cache par le navigateur (ou par un autre proxy, ou par l'intermédiaire du Service Workers). La demande n'est donc jamais envoyée à le serveur
- Le navigateur récupère toutes les ressources incluses dans la page, y compris
header-colors.css
. Cependant, cette image n'étant pas cachée dans le navigateur, la demande est envoyée au serveur;
- Le serveur n'a pas encore généré
header-colors.css
(par exemple, il vient d'être redémarré);
- Il retournera un 404.
Sinon, nous pourrions générer header-colors.css
non lorsque vous demandez /index.html
mais lorsque vous demandez / header -colors.css
lui-même. Cependant, comme ce fichier n’existait pas au départ, la demande est déjà traitée comme un 404. Même si nous pouvions le contourner, modifier les en-têtes pour changer le code de statut en 200 et renvoyer le contenu de l’image, c'est une façon terrible de faire les choses, donc nous n'allons pas envisager cette possibilité (nous sommes bien meilleurs que cela!)
Cela ne laisse qu'une option: générer le fichier statique après que sa source ait changé.
/index.html
; header-colors.css
existe. Comme il ne le fait pas, il obtient le code source et génère le fichier sur le disque;
header-colors.css
; /index.html
; header-colors.css
. Cependant, cette image n'étant pas cachée dans le navigateur, la demande est envoyée au serveur; header-colors.css
(par exemple, il vient d'être redémarré); Création de la statique Fichier lorsque la source change
Veuillez noter que nous pouvons créer du code dynamique à partir de sources dépendantes de l'utilisateur et du site. Par exemple, si le thème permet de modifier l’image d’arrière-plan du site et que cette option est configurée par l’administrateur du site, le fichier statique peut être généré dans le cadre du processus de déploiement. D'autre part, si le site permet à ses utilisateurs de modifier l'image d'arrière-plan de leurs profils, le fichier statique doit être généré à l'exécution. En résumé, nous avons les deux cas suivants:- User Configuration . ] Le processus doit être déclenché lorsque l'utilisateur met à jour une configuration.
- Configuration du site Le processus doit être déclenché lorsque l'administrateur met à jour une configuration pour le site ou avant de déployer le site.
- Gestion des versions Vous devez accéder au fichier statique avec un paramètre «version» afin d'invalider le fichier précédent lors de la création d'un nouveau fichier statique. Alors que # 2 pourrait simplement avoir le même versioning que le site, # 1 doit utiliser une version dynamique pour chaque utilisateur, éventuellement enregistrée dans la base de données.
- L'emplacement du fichier généré # 2 génère un fichier statique unique pour l'ensemble du site (par exemple,
/staticfiles/header-colors.css
), tandis que # 1 crée un fichier statique pour chaque utilisateur (par exemple,/staticfiles/users/leo/header-colors.css
). - Evénement déclencheur Alors que pour le fichier n ° 1, le fichier statique doit être exécuté à l'exécution, pour le fichier n ° 2, il peut également être exécuté dans le cadre d'un processus de construction de notre environnement intermédiaire.
- Déploiement et distribution [19659027] Les fichiers statiques du n ° 2 peuvent être intégrés de manière transparente à l'ensemble de déploiement du site, sans aucun problème. Toutefois, les fichiers statiques n ° 1 ne le peuvent pas. Le processus doit donc gérer des problèmes supplémentaires, tels que plusieurs serveurs derrière un équilibreur de charge (les fichiers statiques seront-ils créés sur un seul serveur ou sur tous, et comment?). 19659061] Nous allons ensuite concevoir et mettre en œuvre le processus. Pour chaque fichier statique à générer, vous devez créer un objet contenant les métadonnées du fichier, calculer son contenu à partir des sources dynamiques et enfin enregistrer le fichier statique sur le disque. Comme exemple d'utilisation pour guider les explications ci-dessous, nous allons générer les fichiers statiques suivants:
-
header-colors.css
avec un style issu des valeurs enregistrées dans la base de données -
welcomeuser data.js
contenant un objet JSON avec des données utilisateur sous une variable:window.welcomeUserData = {name: "Leo"};
.
[create_static_files]
comme je l'ai décrit dans un article précédent . Autres lectures recommandées : Créer un service ouvrier: une étude de casReprésenter le fichier sous forme d'objet
Nous devons modéliser un fichier sous la forme d'un objet PHP avec toutes les propriétés correspondantes afin de pouvoir le sauvegarder sur le disque à un emplacement spécifique. (par exemple sous/ staticfiles /
ou/ staticfiles / users / leo /
), et savent comment demander le fichier en conséquence. Pour cela, nous créons une interfaceRessource
renvoyant à la fois les métadonnées du fichier (nom du fichier, répertoire, type: "css" ou "js", version et dépendances d'autres ressources) et son contenu. Interface
Afin de rendre le code maintenable et réutilisable, nous suivons les principes SOLID pour lesquels nous avons défini un schéma d'héritage d'objet pour que les ressources ajoutent progressivement des propriétés, à partir de la classe abstraiteRessource { fonction get_filename (); fonction get_dir (); fonction get_type (); fonction get_version (); fonction get_dependencies (); fonction get_content (); }
ResourceBase
de laquelle hériteront toutes les implémentations de nos ressources:
Après SOLID, nous créons des sous-classes chaque fois que les propriétés diffèrent. Comme indiqué précédemment, l'emplacement du fichier statique généré et le contrôle de version qui le demandera seront différents selon que le fichier concerne l'utilisateur ou la configuration du site:la classe abstraite ResourceBase implémente Resource { fonction get_dependencies () { // Par défaut, un fichier n'a pas de dépendances retourne un tableau (); } }
Enfin, au dernier niveau, nous implémentons les objets pour les fichiers que nous voulons générer, en ajoutant le nom de fichier, le type de fichier et le code dynamique via la fonctionla classe abstraite UserResourceBase étend la base de ressources { fonction get_dir () { // Un fichier et un dossier différent pour chaque utilisateur $ user = wp_get_current_user (); return "/ staticfiles / users / {$ user-> user_login} /"; } fonction get_version () { // Sauvegarde la version de la ressource pour l'utilisateur sous ses métadonnées. // Lorsque le fichier est régénéré, vous devez exécuter `update_user_meta` pour augmenter le numéro de version. $ user_id = get_current_user_id (); $ meta_key = "version_ressources _". $ this-> get_filename (); return get_user_meta ($ user_id, $ meta_key, true); } } la classe abstraite SiteResourceBase étend la base de données { fonction get_dir () { // Tous les fichiers sont placés dans le même dossier renvoyer "/ staticfiles /"; } fonction get_version () { // Même versioning que le site, supposé défini sous une constante return SITE_VERSION; } }
get_content
:
Avec cela, nous avons modélisé le fichier en tant qu'objet PHP. Nous devons ensuite l’enregistrer sur le disque.classe HeaderColorsSiteResource étend SiteResourceBase { fonction get_filename () { retourne "en-tête-couleurs"; } fonction get_type () { renvoyer "css"; } function get_content () { retour sprintf ( " .site-title a { couleurs; } ", esc_attr (get_header_textcolor ()) ) } } La classe WelcomeUserDataUserResource étend UserResourceBase { fonction get_filename () { renvoyer "welcomeuser-data"; } fonction get_type () { retourne "js"; } function get_content () { $ user = wp_get_current_user (); retour sprintf ( "window.welcomeUserData =% s;", json_encode ( tableau ( "name" => $ user-> display_name ) ) ) } }
Enregistrement du fichier statique sur le disque
L’enregistrement d’un fichier sur le disque peut être facilement effectué à l’aide des fonctions natives fournies par le langage. Dans le cas de PHP, ceci est accompli par la fonctionfwrite
. De plus, nous créons une classe d’utilitéResourceUtils
avec des fonctions fournissant le chemin absolu du fichier sur le disque, ainsi que son chemin relatif à la racine du site:
Ensuite, chaque fois que la source change et que le fichier statique doit être régénéré, nous exécutonsclasse ResourceUtils { fonction statique protégée get_file_relative_path ($ fileObject) { return $ fileObject-> get_dir (). $ fileObject-> get_filename (). ".". $ fileObject-> get_type (); } fonction statique get_file_path ($ fileObject) { // Notez que nous devons ajouter la constante WP_CONTENT_DIR pour rendre le chemin absolu lors de l'enregistrement du fichier. return WP_CONTENT_DIR.self :: get_file_relative_path ($ fileObject); } } classe ResourceGenerator { fonction statique save ($ fileObject) { $ file_path = ResourceUtils :: get_file_path ($ fileObject); $ handle = fopen ($ chemin_fichier, "wb"); $ numbytes = fwrite ($ handle, $ fileObject-> get_content ()); fclose ($ handle); } }
ResourceGenerator :: save
en passant l'objet représentant le fichier en tant que paramètre. Le code ci-dessous régénère et enregistre sur le disque les fichiers “header-colors.css” et “welcomeuser-data.js”:
Une fois qu'ils existent, nous pouvons mettre en file d'attente les fichiers à charger via les balises et .// Lorsque vous avez besoin de régénérer header-colors.css, exécutez: ResourceGenerator :: save (new HeaderColorsSiteResource ()); // Lorsque vous avez besoin de régénérer welcomeuser-data.js, exécutez: ResourceGenerator :: save (new WelcomeUserDataUserResource ());
Mise en file d'attente des fichiers statiques
La mise en file d'attente des fichiers statiques n'est pas différente de la mise en file d'attente de toute ressource dans WordPress: via fonctions
wp_enqueue_script
etwp_enqueue_style
. Ensuite, nous itérons simplement toutes les instances d'objet et utilisons un crochet ou un autre en fonction de leur valeurget_type ()
étant"js"
ou"css"
.Nous ajoutons d’abord des fonctions utilitaires pour fournir l’URL du fichier et indiquer le type, JS ou CSS:
class ResourceUtils { // Suite d'en haut ... fonction statique get_file_url ($ fileObject) { // Ajoute l'URL du site avant le chemin du fichier return get_site_url (). self :: get_file_relative_path ($ fileObject); } fonction statique is_css ($ fileObject) { return $ fileObject-> get_type () == "css"; } fonction statique is_js ($ fileObject) { return $ fileObject-> get_type () == "js"; } }
Une instance de la classe
ResourceEnqueuer
contiendra tous les fichiers qui doivent être chargés. Lorsqu'elles sont appelées, ses fonctionsenqueue_scripts
etenqueue_styles
effectuent la mise en file d'attente, en exécutant les fonctions WordPress correspondantes (wp_enqueue_script
etwp_enqueue_style
))))classe ResourceEnqueuer { protected $ fileObjects; fonction __construct ($ fileObjects) { $ this-> fileObjects = $ fileObjects; } fonction protégée get_file_properties ($ fileObject) { $ handle = $ fileObject-> get_filename (); $ url = ResourceUtils :: get_file_url ($ fileObject); $ dependencies = $ fileObject-> get_dependencies (); $ version = $ fileObject-> get_version (); return array ($ handle, $ url, $ dépendances, $ version); } fonction enqueue_scripts () { $ jsFileObjects = array_map (array (ResourceUtils :: class, 'is_js'), $ this-> fileObjects); foreach ($ jsFileObjects en tant que $ fileObject) { list ($ handle, $ url, $ dépendances, $ version) = $ this-> get_file_properties ($ fileObject); wp_register_script ($ handle, $ url, $ dépendances, $ version); wp_enqueue_script ($ handle); } } fonction enqueue_styles () { $ cssFileObjects = array_map (array (ResourceUtils :: class, 'is_css'), $ this-> fileObjects); foreach ($ cssFileObjects en tant que $ fileObject) { list ($ handle, $ url, $ dépendances, $ version) = $ this-> get_file_properties ($ fileObject); wp_register_style ($ handle, $ url, $ dépendances, $ version); wp_enqueue_style ($ handle); } } }
Enfin, nous instancions un objet de classe
ResourceEnqueuer
avec une liste des objets PHP représentant chaque fichier, et ajoutons un hook WordPress pour exécuter la mise en file d'attente:// Initialize avec les instances d'objet correspondantes pour chaque fichier à mettre en file d'attente $ fileEnqueuer = new ResourceEnqueuer ( tableau ( nouveau HeaderColorsSiteResource (), new WelcomeUserDataUserResource () ) ) // Ajouter les points d'ancrage WordPress pour mettre les ressources en file d'attente add_action ('wp_enqueue_scripts', array ($ fileEnqueuer, 'enqueue_scripts')); add_action ('wp_print_styles', array ($ fileEnqueuer, 'enqueue_styles'));
C’est ça: En cours de mise en file d'attente, les fichiers statiques seront demandés lors du chargement du site dans le client. Nous avons réussi à éviter d’imprimer du code en ligne et de charger des ressources statiques à la place.
Ensuite, nous pouvons appliquer plusieurs améliorations pour améliorer encore les performances.
Lectures recommandées : Introduction à la vérification automatique de Plugins WordPress avec PHPUnit
Regroupement de fichiers
Même si HTTP / 2 a réduit le besoin de regrouper des fichiers, le site reste plus rapide car la compression de fichiers (par exemple via GZip) sera plus efficace. et parce que les navigateurs (tels que Chrome) ont un temps système plus important à traiter de nombreuses ressources.
Nous avons désormais modélisé un fichier en tant qu’objet PHP, ce qui nous permet de traiter cet objet comme une entrée pour d’autres processus. En particulier, nous pouvons répéter le même processus ci-dessus pour regrouper tous les fichiers du même type et servir la version fournie au lieu de tous les fichiers indépendants. Pour cela, nous créons une fonction
get_content
qui extrait simplement le contenu de chaque ressource sous$ fileObjects
et l’imprime à nouveau, produisant l’agrégation de tout le contenu de toutes les ressources:la classe abstraite SiteBundleBase étend SiteResourceBase { protected $ fileObjects; fonction __construct ($ fileObjects) { $ this-> fileObjects = $ fileObjects; } function get_content () { $ content = ""; foreach ($ this-> fileObjects en tant que $ fileObject) { $ content. = $ fileObject-> get_content (). PHP_EOL; } retourne $ contenu; } }
Nous pouvons regrouper tous les fichiers dans le fichier
bundled-styles.css
en créant une classe pour ce fichier:class StylesSiteBundle étend SiteBundleBase { fonction get_filename () { retourne "bundled-styles"; } fonction get_type () { renvoyer "css"; } }
Enfin, nous mettons simplement en file d'attente ces fichiers groupés, comme auparavant, à la place de toutes les ressources indépendantes. Pour CSS, nous créons un paquet contenant des fichiers
header-colors.css
background-image.css
etde font-size.css
pour lesquels nous instancions simplementStylesSiteBundle
avec l'objet PHP pour chacun de ces fichiers (nous pouvons également créer le fichier JS Bundle):$ fileObjects = array ( // CSS nouveau HeaderColorsSiteResource (), new BackgroundImageSiteResource (), nouvelle FontSizesSiteResource (), // JS new WelcomeUserDataUserResource (), new UserShoppingItemsUserResource () ) $ cssFileObjects = array_map (array (ResourceUtils :: class, 'is_css'), $ fileObjects); $ jsFileObjects = array_map (array (ResourceUtils :: class, 'is_js'), $ fileObjects); // Utilise cette définition de $ fileEnqueuer au lieu de la précédente $ fileEnqueuer = new ResourceEnqueuer ( tableau ( nouveau StylesSiteBundle ($ cssFileObjects), nouveau ScriptsSiteBundle ($ jsFileObjects) ) )
C’est tout. Nous ne demanderons plus qu'un seul fichier JS et un seul fichier CSS au lieu de plusieurs.
Une dernière amélioration en ce qui concerne les performances perçues consiste à hiérarchiser les actifs, en retardant le chargement des actifs inutiles immédiatement. Nous allons aborder cette question suivante.
async
/différer
Attributs des ressources JSNous pouvons ajouter des attributs
async
etdifférer
du au . ] balise, pour modifier le moment où le fichier JavaScript est téléchargé, analysé et exécuté, ainsi que pour attribuer une priorité au code JavaScript critique et appliquer le plus tard possible tout élément non critique au minimum, ce qui réduit le temps de chargement apparent du site.Pour implémenter cette fonctionnalité, en suivant les principes SOLID, nous devrions créer une nouvelle interface
JSResource
(qui hérite deResource
) contenant des fonctionsis_async
etet des fonctions
.
. Cependant, cela fermerait la porte aux balises -
Conclusion
Dans cet article, nous avons considéré que l'intégration de code JS et CSS n'était pas toujours idéale. , car le code doit être envoyé à plusieurs reprises au client, ce qui peut avoir un impact négatif sur les performances si la quantité de code est significative. Nous avons vu, à titre d'exemple, comment WordPress charge 43 ko de scripts pour imprimer le Media Manager, qui sont des modèles JavaScript pur et qui pourraient parfaitement être chargés en tant que ressources statiques. Nous avons donc conçu un moyen d'accélérer le développement du site Web en le transformant. Le code en ligne dynamique JS et CSS en ressources statiques, qui peut améliorer la mise en cache à plusieurs niveaux (dans le client, Service Workers, CDN), permet de regrouper tous les fichiers dans une seule ressource JS / CSS afin d'améliorer le rapport lors de la compression. (par exemple via GZip) et pour éviter une surcharge dans les navigateurs de traiter plusieurs ressources simultanément (comme dans Chrome), et permet en outre d'ajouter des attributs async
ou différer
de la pour accélérer l'interactivité de l'utilisateur, améliorant ainsi le temps de chargement apparent du site.
En tant qu'effet secondaire bénéfique, le fait de scinder le code en ressources statiques permet également une meilleure lisibilité du code, ce qui concerne les unités de morue. e au lieu de gros blobs de HTML, ce qui peut conduire à une meilleure maintenance du projet.
La solution que nous avons développée a été réalisée en PHP et inclut quelques éléments de code spécifiques à WordPress. Cependant, le code lui-même est extrêmement simple. à peine quelques interfaces définissant des propriétés et des objets mettant en œuvre ces propriétés conformément aux principes SOLID, ainsi qu'une fonction permettant d'enregistrer un fichier sur un disque. C’est à peu près tout. Le résultat final est net et compact, facile à reproduire pour n’importe quel langage et plate-forme, et facile à introduire dans un projet existant - offrant des gains de performances faciles.
Source link