Maison > interface Web > js tutoriel > Conception du framework JavaScript, module de notes de lecture, chargement des compétences system_javascript

Conception du framework JavaScript, module de notes de lecture, chargement des compétences system_javascript

WBOY
Libérer: 2016-05-16 16:29:23
original
1232 Les gens l'ont consulté

Le chargement des modules consiste en fait à diviser js en plusieurs modules pour faciliter le développement et la maintenance. Par conséquent, lors du chargement de nombreux modules js, ils doivent être chargés dynamiquement pour améliorer l'expérience utilisateur.

Avant de présenter la bibliothèque de chargement de modules, introduisons d'abord une méthode.

Méthode de chargement dynamique js :

Copier le code Le code est le suivant :

fonction loadJs(url, rappel){
var node = document.createElement("script");
node[window.addEventListener ? "onload": "onreadystatechange"] = function(){
If(window.addEventListener || /loaded|complete/i.test(node.readyState)){
rappel();
   node.onreadystatechange = null;
 >
node.onerror = function(){};
Noeud.src = url;
var head = document.getElementsByTagName("head")[0];
head.insertBefore(node,head.firstChild); //Insérer avant le premier nœud de head pour éviter une erreur lors de l'utilisation de appendChild avant la fermeture de l'étiquette head dans IE6.
>

Étant donné que Situ Zhengmei utilise le framework de masse qu'il a écrit pour introduire le chargement de modules, les plus couramment utilisés dans l'industrie sont require.js et sea.js. Je pense donc qu’il a une forte personnalité.

Permettez-moi de parler du processus de chargement des modules de sea.js :

Page chaojidan.jsp, dans la balise head, introduisez sea.js, vous obtiendrez alors l'objet seajs.

Introduisez index.js en même temps.

Le code de index.js est le suivant :

Copier le code Le code est le suivant :
seajs.use(['./a','jquery'],function(a,$){
var num = a.a;
$('#J_A').text(num);
})

a.js :

Copier le code Le code est le suivant :
définir(fonction(exiger,exports,module){
var b = require('./b');
var a = fonction(){
           return 1 parseInt(b.b());
>
exports.a = a;
})

b.js :

Copier le code Le code est le suivant :
définir(fonction(exiger,exports,module){
var c = require('./c');
var b = fonction(){

           return 2 parseInt(c.c());
>
exports.b = b;
})

c.js :


Copier le code Le code est le suivant :
définir(fonction(exiger,exports,module){
var c = fonction(){
Retour 3 ;
>
exports.c = c;
})

On peut voir de ce qui précède que le module a dépend de b, et b dépend de c.

Lorsque le programme entre dans index.js, seajs appellera la méthode use.

Copier le code Le code est le suivant :
seajs.use = function(ids, rappel) {
globalModule._use(ids, rappel)
>

Remarque : lorsque globalModule est initialisé pour seajs (lorsque sea.js est introduit), l'instance de Module est var globalModule = new Module(util.pageUri, STATUS.COMPILED)
À ce moment, les identifiants -> ['./a','jquery'], callback -> function(a,$){var num = a.a;$('#J_A').text(num);}

GlobalModule._use(ids, callback)

sera appelé ensuite

Copier le code Le code est le suivant :

Module.prototype._use = function(ids, rappel) { 
var uris = solve(ids, this.uri); //Résoudre['./a','jquery']
This._load(uris, function() { //Calculez le a analysé, l'adresse du module jquery [url1, url2] et appelez la méthode _load.
//util.map : permet à tous les membres de données d'exécuter une fonction spécifiée à la fois et renvoie un nouveau tableau, qui est le résultat du rappel exécuté par les membres du tableau d'origine
var args = util.map(uris, fonction(uri) {

                                                                                                                                                                                  return uri ? cachedModules[uri]._compile(): null;//Si l'URL existe, appelez la méthode _compile.
  })
   if (callback) { callback.apply(null, args) } 
})
>

Parce qu'après avoir appelé la méthode _load, deux fonctions de rappel apparaîtront, nous marquons donc function(a,$){var num = a.a;$('#J_A').text(num);} comme callback1,
Marquez cette méthode de rappel._load(uris, function() { }) comme callback2. La méthode de résolution consiste à résoudre l’adresse du module, je n’entrerai donc pas dans les détails ici.
Enfin, l'uris dans var uris = solve(ids, this.uri) est analysé dans ['http://localhost/test/SEAJS/a.js','http://localhost/test/SEAJS/lib/juqery /1.7.2/juqery-debug.js'], la résolution du chemin du module est terminée.

Et this._load

sera exécuté ensuite.

Copier le code Le code est le suivant :

// La méthode _load() détermine principalement quels fichiers de ressources ne sont pas encore prêts. Si tous les fichiers de ressources sont à l'état prêt, callback2
sera exécuté. // Dans ce processus, les dépendances circulaires seront également jugées et les js déchargés seront chargés
Module.prototype._load = function(uris, callback2) { 
//util.filter : permet à tous les membres de données d'exécuter une fonction spécifiée à la fois et renvoie un nouveau tableau, qui est le membre qui renvoie vrai après que le membre d'origine du tableau exécute le rappel
//unLoadedUris est le tableau uri du module qui n'a pas été compilé
var unLoadedUris = util.filter(uris, function(uri) {
//Renvoie le membre dont la valeur booléenne de la fonction d'exécution est vraie. Il renvoie vrai lorsque l'uri existe et n'existe pas dans la variable interne cacheModules ou que sa valeur d'état dans les informations stockées est inférieure à STATUS.READY
. // La valeur STATUS.READY est 4. Si elle est inférieure à quatre, il est possible qu'elle soit en cours d'obtention ou de téléchargement.
Renvoie l'uri && (!cachedModules[uri] ||
cachedModules[uri].status });  
//Si tous les modules dans uris sont prêts, exécutez le rappel et quittez le corps de la fonction (à ce moment, la méthode _compile du module sera appelée).
var longueur = unLoadedUris.length
​if (longueur === 0) { callback2() return }
//Le nombre de modules qui n'ont pas encore été chargés
var reste = longueur
//Créez une fermeture et essayez de charger les modules qui ne sont pas chargés
pour (var i = 0; i < longueur; i ) {
(fonction(uri) {
//Déterminer si les informations de stockage de l'uri n'existent pas dans la variable interne cachedModules, instancier un objet Module
        var module = cachedModules[uri] ||
(cachedModules[uri] = nouveau Module(uri, STATUS.FETCHING))
//Si la valeur d'état du module est supérieure ou égale à 2, cela signifie que le module a été téléchargé et existe déjà localement. A ce moment, onFetched()
est exécuté. //Sinon, appelez fetch(uri, onFetched) et essayez de télécharger le fichier de ressources une fois le fichier de ressources téléchargé, onload sera déclenché et la méthode de rappel onFetched sera exécutée dans onload.
          module.status >= STATUS.FETCHED ? onFetched() : fetch(uri, onFetched)
         fonction onFetched() {
            module = cachedModules[uri]
//Lorsque la valeur d'état du module est supérieure ou égale à STATUS.SAVED, cela signifie que toutes les informations de dépendances du module ont été obtenues
                if (module.status >= STATUS.SAVED) {
                    //getPureDependencies : obtenez le tableau de dépendances sans dépendances circulaires
            var deps = getPureDependencies(module)
​​​​​​ //Si le tableau de dépendances n'est pas vide
                 if (deps.length) {
//Exécutez à nouveau la méthode _load() jusqu'à ce que toutes les dépendances soient chargées et que le rappel soit exécuté
                   Module.prototype._load(deps, function() {
cb(module)
               })
            }
//Si le tableau de dépendances est vide, exécutez directement cb(module)
             autre {
cb(module)
            }
          }
                       // Si l'acquisition échoue, par exemple 404 ou n'est pas conforme à la spécification modulaire
​​​​​ //Dans ce cas, module.status restera à FETCHING ou FETCHED
          autre {
cb()
          }
>
})(unLoadedUris[i])
>
// Méthode cb - rappel exécuté après le chargement de tous les modules
Fonction cb(module) {
// Si les informations de stockage du module existent, modifiez la valeur d'état dans ses informations de stockage de module en STATUS.READY
module && (module.status = STATUS.READY)
// Exécute le rappel uniquement lorsque tous les modules sont chargés.
--remain === 0 && callback2()
>
>
>

La longueur du tableau de unLoadedUris ici est de 2, ['http://localhost/test/SEAJS/a.js','http://localhost/test/SEAJS/lib/juqery/1.7.2/juqery- debug .js'], donc deux fermetures nommées avec des chemins js seront ensuite générées.

Prenons http://localhost/test/SEAJS/a.js comme exemple
Suivant : Tout d'abord, un module sera créé :

Copier le code Le code est le suivant :

cachedModules('http://localhost/test/SEAJS/a.js') = nouveau module('http://localhost/test/SEAJS/a.js',1)
module.status >= STATUS.FETCHED ? onFetched() : fetch(uri, onFetched)

Comme le module a n'est pas chargé pour le moment, fetch(uri, onFetched) sera exécuté ensuite, c'est-à-dire fetch('http://localhost/test/SEAJS/a.js', onFetched).

Copier le code Le code est le suivant :

fonction récupérer(uri, onFetched) {
//Remplacez l'uri par la nouvelle adresse de requête selon les règles de la map
var requestUri = util.parseMap(uri)
// Tout d'abord, vérifiez s'il existe un enregistrement requestUri dans la liste obtenue
Si (fetchedList[requestUri]) {
// À ce moment, actualisez les informations de stockage du module de l'uri d'origine vers le requestUri redéfini via la map
cachedModules[uri] = cachedModules[requestUri]
// Exécute onFetched et retourne, ce qui signifie que le module a été obtenu avec succès
onFetched()
       revenir
>
//Interroger les informations de stockage de requestUri dans la liste d'acquisition
Si (fetchingList[requestUri]) {
//Ajoute le callback correspondant à l'uri dans la callbacklist et renvoie
CallbackList[requestUri].push(onFetched) //S'il est en cours de récupération, poussez la méthode de rappel onFetched de ce module dans le tableau et revenez.
       revenir
>
// Si le module que vous essayez de récupérer n'apparaît pas dans fetchedList et fetchingList, ajoutez ses informations respectivement dans la liste de requêtes et la liste de rappel
​ fetchingList[requestUri] = true
​ callbackList[requestUri] = [onFetched]
// Le récupère
Module._fetch(
demandeUri,
        fonction() {
            fetchedList[requestUri] = true
// Met à jour l'état du module
// Si module.status est égal à STATUS.FECTCHING, modifiez le statut du module en FETCHED
          var module = cachedModules[uri]
Si (module.status === STATUS.FETCHING) {
               module.status = STATUS.FETCHED
          }
            if (fetchingList[requestUri]) {
               supprimer fetchingList[requestUri]
          }
// Appels callbackList Exécution unifiée des rappels
            if (callbackList[requestUri]) {
              util.forEach(callbackList[requestUri], function(fn) {
Fn() // fn est la méthode onfeched correspondant au module A.
             })
                 supprimer la liste de rappel[requestUri]
          }
},
​​​​ config.charset
)
>

Ensuite, Module._fetch() sera exécuté. La fonction de rappel ici est appelée callback3.

Cette méthode consiste à appeler la méthode loadJs pour télécharger dynamiquement un fichier.js. (Comme il y a un et jquery, deux nouveaux scripts seront créés.) Il y a une question ici si vous créez un nouveau script et l'ajoutez au head, le fichier js sera téléchargé. Cependant, dans seajs, il ne le sera pas. sera téléchargé, mais attendra jquery. Le script ne sera téléchargé qu'après sa création et son ajout à l'en-tête (le débogueur Google définit un point d'arrêt et affiche toujours en attente). Pourquoi est-ce ?
(Il est recommandé de lire ici : http://ux.sohu.com/topics/50972d9ae7de3e752e0081ff. Ici, je parlerai de problèmes supplémentaires. Vous savez peut-être pourquoi nous devrions utiliser moins de table pour la mise en page, car la table rend l'arbre. Lors de la mise en page, plusieurs calculs sont nécessaires, alors que div n'en a besoin que d'un. En même temps, l'intervieweur du commerce électronique Midea m'a dit que le tableau doit être entièrement analysé avant d'être affiché, et le div. sera affiché dans la mesure où il est analysé. Les balises seront affichées en segments en fonction du corps. Par conséquent, dans IE6, 7 et 8, si vous utilisez innerHTML pour créer un "

", < ;tbody>< y sera automatiquement ajouté.
Une fois le téléchargement réussi, il sera analysé et exécuté, et la méthode de définition sera exécutée. Le code du module a sera exécuté en premier.
définir(id,deps,function(){}) méthode d'analyse

Copier le code Le code est le suivant :

//définir la définition, id : identifiant du module, deps : dépendance du module, usine
Module._define = function(id, deps, usine) {
//Résoudre les dépendances //Si deps n'est pas un type tableau et que factory est une fonction
 if (!util.isArray(deps) && util.isFunction(factory)) { // Faites régulièrement correspondre la chaîne require dans le corps de la fonction et formez un tableau pour renvoyer et attribuer la valeur à deps
   deps = util.parseDependencies(factory.toString())
>
//Définir les méta-informations
 var méta = { id : id, dépendances : deps, usine : usine } 
​ if (document.attachEvent) {
// Récupère le nœud du script courant
  var script = util.getCurrentScript()
// Si le nœud de script existe
   si (script) {
     // Récupère l'adresse uri d'origine
     dérivéUri = util.unParseMap(util.getScriptAbsoluteSrc(script)) >
     if (!derivedUri) {
     util.log('Échec de la déduction de l'URI du script interactif pour :', factory.toString(), 'warn')
     >
   >
.........
>

define effectuera d'abord un jugement sur factory pour déterminer s'il s'agit d'une fonction (la raison est que Define peut également inclure des fichiers et des objets)

S'il s'agit d'une fonction, alors la fonction sera obtenue via factory.toString(), et la dépendance de a.js sera obtenue par correspondance régulière, et la dépendance sera enregistrée dans deps

Pour a.js, sa dépendance est b.js, donc deps est ['./b']

Et enregistrez les informations de a.js var meta = { id: id, dependencies: deps, factory: factory }

Pour a.js meta = { id : undefined , dependencies : ['./b'] , factory : function(xxx){xxx}}

Dans les navigateurs IE 6-9, vous pouvez obtenir le chemin du js en cours d'exécution. Cependant, dans les navigateurs standard, cela n'est pas réalisable, attribuez donc temporairement les méta-informations à anonymeModuleMeta = méta.

Ensuite, onload est déclenché et la méthode de rappel callback3 sera appelée. Cette méthode de rappel modifiera la valeur d'état du module de rappel actuel (a.js) et la définira sur module.status = STATUS.FETCHED.

Ensuite, le rappel correspondant à a.js dans la file d'attente de rappel callbackList sera exécuté uniformément, ce qui est onFetched.

La méthode onFetched vérifiera si le module a a des modules dépendants. Parce que a dépend de b, _load() est exécuté sur b.js dont dépend le module a.

téléchargera le module b, puis exécutera d'abord la méthode de définition de jquery. Parce que jquery ne dépend pas des modules, après le rappel onload. onFetched appelle la méthode cb.

Lorsque b est implémenté selon le même processus que a, le module c sera téléchargé. Enfin, les modules c, b et a sont tous téléchargés et exécutés, et une fois le chargement terminé, la méthode cb sera également appelée (d'abord c, puis b, puis c)

Une fois que tous les modules sont prêts, la méthode callback2 sera appelée.
Enfin rappelez-vous à callback2, exécutez la méthode _compile des modules a et jquery :

Compilez d'abord le module a.js et la fonction du module a est exécutée. Parce que a contient require(b.js), la fonction du module b sera exécutée.
La fonction du module a commence à s'exécuter
La fonction du module b commence à s'exécuter
La fonction du module c commence à s'exécuter
La fonction du module c est exécutée
La fonction du module b est exécutée
La fonction du module a est exécutée

Enfin, exécutez la fonction jquery.

Une fois la compilation terminée, callback1 est exécuté et les objets a et jquery peuvent être utilisés.

PS : La version seajs a été mise à jour et il n'y a plus de méthode _compile maintenant. (Chacun va le voir par soi-même, moi aussi j'ai envie d'aller le voir)

Parlons ensuite du processus module compilation_compile de seajs.

Le premier est la compilation de a.js

Copier le code Le code est le suivant :

Module.prototype._compile = function() {
Module 126 var = ceci
127 // Si le module a été compilé, renvoie directement module.exports
128 si (module.status === STATUS.COMPILED) {
129        retour module.exports
130>
133 // 1. le fichier du module est 404.
134 // 2. le fichier du module n'est pas écrit avec un format de module valide.
135 // 3. autres cas d'erreur.
136 //Voici comment gérer certaines situations anormales et renvoyer directement null
137 si (module.status < STATUS.SAVED && !hasModifiers(module)) {
138 renvoie null
139 >
140 // Changer l'état du module en COMPILATION, indiquant que le module est en cours de compilation
141 module.status = STATUS.COMPILING
142
143 // Utilisé en interne au sein du module, c'est une méthode utilisée pour obtenir les interfaces fournies par d'autres modules (appelés sous-modules) et effectuer des opérations synchrones
144 fonction require(id) {
145 ​ ​ // Analyser le chemin du module en fonction de l'identifiant
146 var uri = résoudre (id, module.uri)
147​​​​//Récupérer le module depuis le cache du module (notez que le sous-module ici a en fait été téléchargé en tant que dépendance du module principal)
148 var enfant = cachedModules[uri]
149
150​​​​//Renvoyer simplement null lorsque l'uri n'est pas valide.
151​​​​//Si l'enfant est vide, cela peut seulement signifier que le remplissage des paramètres est erroné et que l'uri est incorrect, alors null est renvoyé directement
152   si (!enfant) {
153 renvoie nul
154      }
155
156​​​​ // Évite les appels circulaires.
157​​​​//Si l'état du sous-module est STATUS.COMPILING, renvoyez directement child.exports pour éviter de compiler le module à plusieurs reprises en raison de dépendances circulaires
158   if (child.status === STATUS.COMPILING) {
159               retour enfant.exportations
160     }
161​​​​//Pointe vers le module qui appelle le module actuel lors de l'initialisation. Selon cet attribut, la pile d'appels lors de l'initialisation du module peut être obtenue.
162   enfant.parent = module
163​​​​//Renvoyer le module.exports de l'enfant compilé
164    retour enfant._compile()
165 >
166 // Utilisé en interne par le module pour charger le module de manière asynchrone et exécuter le rappel spécifié une fois le chargement terminé.
167 require.async = function(ids, callback) {
168 module._use(ids, rappel)
169 >
170 // Utilisez le mécanisme d'analyse de chemin à l'intérieur du système de modules pour analyser et renvoyer le chemin du module. Cette fonction ne charge pas le module et renvoie uniquement le chemin absolu résolu.
171 require.resolve = function(id) {
172 renvoie la résolution (id, module.uri)
173 >
174 // Grâce à cet attribut, vous pouvez visualiser tous les modules chargés par le système de modules.
175 // Dans certains cas, si vous avez besoin de recharger un module, vous pouvez obtenir l'URI du module, puis supprimer ses informations en supprimant require.cache[uri]. De cette façon, vous l’aurez à nouveau la prochaine fois que vous l’utiliserez.
176 require.cache = cachedModules
177
178 // require est une méthode utilisée pour obtenir les interfaces fournies par d'autres modules.
179 module.require = require
180 // Exports est un objet utilisé pour fournir des interfaces de module avec le monde extérieur.
181 module.exports = {}
182 var usine = module.factory
183
184 // Lorsque factory est une fonction, elle représente la méthode de construction du module. En exécutant cette méthode, vous pouvez obtenir l'interface fournie par le module.
185 si (util.isFunction(factory)) {
186 compileStack.push(module)
187 runInModuleContext(usine, module)
188 compileStack.pop()
189 >
190 // Lorsque factory est un type non fonctionnel tel qu'un objet ou une chaîne, cela signifie que l'interface du module est l'objet, la chaîne ou d'autres valeurs.
191 // Tel que : définir({ "foo": "bar" });
192 // Par exemple : définir('Je suis un modèle. Je m'appelle {{name}}.');
193 sinon if (usine !== non défini) {
194 module.exports = usine
195 >
196
197 // Changer l'état du module en COMPILED, indiquant que le module a été compilé
198 module.status = STATUS.COMPILED
199 // Exécuter la modification de l'interface du module via seajs.modify()
200 execModifiers (module)
201 module de retour.exports
202 >

Copier le code Le code est le suivant :

if (util.isFunction(factory)) {
186 compileStack.push(module)
187 runInModuleContext(usine, module)
188 compileStack.pop()
189 >

Voici pour initialiser module.export. Méthode runInModuleContext :

Copier le code Le code est le suivant :

// Exécuter le code du module selon le contexte du module
489 fonction runInModuleContext(fn, module) {
490 // Passer les deux paramètres liés au module et au module lui-même
491 // les exports sont utilisés pour exposer les interfaces
492 // require est utilisé pour obtenir les modules dépendants (synchronisation) (compilation)
493 var ret = fn(module.require, module.exports, module)
494 // Prise en charge du formulaire d'interface d'exposition de la valeur de retour, tel que :
495 // retour {
496 // fn1 : xx
497 // ,fn2 : xx
498 // ...
499 // >
500 si (ret !== non défini) {
501 module.exports = ret
502 >
503 >

Exécutez la méthode de fonction dans a.js, puis var b = require("b.js"),
sera appelé La méthode require renverra la valeur de retour de la méthode de compilation de b, et il y a var c = require('c.js') dans le module b.
A ce moment, la méthode de compilation de c sera appelée, puis la fonction de c sera appelée. Dans c, si vous souhaitez exposer l'objet, ou renvoyer l'objet c, alors les exportations du module c = c. Ou directement module.export = c; bref, module c.export = c sera renvoyé à la fin ; donc var c = module c.export = c, dans le module b, vous pouvez utiliser la variable c pour appeler la méthode et la méthode de l'objet c dans la propriété du module c.
Par analogie, éventuellement le module a peut également appeler les propriétés et méthodes de l'objet b dans le module b.
Quel que soit le module, tant que module.export = xx module est utilisé, d'autres modules peuvent utiliser require("xx module") pour appeler diverses méthodes dans le module xx.
Le statut final du module deviendra module.status = STATUS.COMPILED.

Copier le code Le code est le suivant :

Module.prototype._use = function(ids, rappel) { 
var uris = solve(ids, this.uri); //Résoudre['./a','jquery']
This._load(uris, function() { //Calculez le a analysé, l'adresse du module jquery [url1, url2] et appelez la méthode _load.
//util.map : permet à tous les membres de données d'exécuter une fonction spécifiée à la fois et renvoie un nouveau tableau, qui est le résultat du rappel exécuté par les membres du tableau d'origine
var args = util.map(uris, fonction(uri) {

                                                                                                                                                                                  return uri ? cachedModules[uri]._compile(): null;//Si l'URL existe, appelez la méthode _compile.
  })
   if (callback) { callback.apply(null, args) } 
})
>

À ce moment args = [module a.export, module jquery.export];

Copier le code Le code est le suivant :

seajs.use(['./a','jquery'],function(a,$){
var num = a.a;
$('#J_A').text(num);
})

À l'heure actuelle, a et $ en fonction sont le module a.export et le module jquery.export.

Parce que j'étudie actuellement le code source jquery et la conception du framework jquery, j'aimerais partager quelques expériences :
J'ai lu beaucoup d'analyses du code source de jquery sur Internet, mais je n'en pouvais plus. Cela n'a pas beaucoup de sens. Je recommande l'analyse du code source jquery de Miaowei Classroom.

La conception du framework JavaScript de Situ Zhengmei est personnellement difficile, mais après l'avoir lu attentivement, vous deviendrez un ingénieur front-end senior.

Je vous recommande d'apprendre le sea.js de Yu Bo et de l'utiliser. Après tout, il a été créé par les Chinois eux-mêmes. Les nouveaux projets ou refactorings de notre entreprise seront réalisés à l'aide de seajs.

L'étape suivante consiste à lire le code source des handbars modulaires et du backbone de mvc ou de l'angular de mvvm. Ici, j'espère que quelqu'un pourra me donner des conseils sur les livres à lire, les sites Web à lire et les vidéos à regarder pour apprendre rapidement.

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal