web-dev-qa-db-fra.com

wp_register_script identifiants multiples?

Afin d'éviter de mauvaises performances avec plusieurs balises <script>, je concatène régulièrement des scripts et ne génère qu'un seul fichier JS bundle.min.js et un identificateur 'jsbundle'.

Le problème est que les choses ajoutées par la suite, comme les plugins, peuvent dépendre d'une ou plusieurs bibliothèques enregistrées qui sont présentes , mais emballées dans un 'jsbundle' générique.

Existe-t-il un moyen d'informer Wordpress que 'jsbundle' implique, par exemple, 'jquery', 'backbone', ... afin que 1) la ressource ne soit pas chargée deux fois 2) que les choses n'échouent pas à cause d'une dépendance non remplie?

J'ai essayé avec la source de wp_register_script, trouvé la classe WP_Scripts() et essayé de "mentir" WP à propos des scripts disponibles, mais pas de chance.

2
Miloshio

Pour que les bibliothèques JavaScript ne soient pas chargées car vous en avez déjà créé un ensemble, procédez comme suit:

En mettant comme suit, la mise en file d'attente habituelle suivante:

function the_js() {
    wp_enqueue_script('bundle_js', get_template_directory_uri() . '/js/bundle.js', array(), false, false);
}

add_action('wp_enqueue_scripts', 'the_js');

et disons que vous avez dans votre bundle les librairies suivantes (listant les handles):

  1. jquery
  2. backbone
  3. colorpicker
  4. bootstrap_js

1, 2, 3 sont déjà dans le noyau, 4 est un tiers, vous avez groupé les 4 parce que vous ne voulez pas que les 4 soient chargés en tant que ressources séparées.

Vous devez vous désinscrire (si elles sont enregistrées, les ressources de base le seraient déjà) et enregistrer chacune d'elles, chacune des bibliothèques contenues dans votre bundle:

function the_js() {
    wp_enqueue_script('bundle_js', get_template_directory_uri() . '/js/bundle.js', array(), false, false);

    //DEREGISTER the SCRIPTS THAT ARE IN YOUR BUNDLE
    wp_deregister_script('jquery'); //because its a Core-Registered Script
    wp_deregister_script('backbone'); //because its a Core-Registered Script
    wp_deregister_script('colorpicker'); //because its a Core-Registered Script

    //REGISTER THEM THIS TIME USING YOUR BUNDLE AS DEPENDENCY
    wp_register_script('jquery', FALSE, array('bundle_js'), '', FALSE);//THE KEY HERE IS THE SRC BEING FALSE
    wp_register_script('backbone', FALSE, array('bundle_js'), '', FALSE);
    wp_register_script('colorpicker', FALSE, array('bundle_js'), '', FALSE);

}

add_action('wp_enqueue_scripts', 'the_js');

la clé ici est de définir le $src comme FALSE pour que le script enregistré soit un alias, vérifiez cette ligne dans le code principal:

// A single item may alias a set of items, by having dependencies, but no source.
if ( ! $obj->src ) {
   return true;
}

c'est ce que jquery fait actuellement, en mettant jquery comme dépendance, il ne charge pas jquery il charge jquery-core et jquery-migrate, il s'agit de l'objet enregistré pour jquery:

object(_WP_Dependency)#329 (6) {
  ["handle"]=>
  string(6) "jquery"
  ["src"]=>
  bool(false)
  ["deps"]=>
  array(2) {
    [0]=>
    string(11) "jquery-core"
    [1]=>
    string(14) "jquery-migrate"
  }
  ["ver"]=>
  string(6) "1.12.4"
  ["args"]=>
  NULL
  ["extra"]=>
  array(0) {
  }
}

donc, bundle_js sa sera chargée quand un script aura comme dépendance de l’une des bibliothèques (jquery, backbone, colorpicker) et il sera chargé une fois, puisque la logique dans WP_Dependencies vérifie si elle est déjà dans le tableau queue.

Si vous voulez vérifier si un script est déjà enregistré, utilisez:

global $wp_scripts;
$wp_scripts->query('jquery'); //jquery as example handle

il retournera un objet WP_dependency s'il est enregistré, false s'il ne l'est pas.

Quelques liens pour mieux comprendre:
class.wp-dependencies.php
class.wp-scripts.php
functions.wp-scripts.php

4
David Lee

La solution proposée par Alexander semble raisonnable. Vous pouvez conserver toutes les dépendances minifiées (libs.js dans mon exemple) dans un fichier et vos scripts dans un second fichier comme suit:

wp_enqueue_script( 'libs', get_template_directory_uri() . '/assets/js/libs.js', [], '1.0.0', true );

// third parameter here will make sure that libs.js is loaded before jsbundle:

wp_enqueue_script( 'jsbundle', get_template_directory_uri() . '/assets/js/jsbundle.min.js', [ 'libs' ], '1.0.0', true );

Même si regrouper des scripts est une pratique bien connue pour optimiser la vitesse de chargement des pages et une bonne idée la plupart du temps, il se peut que ce ne soit pas toujours la solution la plus performante. Je considérerais toujours d'autres options et déciderais ce qui fonctionne le mieux pour un cas d'utilisation particulier.

CDN

Vous voudrez peut-être utiliser l'un des CDN disponibles pour vos bibliothèques tierces pour obtenir tous les avantages énumérés dans cette réponse .

Par exemple. Si vous utilisez le CDN de Google ou de Facebook, il est fort probable que vos visiteurs aient déjà des scripts populaires en mémoire cache dans leur navigateur et qu'ils n'auraient pas besoin de les télécharger à nouveau.

Dans ce cas, le regroupement de scripts tiers supprime cet avantage et le regroupement complet doit être téléchargé, même si une partie de celui-ci est déjà enregistrée dans la mémoire cache du navigateur.

Vous pouvez facilement mettre en file d'attente des scripts à partir de CDN avec wp_enqueue_script(); il vous suffit d'omettre le paramètre $ver, car vous ne souhaitez pas actualiser le script mis en cache:

wp_enqueue_script( 'jquery', 'https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js', [], null, true );

Tags conditionnels

L'autre chose que je considérerais est d'enregistrer plus de scripts et de ne les appeler que sur les pages où ils sont réellement utilisés. Cela fonctionne bien surtout si vous mettez souvent à jour vos scripts.

Si vous conservez tous les scripts dans un lot, l’application de quelques modifications nécessite l’analyse de cache pour l’ensemble du script. Parfois, il peut être préférable de séparer certaines parties de vos scripts afin de pouvoir changer de version uniquement pour le bloc que vous avez réellement modifié.

Supposons que j'utilise le script foo.js sur ma page d'accueil et qu'il ne se passe pas grand-chose, donc je ne prévois pas de le changer de si tôt, mais j'ai en même temps un script compliqué bar.js que je dois entretenir et actualiser souvent. Dans ce cas, il serait peut-être préférable d’enregistrer les scripts séparément.

En outre, certaines de vos bibliothèques peuvent n'être utilisées que sur des sous-pages qui ne sont pas visitées très souvent (disons que j'utilise masonry sur une sous-page moins populaire), leur préchargement sur votre page d'accueil risque donc de ne pas être la solution. Dans ce cas, je ferais: // enregistre la maçonnerie mais ne la place pas encore dans la file d'attente wp_register_script ('maçonnerie', get_template_directory_uri (). '/Assets/js/masonry.min.js', [], 1.0, true) ;

// foo.js is my implementation of masonry.js
wp_register_script( 'foo.js', get_template_directory_uri() . '/assets/js/masonry.min.js', [ 'masonry' ], 1.0, true );

// in bar.js I keep functions used all over my page so I enqueue it everywhere immediately
wp_enqueue_script( 'bar.js', get_template_directory_uri() . '/assets/js/masonry.min.js', [], 1.0, true );

// masonry.js and foo.js can wait until the user reaches my less popular subpage as it won't be needed most of the time
if ( is_post_type_archive( 'unpopular-posts' ) ){
    wp_enqueue_script( 'foo.js' );
}
0
Levi Dulstein

Un échange rapide avec @janh dans le cadre de ma réponse précédente m'a incité à réfléchir à une approche différente. J'irais quand même avec la minimisation de toutes les bibliothèques tierces dans un fichier de paquet séparé (bundlejs), puis avec la variable globale $wp_scripts (le crédit pour cette idée va à ce fil )

Cela ne sera jamais entièrement automatisé - comme Janh l'a remarqué, il est impossible de prédire comment d'autres personnes appelleraient les scripts dans leurs plugins ou leurs thèmes. Dans ma fonction, j'utilise un tableau de noms de script que contient mon fichier de paquet. Pour doubler les chances de doublons possibles, j'ai également ajouté la prise en charge des noms de fichier de script au lieu des poignées uniquement.

Gardez à l’esprit que certains scripts d’administrateur ne doivent absolument pas être touchés (voir codex )

La solution est assez crue, je suis sûr qu’elle peut être perfectionnée de nombreuses façons, mais elle pourrait certainement utiliser un peu de refactoring et la prise en charge du cache WP. De plus, je ne suis pas sûr de l’impact sur les performances dans les cas réels. Mais c'est ici que vous avez une idée générale et peut-être une inspiration:

function switch_dependencies_to_bundle(){
    global $wp_scripts;

    /**
     * array of scripts that our bundle file contains - can be either script name or handle name
     * it's never going to be bulletproof tho', as plugin authors can change file names, i.e.
     * include other version of script
     */
    $bundled = [
        'jquery',
        'masonry',
        'masonry-js',
        'masonry.js',
        'masonry.min.js', //we can use file name too
        'backbone',
        'backbone.min.js'
    ];

    $deps_to_remove = [];

    // register our bundle script
    wp_register_script( 'bundlejs', get_template_directory_uri() . '/assets/js/bundle.min.js', [], '1.0.0', true );
    wp_enqueue_script( 'bundlejs' );

    // get registered scripts that our bundle file would duplicate
    foreach( $wp_scripts->registered as $handle => $script ){
        $file_name = substr( $script->src , strrpos( $script->src, "/" ) + 1 );
        if ( in_array( $handle, $bundled, true ) || in_array( $file_name, $bundled, true ) ){
            $deps_to_remove[] = $handle;
        }
    }

    //get rid of redundant scripts with deregister and dequeue.
    //NOTE: does not work for some admin scripts, see codex
    foreach( $deps_to_remove as $script ){
        wp_deregister_script( $script );
        wp_dequeue_script( $script );
    }

    // take care of remaining scripts' dependencies (they wouldn't load with deps missing)
    // add our bundle as dependency, as it contains the same scripts that we just removed
    foreach( $wp_scripts->registered as $script ){
        if ( ! empty( array_intersect( $deps_to_remove, $script->deps ) ) ) {
            $script->deps[] = 'bundlejs';
            $script->deps = array_diff( $script->deps, $deps_to_remove );
        }
    }
}

// load the function with high priority so it kickes in after other plugins registered their scripts
add_action('wp_enqueue_scripts', 'switch_dependencies_to_bundle', 9999);
0
Levi Dulstein