Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Einführung in die JavaScript-Modularisierung und SeaJs-Quellcode-Analyse

Detaillierte Einführung in die JavaScript-Modularisierung und SeaJs-Quellcode-Analyse

黄舟
黄舟Original
2017-03-10 15:31:581237Durchsuche

Die Struktur von Webseiten wird immer komplexer. Sie kann als einfache APP betrachtet werden. Wenn Sie den gesamten Code wie zuvor in eine Datei packen, treten einige Probleme auf:

  • Globale Variablen beeinflussen sich gegenseitig

  • JavaScript-Dateien werden größer, was sich auf die Ladegeschwindigkeit auswirkt

  • Die Struktur ist verwirrend und schwer zu warten

mit dem Backend (wie Java) vergleicht, erkennt man die offensichtliche Lücke. Im Jahr 2009 gründete Ryan Dahl das Projekt node.js zur Verwendung von JavaScript für die Serverprogrammierung, was die offizielle Geburtsstunde der „modularen JS-Programmierung“ markierte.

Grundprinzip

Ein Modul ist eine Sammlung von Funktionen. Dann können Sie eine große Datei in einige kleine Dateien aufteilen, in jeder Datei unterschiedliche Funktionen definieren und diese dann in HTML einführen:

var module1 = new Object({
    _count : 0,
    m1 : function (){
        //...
    },
    m2 : function (){
        //...
    }
});
Der Nachteil dabei ist, dass alle Mitglieder im Modul exponiert sind! Wir wissen, dass auf die lokalen Variablen der Funktion nicht von außen zugegriffen werden kann, daher können wir die Funktion zur sofortigen Ausführung zur Optimierung verwenden:

var module1 = (function(){
    var _count = 0;
    var m1 = function(){
        //...
    };
    var m2 = function(){
        //...
    };
    return {
        m1 : m1, m2 : m2
    };
})();
Jeder kann Module auf verschiedene Arten definieren, wenn sie alle bestimmten folgen können Spezifikationen, Der Vorteil wäre riesig: Man kann sich gegenseitig referenzieren!

Modulspezifikation

Das math.js-Modul ist in node.js wie folgt definiert:

function add(a, b){
    return a + b;
}
exports.add = add;
Bei Verwendung in anderen Modulen verwenden Sie einfach die globale Anforderungsfunktion Laden Sie es:

var math = require('math');
math.add(2,3);
Es gibt kein Problem mit der Synchronisierung auf dem Server, aber der Browser kann dies nicht in einer Netzwerkumgebung tun, daher gibt es eine asynchrone AMD-Spezifikation:

require(['math'], function (math) {// require([module], callback);
    math.add(2, 3);
});
Modul Die Definition lautet wie folgt (Module können von anderen Modulen abhängen):

define(function (){ // define([module], callback);
    var add = function (x,y){
        return x+y;
    };
    return { add: add };
});
Sie können viele andere Ressourcen mit RequireJS laden (siehe hier), was sehr gut und leistungsstark ist! Ich verwende SeaJS häufiger bei der Arbeit und die verwendete Spezifikation heißt CMD. Es wird empfohlen (sollte sich auf den asynchronen Modus beziehen):

so faul wie möglich!

Der Unterschied zwischen der Verarbeitungsmethode abhängiger Module und AMD ist:

AMD wird im Voraus ausgeführt (Abhängigkeiten stehen im Vordergrund) und CMD wird verzögert ausgeführt Art und Weise (Abhängigkeiten sind in der Nähe).

Die Art und Weise, ein Modul in CMD zu definieren, ist wie folgt:

define(function(require, exports, module) {
    var a = require('./a');
    a.doSomething();
    var b = require('./b');
    b.doSomething();
});
Informationen zur Verwendung finden Sie direkt im Dokument, daher werde ich hier nicht auf Details eingehen!

SeaJS-Quellcode-Analyse

Als ich zum ersten Mal mit der Modularisierung in Kontakt kam, hatte ich das Gefühl, dass das zu einfach ist, nicht wahr:

Set onload und src beim Erstellen des Skript-Tags!

Das ist tatsächlich der Fall, aber nicht ganz! Schauen wir uns zunächst den SeaJS-Code (sea-debug.js) an. Ein Modul kann während des Ladevorgangs die folgenden Zustände durchlaufen:

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
}
Das

-Objekt wird im Speicher verwendet, um Modulinformationen zu verwalten: Modul

function Module(uri, deps) {
    this.uri = uri
    this.dependencies = deps || [] // 依赖模块ID列表
    this.deps = {} // 依赖模块Module对象列表
    this.status = 0 // 状态
    this._entry = [] // 在模块加载完成之后需要调用callback的模块
}
Starten Sie das Modul auf dem Seite Das System muss die

-Methode verwenden: seajs.use

seajs.use(‘./main’, function(main) {// 依赖及回调方法
    main.init();
});
Die Gesamtlogik des Ladevorgangs ist in

zu sehen: Module.prototype.load

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]()
        }
    }
}
Die Gesamtlogik ist sehr glatt, daher werde ich nicht auf Details eingehen. Das einzige, was komplizierter ist, ist das Array

. Ich konnte im Internet keinen relativ leicht verständlichen Artikel finden, also habe ich mir den Code angesehen und ihn erraten, und ich habe ihn ungefähr verstanden. Denken Sie nur an sein Ziel: _entry

Wenn alle abhängigen Module geladen sind, führen Sie die Callback-Funktion aus!

Mit anderen Worten:

Der array_entry speichert eine Liste, welche Modulabhängigkeiten geladen werden dürfen, nachdem das aktuelle Modul geladen wurde (umgekehrte Beziehung der Abhängigkeiten)!

Wenn beispielsweise Modul A von den Modulen B, C und D abhängt, lautet der Status nach dem Bestehen wie folgt:

At Diesmal ist A

in 3, was bedeutet, dass es immer noch drei abhängige Module gibt, die nicht geladen wurden! Und wenn Modul B von den Modulen E und F abhängt, dann wird A beim Laden ebenfalls übergangen: remain

Es gibt mehrere Details:

  1. Module, die geladen wurden, werden nicht weitergegeben

  2. Module, die einmal weitergegeben wurden, werden nicht erneut weitergegeben

  3. Wenn das abhängige Modul geladen wird, wird es rekursiv weitergegeben.

Nachdem die Abhängigkeitsbeziehung beibehalten wurde, können Sie das Modul über

laden : Module.prototype.fetchsendRequest

    importScripts
  1. script
  2. führt dann die Methode
oder

aus basierend auf dem Ergebnis. Nachdem alle abhängigen Module geladen wurden, wird die load-Methode ausgeführt: erroronload

wobei
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
}
gleichbedeutend damit ist, dem entsprechenden Modul des Eintrags mitzuteilen: Eine Ihrer Abhängigkeitslisten ist bereits abgeschlossen! Und

bedeutet, dass alle Module, von denen es abhängt, geladen wurden! Dann wird zu diesem Zeitpunkt die Rückruffunktion ausgeführt: --entry.remainentry.remain === 0

Nachdem das Skript heruntergeladen wurde, wird die Methode
for (var i = 0, len = uris.length; i < len; i++) {
    exports[i] = cachedMods[uris[i]].exec();
}
if (callback) {
    callback.apply(global, exports)// 执行回调函数
}
sofort ausgeführt, um die Modulinformationen beizubehalten:

define

Wenn Abhängigkeiten nicht explizit angegeben werden, wird ParseDependencies verwendet, um das require()-Fragment in der regulären Matching-Methode zu verwenden (es ist eine gute Angewohnheit, eine Abhängigkeitsliste anzugeben).

Führen Sie dann die Methode
aus, um Moduldaten zu generieren:

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中也开始支持,但是浏览器支持还是比较堪忧的~~

Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in die JavaScript-Modularisierung und SeaJs-Quellcode-Analyse. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn