Maison  >  Article  >  php教程  >  Chargement des dépendances entre les modules seajs et exécution des modules

Chargement des dépendances entre les modules seajs et exécution des modules

高洛峰
高洛峰original
2016-12-28 13:55:151517parcourir

Cet article présente le chargement des dépendances entre les modules seajs et l'exécution des modules. N'entrons pas dans les détails ci-dessous.

Méthode de saisie

Chaque programme a une méthode de saisie, similaire à la fonction principale de c, et seajs ne fait pas exception. La démo de la série 1 utilise seajs.use() sur la page d'accueil, qui est la méthode de saisie. La méthode de saisie peut accepter 2 paramètres, le premier paramètre est le nom du module et le deuxième paramètre est la fonction de rappel. La méthode d'entrée définit un nouveau module, et ce module nouvellement défini dépend du module fourni par le paramètre d'entrée. Définissez ensuite la fonction de rappel du nouveau module à appeler après l'état chargé. Cette fonction de rappel exécute principalement les fonctions d'usine de tous les modules dépendants, et exécute enfin le rappel fourni par la méthode d'entrée.

// Public API
// 入口地址
seajs.use = function(ids, callback) {
 Module.preload(function() {
 Module.use(ids, callback, data.cwd + "_use_" + cid())
 })
 return seajs
}
 
// Load preload modules before all other modules
Module.preload = function(callback) {
 var preloadMods = data.preload
 var len = preloadMods.length
 
 if (len) {
 Module.use(preloadMods, function() {
  // Remove the loaded preload modules
  preloadMods.splice(0, len)
 
  // Allow preload modules to add new preload modules
  Module.preload(callback)
 }, data.cwd + "_preload_" + cid())
 }
 else {
 callback()
 }
}
 
// Use function is equal to load a anonymous module
Module.use = function (ids, callback, uri) {
 var mod = Module.get(uri, isArray(ids) ? ids : [ids])
 
 mod.callback = function() {
 var exports = []
 var uris = mod.resolve()
 
 for (var i = 0, len = uris.length; i < len; i++) {
  exports[i] = cachedMods[uris[i]].exec()
 }
 // 回调函数的入参对应依赖模块的返回值
 if (callback) {
  callback.apply(global, exports)
 }
 
 delete mod.callback
 }
 
 mod.load()
}

Module.preload est utilisé pour précharger les plug-ins fournis par seajs. Ce n'est pas une fonction principale et peut être ignoré. Module.use est la méthode principale. Comme mentionné précédemment, cette méthode crée un nouveau module et définit la fonction de rappel, et charge enfin tous les modules dépendants du nouveau module.

La méthode de chargement des dépendances

La méthode de chargement peut être considérée comme l'essence même de Seajs. Cette méthode charge principalement les modules dépendants et exécute les fonctions de rappel des modules dépendants en séquence. La fonction de rappel finale est le rappel du nouveau module créé via seajs.use("./name"), qui est mod.callback.

La méthode de chargement charge récursivement les modules dépendants. Si le module dépendant dépend également d'autres modules, alors chargez ce module. Ceci est réalisé via _waitings et _remain dans la classe Module.

Module.prototype.load = function() {
 var mod = this
 
 // If the module is being loaded, just wait it onload call
 if (mod.status >= STATUS.LOADING) {
 return
 }
 
 mod.status = STATUS.LOADING
 
 // Emit `load` event for plugins such as combo plugin
 var uris = mod.resolve()
 emit("load", uris, mod)
 
 var len = mod._remain = uris.length
 var m
 
 // Initialize modules and register waitings
 for (var i = 0; i < len; i++) {
 m = Module.get(uris[i])
 
 // 修改 依赖文件 的 _waiting属性
 if (m.status < STATUS.LOADED) {
  // Maybe duplicate: When module has dupliate dependency, it should be it&#39;s count, not 1
  m._waitings[mod.uri] = (m._waitings[mod.uri] || 0) + 1
 }
 else {
  mod._remain--
 }
 }
 
 // 加载完依赖,执行模块
 if (mod._remain === 0) {
 mod.onload()
 return
 }
 
 // Begin parallel loading
 var requestCache = {}
 
 for (i = 0; i < len; i++) {
 m = cachedMods[uris[i]]
 
 // 该依赖并未加载,则先fetch,将seajs.request函数绑定在对应的requestCache上,此时并未加载模块
 if (m.status < STATUS.FETCHING) {
  m.fetch(requestCache)
 }
 else if (m.status === STATUS.SAVED) {
  m.load()
 }
 }
 
 // Send all requests at last to avoid cache bug in IE6-9. Issues#808
 // 加载所有模块
 for (var requestUri in requestCache) {
 if (requestCache.hasOwnProperty(requestUri)) {
  // 此时加载模块
  requestCache[requestUri]()
 }
 }
}
 
// 依赖模块加载完毕执行回调函数
// 并检查依赖该模块的其他模块是否可以执行
Module.prototype.onload = function() {
 var mod = this
 mod.status = STATUS.LOADED
 
 if (mod.callback) {
 mod.callback()
 }
 console.log(mod)
 // Notify waiting modules to fire onload
 var waitings = mod._waitings
 var uri, m
 
 for (uri in waitings) {
 if (waitings.hasOwnProperty(uri)) {
  m = cachedMods[uri]
  m._remain -= waitings[uri]
  if (m._remain === 0) {
  m.onload()
  }
 }
 }
 
 // Reduce memory taken
 delete mod._waitings
 delete mod._remain
}

Initialisez d'abord les attributs _waitings et _remain du module. Si _remain vaut 0, cela signifie qu'il n'y a pas de dépendance ou que la dépendance est chargée, et la fonction onload peut être exécutée si elle n'est pas 0 ; , le module fetch n'est pas chargé. Il y a une petite astuce d'implémentation ici, qui consiste à charger toutes les dépendances en même temps : l'objet requestCache enregistre la fonction de chargement : (définie dans la fonction fetch)

if (!emitData.requested) {
 requestCache ?
  requestCache[emitData.requestUri] = sendRequest :
  sendRequest()
 }

Parmi eux, la fonction sendRequest est définie comme suit :

function sendRequest() {
 seajs.request(emitData.requestUri, emitData.onRequest, emitData.charset)
 }

Charger toutes les dépendances en parallèle Lorsque les dépendances sont chargées, le rappel onRequest est exécuté, bouillonne et charge les dépendances dépendantes jusqu'à ce qu'il n'y ait plus de modules dépendants.

Lorsque la dépendance de niveau supérieur ne dépend plus du module, exécutez la fonction onload, définissez l'état sur chargé dans le corps de la fonction, exécutez mod.callback, puis vérifiez et définissez l'attribut _waitings du module sur déterminer si le module inférieur est toujours S'il y a une dépendance, sinon, le mod.callback du module inférieur sera exécuté. Cette séquence de retour en arrière exécutera éventuellement le mod.callback du module anonyme créé via seajs.use.

Exemple

Utilisez un exemple simple pour démontrer le processus ci-dessus :

tst.html
 
<script>
  seajs.use(&#39;./b&#39;);
</script>
-------------------------------------
a.js
 
define(function(require,exports,module){
 exports.add = function(a,b){
  return a+b;
 }
})
------------------------------------
b.js
 
define(function(require,exports,module){
 var a = require("./a");
 console.log(a.add(3,5));
})

À l'aide des outils de débogage, vous pouvez voir l'ordre dans lequel le chargement est exécuté :

Chargement des dépendances entre les modules seajs et exécution des modules

Finalement, on constate que le code d'état du module anonyme est 4, ce qui signifie que le module n'a pas été exécuté. En effet, il n'y a pas de fonction usine définie pour l'anonyme. module et il ne peut pas être exécuté.

Exécution du module exec

L'exécution du module est appelée dans le mod.callback défini dans seajs.use, et toutes les méthodes d'exécution dépendantes sont appelées dans l'ordre pour exécuter le programme. logique. Il y a quelques mots-clés ou fonctions importants de commonJS dans la méthode exec, tels que require, exports, etc. Jetons un coup d'oeil :

Module.prototype.exec = function () {
 var mod = this
 
 // When module is executed, DO NOT execute it again. When module
 // is being executed, just return `module.exports` too, for avoiding
 // circularly calling
 if (mod.status >= STATUS.EXECUTING) {
 return mod.exports
 }
 
 mod.status = STATUS.EXECUTING
 
 // Create require
 var uri = mod.uri
 
 function require(id) {
 return Module.get(require.resolve(id)).exec()
 }
 
 require.resolve = function(id) {
 return Module.resolve(id, uri)
 }
 
 require.async = function(ids, callback) {
 Module.use(ids, callback, uri + "_async_" + cid())
 return require
 }
 
 // Exec factory
 var factory = mod.factory
 
 // 工厂函数有返回值,则返回;
 // 无返回值,则返回mod.exports
 var exports = isFunction(factory) ?
  factory(require, mod.exports = {}, mod) :
  factory
 
 if (exports === undefined) {
 exports = mod.exports
 }
 
 // Reduce memory leak
 delete mod.factory
 
 mod.exports = exports
 mod.status = STATUS.EXECUTED
 
 // Emit `exec` event
 emit("exec", mod)
 
 return exports
}

La fonction require obtient le module et exécute la fonction d'usine du module pour obtenir la valeur de retour. La méthode de résolution de la fonction require obtient l'URL absolue du nom du module correspondant, et la méthode async de la fonction require charge de manière asynchrone les dépendances et exécute les rappels. Pour la valeur de retour de la méthode factory, si la méthode factory est un objet, c'est la valeur des exportations ; ou si la méthode factory a une valeur de retour, c'est la valeur des exportations ou la valeur de module.exports ; valeur des exportations. Lorsque la valeur des exportations peut être obtenue, définissez le statut sur exécuté.

Une chose à noter : lorsque vous souhaitez exporter un objet en attribuant une valeur aux exportations,

define(function(require,exports,module){
 exports ={
  add: function(a,b){
    return a+b;
  }
 }
})

échoue. Nous jugeons la valeur des exportations finales exportées en exécutant ce qui précède. Méthode. Premièrement, la fonction n'a pas de valeur de retour. Deuxièmement, mod.exports n'est pas défini et les exportations finales exportées ne sont pas définies. Pourquoi cela arrive-t-il ? Cela est dû à l’affectation de référence dans js. La stratégie d'affectation de js est "passer par partage". Bien que exports === module.exports initialement, lorsqu'un objet est affecté à exports, exports pointe vers l'objet, mais module.exports n'a pas encore été initialisé et n'est pas défini, donc Quelque chose va mal tourner. La manière correcte d'écrire

est

define(function(require,exports,module){
 module.exports ={
  add: function(a,b){
    return a+b;
  }
 }
})

Résumé

On peut dire que l'implémentation du module de base de seajs a été expliquée, et j'ai vu un beaucoup de compétences en codage et connaissance du mode de rappel. Ingéniosité et prise en compte des détails. Chaque partie du code prend en compte les dangers des fuites de mémoire et de ce décalage de référence de pointeur, et prend des précautions actives. Cet esprit mérite d'être appris.

Pour plus d'articles liés au chargement des dépendances entre les modules seajs et à l'exécution des modules, veuillez faire attention au site PHP chinois !

Déclaration:
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