在前端模块化开发实践中,SeaJS 的模块依赖管理机制一直是开发者容易遇到难题的地方。最近我们在项目中就发现了一个典型的依赖加载问题,正好借此机会深入分析一下。

假设我们有三个模块文件:
/*init.js*/
define(function(require, exports, module){
require('jquery');
require('jquery.plugA');
})
/*jquery.plugA.js*/
define(function(require, exports, module){
require('jquery');
require('jquery.plugB');
//code...
})
/*jquery.plugB.js*/
define(function(require, exports, module){
require('jquery');
//code...
})
当执行 init.js 时,init.js、jquery.plugA.js、jquery.plugB.js 都会依赖 jquery。那么 SeaJS 对 jquery 的加载处理究竟是怎么实现的?是只执行一次,还是多次执行,亦或存在其他机制?
这里直接引用玉伯(SeaJS 的作者)给出的解释:
模块调用的本质是获取某个模块的接口。在 SeaJS 中,只有 seajs.use、require.async 和 require 才会触发模块调用。例如 var a = require('./a'),在执行 require('./a') 时,会获取模块的接口。如果是第一次调用,系统会初始化模块 a;以后再次调用时,直接返回模块 a 的已缓存接口。而 define 仅用于注册模块信息,比如打包后的代码:define(id, deps, factory) 就是将模块注册到 seajs.cache 中,类似于 seajs.cache[id] = { id: id, dependencies: deps, factory: factory } —— 这仅仅是注册而已。
真正执行模块是在 require('./a') 时才会进行,此时会调用 seajs.cache['a'].factory,执行后得到 seajs.cache['a'].exports。因此,jquery 只会被初始化一次,后续所有的 require('jquery') 都会直接返回已缓存的接口,不会再重复执行工厂函数。
扩展:URI与URL的区别
URI(Uniform Resource Identifiers):统一资源标识符;
URL(Uniform Resource Locators):统一资源定位符;
URN(Uniform Resource Names):统一资源名称。
URL 和 URN 都是 URI 的子集。
参考
- Module.STATUS 的具体含义:https://github.com/seajs/seajs/issues/303
- 分清 URI、URL 和 URN:https://www.ibm.com/developerworks/cn/xml/x-urlni.html
