La gestion des librairies avec Drupal 8

Une librairie

La gestion des ressources (feuilles de style CSS, javascript JS) sous Drupal 8 a fondamentalement changé comparé à Drupal 7. Pour des questions de performances, Drupal 8 ne charge plus aucune ressource par défaut sans qu'on ne lui ait demandé explicitement de les charger. Ceci signifie des pages plus légères et donc plus rapides à charger. En particulier, Drupal 8 ne nécessite pas que la librairie jQuery soit chargée par défaut pour tous les visiteurs anonymes sur toutes les pages.

Le principe général qui a guidé la refonte de la gestion des ressources sous Drupal 8 est que seules les pages ayant besoin de ces ressources les chargeront. Le résultat immédiat est une performance accrue, mais son corollaire est une gestion plus fine du chargement de ces ressources. Heureusement, Drupal 8 a revu aussi en profondeur la gestion de ces ressources pour la simplifier et la rendre modulable à volonté en quelques lignes de configuration.

Désormais tout est Librairie !

Les différences fondamentales avec Drupal 7

Sous Drupal 7, nous pouvions déclarer nos feuilles de style et ressources javascript dans le fichier de déclaration du thème avec les propriétés stylesheets et scripts. Ces ressources étaient alors chargées sur toutes les pages du site. Ces propriétés n'existent plus sous Drupal 8.

Désormais, toutes les ressources sont gérées au moyen des libairies.

A noter que les propriétés stylesheets-override et stylesheets-remove peuvent être utilisées dans le fichier de déclaration du thème (theme.info.yml)  pour surcharger ou supprimer des feuilles de styles. Ceci peut être particulièrement utile pour surcharger ou enlever des ressources ajoutées par le coeur de Drupal, ou d'autres modules.

Les libairies sous Drupal 7 étaient déclarées avec le HOOK hook_library_info(). Sur Drupal 8, nous utilisons désormais les fichiers de configuration YAML sous la forme *.libraries.yml (ou vous pouvez remplacer * par le nom du thème ou du module).

De même l'ajout de ressources avec Drupal 7 pouvait se faire avec les fonction drupal_add_css(), drupal_add_js() ou drupal_add_library(). Ces fonctions ont été retirés du coeur de Drupal 8 en faveur de la propriété #attached. Pour injecter une ressource, il suffit désormais de l'attacher à la page via sa propriété #attached

Le processus général de gestion des librairies avec Drupal 8

Le chargement des ressources CSS/JS sur Drupal 8 se réalise simplement en 3 étapes :

  1. Création / enregistrement du fichier de ressources (CSS/JS)
  2. Définition de la librairie qui contient ces fichiers de ressources
  3. Attachement de cette librairie aux pages de Drupal au moyen d'un hook ou avec Twig

A noter qu'il reste possible de charger simplement des ressources CSS/JS sur toutes les pages de votre site Drupal 8, en déclarant la librairie dans le fichier de déclaration d'un thème. A utiliser avec modération, et si votre site Drupal 8 le nécessite réellement.

Définir une librairie Drupal 8

Pour définir une (ou plusieurs) librairies, il suffit de créer un fichier *.libraries.yml dans le répertoire de votre thème (ou module). Si votre thème s'intitule flocon, vous devez donc créer le fichier flocon.libraries.yml. Chaque librairie est définie dans ce fichier par une entrée détaillant les différentes resources CSS et/ou JS à charger, comme ci-dessous.

flocon-slider:
  version: 1.x
  css:
    theme:
      css/flocon-slider.css: {}
  js:
    js/flocon-slider.js: {}

Cet exemple définit la librairie flocon-slider qui contient le fichier CSS flocon-slider.css situé dans le répertoire css de votre thème, et le fichier JS flocon-slider.js situé dans le répertoire js du thème. Vous noterez la clé theme utilisée pour classifier les ressources CSS selon les standards SMACSS. Les différentes valeurs possibles sont : layout, base, theme, component, state.

Comme nous l'avons évoqué plus haut, Drupal 8 ne charge plus aucune ressource par défaut. Si votre ressource javascript nécessite la librairie jQuery pour fonctionner il suffit alors de déclarer cette librairie comme une dépendance. Pour déclarer une dépendance, ou attacher une librairie d'ailleurs, il faut utiliser le nom de cette librairie, préfixé par le nom du module ou du thème qui l'a implémenté (et séparé par un /). Ainsi pour appeler notre librairie, nous devrions appeler la librairie flocon/flocon-slider. jQuery est fourni par le coeur de Drupal, nous allons donc déclarer comme dépendance cette librairie. Notre exemple devient :

flocon-slider:
  version: 1.x
  css:
    theme:
      css/flocon-slider.css: {}
  js:
    js/flocon-slider.js: {}
  dependencies:
    - core/jquery  

Nous pouvons également déclarer les média (print, screen, all) pour lesquelles les feuilles de style css s'appliquent. Par exemple nous pouvons déclarer

general:
  version: 1.x
  css:
    theme:
      css/style.css: {}
      css/print.css: { media: print }    

pour cibler certaines feuilles de style pour certains média. A noter également que les ressouces sont chargées dans le même ordre de leur déclaration dans la librairie.

Par défaut, pour ne pas bloquer inutilement le rendu des pages, Drupal 8 charge désormais toutes les ressources javascript dans le footer de la page. Charger les ressources JS dans le HEAD de la page doit désormais être un choix explicite. Pour ce faire il suffit d'utiliser la propriété header, comme dans l'exemple ci-dessous.

flocon-header:
  header: true
  js:
    header.js: {}

flocon-footer:
  js:
    footer.js: {}  

A noter que toutes les dépendances, directes ou indirectes, d'une librairie chargée dans le head de la page seront également chargées dans le head. 

Surcharger ou étendre les librairies Drupal 8

Toutes les librairies peuvent être surchargées ou étendues en utilisant les entrées libraries-override et libraries-extend dans un fichier de déclaration d'un thème (flocon.info.yml dans notre exemple).

libraries-override est une méthode pour manipuler les ressources (CSS/JS) fournies par d'autres librairies. Nous pouvons enlever ou remplacer une ressource, voir enlever une librairie entièrement. Les exemples ci-dessous illustrent les différents cas :

libraries-override:
  # Remplacer une librairie entière.
  core/drupal.collapse: mytheme/collapse
  
  # Remplacer une ressouce par une autre.
  subtheme/library:
    css:
      theme:
        css/layout.css: css/my-layout.css
  
  # Supprimer une ressource.
  drupal/dialog:
    css:
      theme:
        dialog.theme.css: false
  
  # Supprimer une librairie entière.
  core/modernizr: false

libraries-extend permet aux thèmes d'altérer les ressources fournies par une librairie en leur déclarant des dépendances supplémentaires.

  # Extend drupal.user: add assets from flocon/user library.
libraries-extend:
  core/drupal.user: 
    - flocon/user

Attacher une librairie à des pages

Une fois nos librairies créées, nous pouvons les attacher à nos pages de différentes manières.

Pour les attacher à toutes les pages, il nous suffit de déclarer la librairie dans le fichier de déclaration du thème (flocon.info.yml). Par exemple :

name: Flocon
type: theme
base theme: bartik
description: 'A custom sub-theme, recolorable, based on bartik.'
package: Core
core: 8.x
screenshot : flocon.png
libraries:
  - flocon/general  

Pour cibler des pages plus précises, et ne pas ainsi surcharger les pages de ressources dont elles n'ont pas besoin, nous pouvons attacher nos librairies soit au niveau des templates Twig soit en utilisant les nombreuses fonctions de preprocess à notre disposition, au moyen de la propriété #attached.

Pour utiliser les templates Twig, il nous suffit d'utiliser la fonction attach_library() dans n'importe quel template twig (*.tpl.twig).

{{ attach_library('flocon/flocon-slider') }}

Les différentes fonctions de preprocess (hook_preprocess_hook) nous permettent aussi d'attacher nos librairies, tout comme les fonctions d'altération. Par exemple

function flocon_preprocess_node(&$variables) {
  $variables['#attached']['library'][] = 'flocon/flocon-slider';
}

Les thèmes peuvent implémenter ces fonctions de preprocess dans le fichier {nom_du_theme}.theme, qui est le successeur du vénérable template.php de Drupal 7.

Gérer les librairies externes avec Drupal 8

Pour améliorer les performances de votre site Drupal 8, vous pouvez aussi vouloir attacher des ressources externes disponibles sur des CDN (Content Delivery Network). Il suffit alors de déclarer une librairie comme externe. Les bonnes pratiques vous invitent à renseigner les informations de cette librairie externe dans sa déclaration. Par exemple :

angular.angularjs:
  remote: https://github.com/angular/angular.js
  version: 1.4.4
  license:
    name: MIT
    url: https://github.com/angular/angular.js/blob/master/LICENSE
    gpl-compatible: true
  js:
    https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js: { type: external, minified: true }

Vous pourrez alors attacher cette librairie dans Twig avec la fonction

{{ attach_library('flocon/angular.angularjs') }}

Pour utiliser le même protocle que celui utilisé par votre site (http ou https), vous pouvez utiliser la notation // en lieu et place de http ou https.

  js:
    //ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js: { type: external, minified: true }

Transmettre des variables à Javascript

Pour pouvoir transmettre des valeurs calculées par PHP à votre code javascript, vous pouvez utiliser la librairie drupalSettings. Il suffit de rajouter cette librairie comme dépendance à la votre, puis de définir vos variables transmises à drupalSettings dans les fonctions de preprocess. Ainsi, notre précédente librairie flocon-slider devient

flocon-slider:
  version: 1.x
  css:
    theme:
      css/flocon-slider.css: {}
  js:
    js/flocon-slider.js: {}
  dependencies:
    - core/jquery
    - core/drupalSettings

Et nous transmettons nos variables avec les fonctions de preprocess. Par exemple :

function flocon_preprocess_node(&$variables) {
  $result = 3;
  $variables['#attached']['library'][] = 'flocon/flocon-slider';
  $variables['#attached']['drupalSettings']['flocon']['floconSlider']['number'] = $result;
}

Ainsi le script flocon-slider.js sera en mesure d'accéder à la valeur de $result au moyen de la variable drupalSettings.flocon.floconSlider.number.

Injecter du javascript dans le corps de la page

L'injection de javascript directement dans le corps de la page est déconseillée. Il est préférable de gérer son javascript au sein de fichiers, car ceux-ci peuvent alors être mis en cache et peuvent être plus facilement maintenus. Il existe toutefois des cas de figure où cela s'avère nécessaire, par exemple pour du code javascript qui génère des éléments de contenu sur la page (boutons de partage sociaux, publicité, etc.), ou du code javascript qui affecte l'ensemble de la page (Google analytics par exemple).

Pour ce faire, vous pouvez soit utiliser un bloc personnalisé pour insérer ce code javascipt au sein de votre page, ou encore l'insérer directement dans vos templates twig (par exemple au moyen d'une variable générée dans une fonction de preprocess, pour conserver un template propre et maintenable).

Conclusion

Drupal 8 nous offre avec cette nouvelle version majeure une méthode simple, consistante et extensible pour gérer les librairies de votre projet, de façon granulaire. Il n'y aura plus d'excuses à trouver toutes les ressources d'un projet chargées sur toutes les pages. 

J'espère que ce panorama vous en aura convaincu.

 

Ajouter un commentaire