>  기사  >  웹 프론트엔드  >  JavaScript 모듈화 및 SeaJs 소스 코드 분석에 대한 자세한 소개

JavaScript 모듈화 및 SeaJs 소스 코드 분석에 대한 자세한 소개

黄舟
黄舟원래의
2017-03-10 15:31:581297검색

웹 페이지의 구조가 점점 더 복잡해지고 있습니다. 여전히 모든 코드를 이전처럼 하나의 파일에 넣으면 다음과 같은 문제가 발생합니다. 🎜>

  • 전역 변수는 서로 영향을 미칩니다

  • JavaScript 파일이 커지면서 로딩 속도에 영향을 줍니다

  • 구조 혼란스럽고 유지 관리가 어렵습니다

를 백엔드(예: Java)와 비교하면 명백한 차이를 볼 수 있습니다. 2009년 Ryan Dahl은 서버 프로그래밍에 JavaScript를 사용하기 위해 node.js 프로젝트를 만들었고, 이는 "JS 모듈형 프로그래밍"의 공식적인 탄생을 의미합니다.

기본 원리

모듈은 큰 파일을 몇 개의 작은 파일로 분할하고 각 파일에 서로 다른 기능을 정의한 다음 HTML로 도입할 수 있습니다.

var module1 = new Object({
    _count : 0,
    m1 : function (){
        //...
    },
    m2 : function (){
        //...
    }
});

이것의 단점은 모듈의 모든 멤버가 노출된다는 것입니다! 함수의 로컬 변수는 외부에서 접근할 수 없다는 것을 알고 있으므로 즉시 실행 기능을 사용하여 최적화할 수 있습니다.

var module1 = (function(){
    var _count = 0;
    var m1 = function(){
        //...
    };
    var m2 = function(){
        //...
    };
    return {
        m1 : m1, m2 : m2
    };
})();

누구나 특정 사양을 따를 수 있다면 다양한 방식으로 모듈을 정의할 수 있습니다. 이점은 매우 클 것입니다. 서로 참조할 수 있습니다!

모듈 사양

math.js 모듈은 node.js에 다음과 같이 정의되어 있습니다.

function add(a, b){
    return a + b;
}
exports.add = add;

다른 모듈에서 사용할 경우 전역 require 함수를 사용하여 로드하면 됩니다. :

var math = require('math');
math.add(2,3);

서버에서는 require 동기화에는 문제가 없으나, 네트워크 환경에서는 브라우저가 이를 할 수 없기 때문에 비동기식 AMD 사양이 있습니다:

require(['math'], function (math) {// require([module], callback);
    math.add(2, 3);
});

모듈은 다음과 같이 정의됩니다. 다음과 같습니다(모듈은 다른 모듈에 따라 달라질 수 있음):

define(function (){ // define([module], callback);
    var add = function (x,y){
        return x+y;
    };
    return { add: add };
});

매우 훌륭하고 강력한 RequireJS(여기 참조)를 사용하면 다른 많은 리소스를 로드할 수 있습니다! 저는 직장에서 SeaJS를 더 자주 사용하는데, 사용되는 사양은 CMD라고 합니다. 권장합니다(비동기 모드 참조).

최대한 게으르게!

종속 모듈 처리 방식과 AMD의 차이점은

AMD가 미리 실행되고(종속 항목이 앞에 있음), CMD가 지연되어 실행되는 것입니다. 방식(종속성이 근처에 있음).

CMD에서 모듈을 정의하는 방법은 다음과 같습니다.

define(function(require, exports, module) {
    var a = require('./a');
    a.doSomething();
    var b = require('./b');
    b.doSomething();
});

사용방법은 문서를 직접 참고하시기 때문에 여기서는 자세히 다루지 않겠습니다!

SeaJS 소스 코드 분석

처음 모듈화를 접했을 때 너무 단순하다고 느꼈습니다.

onload를 설정하고 스크립트 태그를 생성할 때 src!

실제로는 그렇긴 하지만 전부는 아닙니다! SeaJS 코드(sea-debug.js)부터 살펴보겠습니다. 모듈은 로드 프로세스 중에 다음 상태를 거칠 수 있습니다.

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
}

개체는 모듈 정보를 유지하기 위해 메모리에서 사용됩니다. Modul

function Module(uri, deps) {
    this.uri = uri
    this.dependencies = deps || [] // 依赖模块ID列表
    this.deps = {} // 依赖模块Module对象列表
    this.status = 0 // 状态
    this._entry = [] // 在模块加载完成之后需要调用callback的模块
}
페이지에서 모듈 시스템을 시작하려면,

메소드: seajs.use

seajs.use(‘./main’, function(main) {// 依赖及回调方法
    main.init();
});

를 사용해야 합니다. 로딩 프로세스의 전체 로직은

: 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]()
        }
    }
}

에서 볼 수 있습니다. 전체적인 로직은 매우 매끄러우므로 생략하겠습니다. 더 복잡한 것은

배열입니다. 인터넷에서 비교적 이해하기 쉬운 글을 찾을 수 없어서 코드를 보고 추측해봤는데 사실 그 목적만 기억하면 됩니다: _entry

모든 종속 모듈이 로드되면 콜백 함수를 실행하세요!

즉,

array_entry는 현재 모듈이 로드된 후 로드될 수 있는 모듈 종속성의 목록을 저장합니다(종속성의 역관계) !

예를 들어 모듈 A가 모듈 B, C, D에 종속되는 경우 통과 후 상태는 다음과 같습니다.

At 이번에는

의 값이 3인데, 이는 아직 로드되지 않은 종속 모듈이 세 개 있다는 뜻입니다! 그리고 모듈 B가 모듈 E와 F에 의존하는 경우 A도 로드될 때 전달됩니다. remain

몇 가지 세부 정보가 있습니다.

  1. 로드된 모듈은 전파되지 않습니다.

  2. 한 번 전파된 모듈은 다시 전파되지 않습니다. > 종속 모듈이 로드되면 재귀적으로 전파됩니다.

  3. 종속 관계를 유지한 후

    을 통해 모듈을 로드할 수 있습니다. :

importScriptsModule.prototype.fetchsendRequest

    script
  1. 그런 다음
  2. 또는

    메소드를 실행합니다. 결과를 바탕으로. 모든 종속 모듈이 로드된 후

    메소드가 실행됩니다:
  3. 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
    }
여기서

는 항목에 해당하는 모듈에 종속성 목록 중 하나가 이미 완료되었음을 알리는 것과 같습니다! 그리고 load은 의존하는 모든 모듈이 로드되었음을 의미합니다! 그러면 이때 콜백 함수가 실행됩니다: error

for (var i = 0, len = uris.length; i < len; i++) {
    exports[i] = cachedMods[uris[i]].exec();
}
if (callback) {
    callback.apply(global, exports)// 执行回调函数
}
onload스크립트가 다운로드된 후 모듈 정보를 유지하기 위해

메서드가 즉시 실행됩니다.

--entry.remainentry.remain === 0은 종속성이 명시적으로 지정되지 않은 경우 사용됩니다.parseDependency는 일반 일치 메서드에서 require() 조각을 사용합니다(종속성 목록을 지정하는 것이 좋습니다).

define그런 다음

메소드를 실행하여 모듈 데이터를 생성합니다.
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中也开始支持,但是浏览器支持还是比较堪忧的~~

위 내용은 JavaScript 모듈화 및 SeaJs 소스 코드 분석에 대한 자세한 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.