Maison  >  Article  >  interface Web  >  Introduction détaillée à la modularisation JavaScript et à l'analyse du code source SeaJs

Introduction détaillée à la modularisation JavaScript et à l'analyse du code source SeaJs

黄舟
黄舟original
2017-03-10 15:31:581280parcourir

La structure des pages Web devient de plus en plus complexe. Elle peut être considérée comme une simple application. Si vous mettez toujours tout le code dans un seul fichier comme avant, il y aura quelques problèmes : <.>

  • Les variables globales s'influencent mutuellement

  • Les fichiers JavaScript deviennent plus gros, affectant la vitesse de chargement

  • La structure est déroutant et difficile à maintenir

  • avec le backend (comme Java), vous pouvez voir l'écart évident. En 2009, Ryan Dahl crée le projet node.js, utilisant JavaScript pour la programmation serveur, qui marque la naissance officielle de la « programmation modulaire JS ».

Principe de base

Un module est un ensemble de fonctions. Vous pouvez ensuite diviser un gros fichier en quelques petits fichiers, définir différentes fonctions dans chaque fichier, puis les introduire en HTML :

L'inconvénient est que tous les membres du module sont exposés ! Nous savons que les variables locales de la fonction ne sont pas accessibles de l'extérieur, nous pouvons donc utiliser la fonction d'exécution immédiate pour optimiser :
var module1 = new Object({
    _count : 0,
    m1 : function (){
        //...
    },
    m2 : function (){
        //...
    }
});

Chacun peut définir des modules de différentes manières s'ils peuvent tous suivre certaines. cahier des charges, L'avantage serait énorme : vous pourrez vous référencer !
var module1 = (function(){
    var _count = 0;
    var m1 = function(){
        //...
    };
    var m2 = function(){
        //...
    };
    return {
        m1 : m1, m2 : m2
    };
})();

Spécification du module

Le module math.js est défini dans node.js comme suit :

Lorsqu'il est utilisé dans d'autres modules, utilisez simplement la fonction globale require pour chargez-le :
function add(a, b){
    return a + b;
}
exports.add = add;

Il n'y a aucun problème de synchronisation avec require sur le serveur, mais le navigateur ne peut pas le faire dans un environnement réseau, il existe donc une spécification AMD asynchrone :
var math = require(&#39;math&#39;);
math.add(2,3);

module La définition est la suivante (les modules peuvent dépendre d'autres modules) :
require([&#39;math&#39;], function (math) {// require([module], callback);
    math.add(2, 3);
});

Vous pouvez charger bien d'autres ressources avec RequireJS (voir ici), qui est très bon et puissant ! J'utilise SeaJS plus souvent au travail, et la spécification utilisée s'appelle CMD. Elle est recommandée (doit faire référence au mode asynchrone) :
define(function (){ // define([module], callback);
    var add = function (x,y){
        return x+y;
    };
    return { add: add };
});

aussi paresseux que possible !

La différence entre la méthode de traitement des modules dépendants et AMD est :

AMD est exécuté à l'avance (les dépendances sont devant), et CMD est exécuté en différé manière (les dépendances sont à proximité).

La façon de définir un module dans CMD est la suivante :

Pour l'utilisation, veuillez vous référer directement au document, je n'entrerai donc pas dans les détails ici !
define(function(require, exports, module) {
    var a = require(&#39;./a&#39;);
    a.doSomething();
    var b = require(&#39;./b&#39;);
    b.doSomething();
});

Analyse du code source SeaJS

Quand je suis entré en contact pour la première fois avec la modularisation, j'ai trouvé que c'était trop simple, n'est-ce pas :

Définissez onload et src lors de la création de la balise script !

C’est effectivement le cas, mais pas entièrement ! Commençons par regarder le code SeaJS (sea-debug.js). Un module peut passer par les états suivants pendant le processus de chargement :

L'objet
var STATUS = Module.STATUS = {
    // 1 - The `module.uri` is being fetched
    FETCHING: 1,
    // 2 - The meta data has been saved to cachedMods
    SAVED: 2,
    // 3 - The `module.dependencies` are being loaded
    LOADING: 3,
    // 4 - The module are ready to execute
    LOADED: 4,
    // 5 - The module is being executed
    EXECUTING: 5,
    // 6 - The `module.exports` is available
    EXECUTED: 6,
    // 7 - 404
    ERROR: 7
}
est utilisé en mémoire pour conserver les informations du module :

Modul

Démarrer le module sur le page Le système doit utiliser la méthode
function Module(uri, deps) {
    this.uri = uri
    this.dependencies = deps || [] // 依赖模块ID列表
    this.deps = {} // 依赖模块Module对象列表
    this.status = 0 // 状态
    this._entry = [] // 在模块加载完成之后需要调用callback的模块
}
 :

seajs.use

La logique globale du processus de chargement peut être vue dans
seajs.use(‘./main’, function(main) {// 依赖及回调方法
    main.init();
});
 :

Module.prototype.load

La logique globale est très fluide, donc je n'entrerai pas dans les détails. La seule chose qui est plus compliquée est le tableau
Module.prototype.load = function() {
    var mod = this
    if (mod.status >= STATUS.LOADING) {
        return
    }
    mod.status = STATUS.LOADING
    var uris = mod.resolve() // 解析依赖模块的URL地址
    emit("load", uris)
    for (var i = 0, len = uris.length; i < len; i++) {
        mod.deps[mod.dependencies[i]] = Module.get(uris[i])// 从缓存取或创建
    }
    mod.pass(); // 将entry传递给依赖的但还没加载的模块
    if (mod._entry.length) {// 本模块加载完成
        mod.onload()
        return
    }
    var requestCache = {};
    var m;
    // 加载依赖的模块
    for (i = 0; i < len; i++) {
        m = cachedMods[uris[i]]
        if (m.status < STATUS.FETCHING) {
            m.fetch(requestCache)
        } else if (m.status === STATUS.SAVED) {
            m.load()
        }
    }
    for (var requestUri in requestCache) {
        if (requestCache.hasOwnProperty(requestUri)) {
            requestCache[requestUri]()
        }
    }
}
. Je n'ai pas trouvé d'article facile à comprendre sur Internet, alors j'ai regardé le code et je l'ai deviné, et je l'ai à peu près compris. En fait, rappelez-vous simplement son objectif :

_entry

Quand. tous les modules dépendants sont chargés. Exécutez ensuite la fonction de rappel !

En d'autres termes :

Le array_entry stocke une liste des dépendances de module qui peuvent être chargées après le chargement du module actuel (relation inverse des dépendances) !

Par exemple, si le module A dépend des modules B, C et D, alors le statut après réussite est le suivant :

À cette fois A Le

in est 3, ce qui signifie qu'il a encore trois modules dépendants qui n'ont pas été chargés ! Et si le module B dépend des modules E et F, alors A sera également distribué lors de son chargement :

remain

Il y a plusieurs détails :

    Les modules qui ont été chargés ne seront pas propagés
  1. Les modules qui ont été propagés une fois ne seront pas propagés à nouveau
  2. Si le module dépendant est en cours de chargement, il se propagera de manière récursive
  3. Après avoir maintenu la relation de dépendance, vous pouvez charger le module via
  4. . Il existe deux
méthodes d'implémentation. :

Module.prototype.fetchsendRequest

importScripts
  1. script
  2. puis exécute la méthode
  3. ou
en fonction du résultat. Une fois tous les modules dépendants chargés, la méthode

sera exécutée : loaderroronload

équivaut à indiquer au module correspondant de l'entrée : une de vos listes de dépendances est déjà complétée ! Et
Module.prototype.onload = function() {
    var mod = this
    mod.status = STATUS.LOADED
    for (var i = 0, len = (mod._entry || []).length; i < len; i++) {
        var entry = mod._entry[i]
        if (--entry.remain === 0) {
            entry.callback()
        }
    }
    delete mod._entry
}
signifie que tous les modules dont il dépend ont été chargés ! Ensuite, la fonction de rappel sera exécutée à ce moment :

--entry.remainentry.remain === 0Une fois le script téléchargé, la méthode

sera exécutée immédiatement pour conserver les informations du module :
for (var i = 0, len = uris.length; i < len; i++) {
    exports[i] = cachedMods[uris[i]].exec();
}
if (callback) {
    callback.apply(global, exports)// 执行回调函数
}

define Lorsque les dépendances ne sont pas explicitement spécifiées, ParseDependencies sera utilisé pour utiliser le fragment require() dans la méthode de correspondance régulière (c'est une bonne pratique de spécifier une liste de dépendances).

Exécutez ensuite la méthode

pour générer les données du module :
var exports = isFunction(factory) ?
    factory.call(mod.exports = {}, require, mod.exports, mod) :
    factory

然后执行你在seajs.use中定义的callback方法:

if (callback) {
    callback.apply(global, exports)
}

当你写的模块代码中require时,每次都会执行factory方法:

function require(id) {
    var m = mod.deps[id] || Module.get(require.resolve(id))
    if (m.status == STATUS.ERROR) {
        throw new Error(&#39;module was broken: &#39; + m.uri)
    }
    return m.exec()
}

到这里核心的逻辑基本上讲完了,补一张状态的转换图:

以后在用的时候就可以解释一些诡异的问题了!

总结

模块化非常好用,因此在ECMAScript 6中也开始支持,但是浏览器支持还是比较堪忧的~~

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en 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