이 글에서는 seajs 모듈 간의 종속성 로딩과 모듈 실행을 소개합니다. 아래에서는 자세히 설명하지 않겠습니다.
입력 방법
모든 프로그램에는 c의 주요 기능과 유사한 입력 방법이 있으며, seajs도 예외는 아닙니다. 시리즈 1의 데모에서는 홈페이지의 seajs.use()를 진입 방식으로 사용하고 있습니다. 진입 방법은 2개의 매개변수를 받을 수 있습니다. 첫 번째 매개변수는 모듈 이름이고 두 번째 매개변수는 콜백 함수입니다. 진입 방법은 새로운 모듈을 정의하며, 새로 정의된 이 모듈은 입력 매개변수가 제공하는 모듈에 따라 달라집니다. 그런 다음 로드된 상태 후에 호출될 새 모듈의 콜백 함수를 설정합니다. 이 콜백 함수는 주로 모든 종속 모듈의 팩토리 함수를 실행하고 마지막으로 진입 메소드에서 제공하는 콜백을 실행합니다.
// 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는 seajs에서 제공하는 플러그인을 미리 로드하는 데 사용됩니다. 이는 주요 기능이 아니므로 무시할 수 있습니다. Module.use는 앞서 언급한 것처럼 새로운 모듈을 생성하고 콜백 함수를 설정한 후 최종적으로 새로운 모듈의 모든 종속 모듈을 로드하는 핵심 메서드입니다.
의존성 로딩의 로드 방식
로드 방식은 seajs의 핵심이라고 할 수 있습니다. 이 메소드는 주로 종속 모듈을 로드하고 종속 모듈의 콜백 함수를 순차적으로 실행합니다. 최종 콜백 함수는 seajs.use("./name")을 통해 생성된 새 모듈의 콜백인 mod.callback입니다.
load 메소드는 종속 모듈을 재귀적으로 로드합니다. 종속 모듈도 다른 모듈에 종속되는 경우 이 모듈을 로드합니다. 이는 모듈 클래스의 _waitings 및 _remain을 통해 달성됩니다.
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'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 }
먼저 모듈의 _waitings 및 _remain 속성을 초기화합니다. _remain이 0이면 종속성이 없거나 종속성이 로드되었으며, 0이 아니면 onload 함수를 실행할 수 있음을 의미합니다. , 가져오기가 로드되지 않았습니다. 다음은 모든 종속성을 동시에 로드하는 약간의 구현 트릭입니다. requestCache 객체는 로드 함수를 저장합니다. (fetch 함수에 정의됨)
if (!emitData.requested) { requestCache ? requestCache[emitData.requestUri] = sendRequest : sendRequest() }
그 중 sendRequest 함수는 다음과 같이 정의됩니다.
function sendRequest() { seajs.request(emitData.requestUri, emitData.onRequest, emitData.charset) }
모든 종속성을 병렬로 로드합니다. 종속성이 로드되면 onRequest 콜백이 실행되고 버블링되며 종속 모듈이 없어질 때까지 종속 종속성을 로드합니다.
최상위 종속성이 더 이상 모듈에 종속되지 않으면 onload 함수를 실행하고 함수 본문에 상태를 로드됨으로 설정한 다음 mod.callback을 실행하고 모듈의 _waitings 속성을 확인하여 다음으로 설정합니다. 하위 모듈이 여전히 있는지 확인합니다. 종속성이 있는 경우 하위 모듈의 mod.callback이 실행됩니다. 이 역추적 시퀀스는 결국 seajs.use를 통해 생성된 익명 모듈의 mod.callback을 실행합니다.
예
간단한 예를 사용하여 위 프로세스를 보여줍니다.
tst.html <script> seajs.use('./b'); </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)); })
디버깅 도구를 사용하면 onload가 실행되는 순서를 확인할 수 있습니다.
마지막으로 익명 모듈의 상태 코드가 4임을 알 수 있는데, 이는 해당 모듈이 실행되지 않았음을 의미합니다. 실제로 익명 모듈에 대해 정의된 팩토리 함수가 없습니다.
모듈 실행 exec
모듈 실행은 seajs.use에 정의된 mod.callback에서 호출되며, 종속된 모든 exec 메소드가 순차적으로 호출되어 프로그램을 실행합니다. 논리. exec 메서드에는 require, 내보내기 등과 같은 commonJS의 몇 가지 중요한 키워드나 기능이 있습니다. 살펴보겠습니다:
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 }
require 함수는 모듈을 획득하고 모듈의 팩토리 함수를 실행하여 다음을 수행합니다. 반환 값을 얻습니다. require 함수의 resolve 메소드는 해당 모듈 이름의 절대 URL을 획득하고, require 함수의 async 메소드는 의존성을 비동기적으로 로드하고 콜백을 실행합니다. 팩토리 메소드의 반환 값의 경우 팩토리 메소드가 객체인 경우 이는 내보내기 값이고, 팩토리 메소드에 반환 값이 있는 경우 이는 내보내기 값이거나 module.exports의 값입니다. 수출 가치. 내보내기 값을 얻을 수 있으면 상태를 실행으로 설정합니다.
주의할 점: 내보내기에 값을 할당하여 개체를 내보내려고 할 때
define(function(require,exports,module){ exports ={ add: function(a,b){ return a+b; } } })
은 위의 실행을 통해 최종 내보내기 내보내기의 값을 판단하지 못합니다. 첫째, 함수에는 반환 값이 없습니다. 둘째, mod.exports가 정의되지 않았고 최종 내보내기가 정의되지 않았습니다. 왜 이런 일이 발생합니까? 이는 js의 참조 할당으로 인해 발생합니다. js의 할당 전략은 "공유를 통한 전달"입니다. 처음에는 내보내기 === module.exports이지만 객체를 내보내기에 할당하면 내보내기가 객체를 가리키지만 module.exports는 아직 초기화되지 않았으며 정의되지 않았습니다. 뭔가 잘못될 것이다.
를 올바르게 쓰는 방법은
define(function(require,exports,module){ module.exports ={ add: function(a,b){ return a+b; } } })
요약
입니다. 많은 코딩 기술과 콜백 모드에 대한 독창성과 세부 사항에 대한 고려를 배웠습니다. 코드의 모든 부분은 메모리 누수의 위험과 이 포인터 참조 오프셋을 고려하고 적극적인 예방 조치를 취합니다.
seajs 모듈 간의 종속성 로딩 및 모듈 실행과 관련된 더 많은 기사를 보려면 PHP 중국어 웹사이트를 주목하세요!