Quelle est la différence entre utiliser Require.JS AMD et créer simplement un élément <script>
dans le DOM?
D'après ce que je comprends de Require.JS, il offre la possibilité de charger des dépendances, mais cela ne peut-il pas être fait simplement en créant un élément <script>
qui charge le fichier JS externe nécessaire?
Par exemple, supposons que j’ai la fonction doStuff()
, qui nécessite la fonction needMe()
. doStuff()
est dans le fichier externe do_stuff.js
, alors que needMe()
est dans le fichier externe need_me.js
.
Faire ceci à la manière Require.JS:
define(['need_me'],function(){
function doStuff(){
//do some stuff
needMe();
//do some more stuff
}
});
Faire cela en créant simplement un élément de script:
function doStuff(){
var scriptElement = document.createElement('script');
scriptElement.src = 'need_me.js';
scriptElement.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(scriptElement);
//do some stuff
needMe();
//do some more stuff
}
Les deux fonctionnent. Cependant, la deuxième version ne nécessite pas que je charge toute la bibliothèque Require.js. Je ne vois pas vraiment de différence fonctionnelle ...
Voici l'article de Nice sur ajaxian.com expliquant pourquoi l'utiliser:
RequireJS: chargement JavaScript asynchrone
Quels sont les avantages de Require.JS par rapport à la simple création d'un élément dans le DOM?
Dans votre exemple, vous créez la balise de script de manière asynchrone, ce qui signifie que votre fonction needMe()
sera appelée avant le fichier need_me.js se termine. Cela entraîne des exceptions non capturées pour lesquelles votre fonction n'est pas définie.
Au lieu de cela, pour que ce que vous suggérez fonctionne réellement, vous devez faire quelque chose comme ceci:
function doStuff(){
var scriptElement = document.createElement('script');
scriptElement.src = 'need_me.js';
scriptElement.type = 'text/javascript';
scriptElement.addEventListener("load",
function() {
console.log("script loaded - now it's safe to use it!");
// do some stuff
needMe();
//do some more stuff
}, false);
document.getElementsByTagName('head')[0].appendChild(scriptElement);
}
On peut soutenir qu'il est préférable ou non d'utiliser un gestionnaire de paquets tel que RequireJS ou d'utiliser une stratégie purement JavaScript, comme indiqué ci-dessus. Bien que votre application Web puisse se charger plus rapidement, appeler des fonctionnalités et des fonctionnalités sur le site serait plus lent, car cela impliquerait d'attendre le chargement des ressources avant que cette action puisse être effectuée.
Si une application Web est conçue comme une application d'une seule page, sachez que les utilisateurs ne rechargeront pas la page très souvent. Dans ces cas, tout précharger contribuerait à rendre l'expérience plus rapide en utilisant l'application . Dans ce cas, vous avez raison, vous pouvez simplement charger toutes les ressources en incluant simplement les balises de script dans l'en-tête ou le corps de la page.
Toutefois, si vous créez un site Web ou une application Web qui suit le modèle plus traditionnel, qui consiste à passer d'une page à une autre et à recharger des ressources, une approche de chargement paresseux peut aider à accélérer ces transitions.
Quelques autres raisons très précises expliquent pourquoi RequireJS a du sens:
Tiré de les commentaires de rmurphey ici dans ce Gist .
Les couches d’abstraction peuvent être un cauchemar auquel il faut apprendre et s’adapter, mais quand cela sert un but et le fait bien, cela a simplement un sens.
Voici un exemple plus concret.
Je travaille dans un projet avec 60 fichiers. Nous avons 2 modes de fonctionnement différents.
Chargez une version concaténée, 1 gros fichier. (Production)
Charger les 60 fichiers (développement)
Nous utilisons un chargeur, nous n'avons donc qu'un script dans la page Web.
<script src="loader.js"></script>
Par défaut, le mode n ° 1 est chargé (chargement du fichier volumineux concaténé). Pour exécuter le mode in 2 (fichiers séparés), nous définissons un indicateur. Cela pourrait être n'importe quoi. Une clé dans la chaîne de requête. Dans cet exemple, nous faisons juste cela
<script>useDebugVersion = true;</script>
<script src="loader.js"></script>
loader.js ressemble à ceci
if (useDebugVersion) {
injectScript("app.js");
injectScript("somelib.js");
injectScript("someotherlib.js");
injectScript("anotherlib.js");
... repeat for 60 files ...
} else {
injectScript("large-concatinated.js");
}
Le script de construction est juste un fichier .sh qui ressemble à ceci
cat > large-concantinated.js app.js somelib.js someotherlib.js anotherlib.js
etc...
Si un nouveau fichier est ajouté, nous utiliserons probablement le mode n ° 2, car nous travaillons au développement, nous devons ajouter une ligne injectScript("somenewfile.js")
à loader.js.
Ensuite, pour la production, nous devons également ajouter somenewfile.js à notre script de construction. Une étape que nous oublions souvent et obtenons des messages d'erreur.
En passant à AMD, nous n’avons pas besoin de modifier 2 fichiers. Le problème de garder loader.js et le script de construction synchronisés disparaît. En utilisant r.js
ou webpack
, il peut simplement lire le code pour construire large-concantinated.js
Il peut aussi traiter les dépendances, par exemple nous avions 2 fichiers lib1.js et lib2.js chargés comme ceci
injectScript("lib1.js");
injectScript("lib2.js");
lib2 a besoin de lib1. Il a un code à l'intérieur qui fait quelque chose comme
lib1Api.installPlugin(...);
Mais comme les scripts injectés sont chargés de manière asynchrone, rien ne garantit qu'ils se chargeront dans le bon ordre. Ces 2 scripts ne sont pas des scripts AMD mais en utilisant require.js, nous pouvons lui dire leurs dépendances.
require.config({
paths: {
lib1: './path/to/lib1',
lib2: './path/to/lib2',
},
shim: {
lib1: {
"exports": 'lib1Api',
},
lib2: {
"deps": ["lib1"],
},
}
});
Je notre module qui utilise lib1 nous le faisons
define(['lib1'], function(lib1Api) {
lib1Api.doSomething(...);
});
Require.js va maintenant injecter les scripts pour nous et il n'injectera pas lib2 tant que lib1 n'aura pas été chargé car nous lui avons dit que lib2 dépend de lib1. De plus, il ne lancera pas notre module utilisant lib1 tant que lib2 et lib1 ne seront pas chargés.
Cela rend le développement agréable (pas d’étape de construction, pas de souci de chargement de la commande) et rend la production agréable (pas besoin de mettre à jour un script de construction pour chaque script ajouté).
Comme bonus supplémentaire, nous pouvons utiliser le plugin babel de webpack pour exécuter babel sur le code des navigateurs plus anciens et nous n'avons pas non plus besoin de gérer ce script de construction.
Notez que si Chrome (notre navigateur de choix) commençait à prendre en charge import
pour de vrai, nous passerions probablement à cela pour le développement, mais cela ne changerait rien. Nous pourrions toujours utiliser webpack pour créer un fichier concaténé et l'utiliser pour exécuter babel sur le code de tous les navigateurs.
Tout cela est gagné en n'utilisant pas de balises de script et en utilisant AMD