理解模块加载器 — sea.js

create on in module_loader with 0 comment and 321 view

通过与 require.js 作对比,学习 sea.js 的实现。

参考自sea.js 2.2.0 module 核心代码

入口

// require.js require(['dep1', 'dep2'], function(dep1, dep2){}) // sea.js seajs.use(['dep1', 'dep2'], function(dep1, dep2){})

在入口处引入 dep1, dep2 两个依赖模块,引用方式大致相同。sea.js 为了与 require.js 区分开来,自己设计入口为 seajs.use。

require.js 的 require 还分为全局 require 和 参数 require,对于开发者来说显然是不友好的。

模块定义

// require.js define(['a.js', 'b.js'], function(a, b){}) // sea.js define(function(require, exports, module){ var a = require('a.js') var b = require('b.js') })

require.js 遵循 AMD 规范,sea.js 遵循 CMD(通用模块定义) 规范,其规范结构如下。

define(id, deps, factory) // AMD define(factory) // CMD

其实,sea.js define 是支持接受 (id, deps, factory)这样的参数的,但带 id 和 deps 参数的 define 用法不属于 CMD 规范

更多 API 请参考:
sea.js API
require.js API

模块加载

sea.js 在 define 一个模块时,首先会分析依赖模块,require.js也会分析依赖模块,而两者分析依赖的方式是不同的。

// sea.js -> module.js -> define源码 // Define a module Module.define = function (id, deps, factory) { ... // Parse dependencies according to the module factory code if (!isArray(deps) && isFunction(factory)) { deps = parseDependencies(factory.toString()) } var meta = { id: id, uri: Module.resolve(id), deps: deps, factory: factory } ... // Emit `define` event, used in nocache plugin, seajs node version etc emit("define", meta) ... }

sea.js 执行 define 后,首先,会分析参数,若 deps 为非数组(即 CMD 模块定义规范),那么将 factory 函数转为字符串,借助正则将该字符串中的 require 调用的依赖模块 uri 提取出来,然后预先加载,并存于 cache 中,直到当前模块的回调(factory)内执行 require 时,才执行对应模块的回调(factory)。

// util-deps.js var REQUIRE_RE = /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^\/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*require|(?:^|[^$])\brequire\s*\(\s*(["'])(.+?)\1\s*\)/g var SLASH_RE = /\\\\/g function parseDependencies(code) { var ret = [] code.replace(SLASH_RE, "") .replace(REQUIRE_RE, function(m, m1, m2) { if (m2) { ret.push(m2) } }) return ret }

这里同时也说明了,sea.js 为什么不能使用 require('jq' + 'uery') 这样的计算值来加载模块。这就是因为在分析依赖时处在类似于静态编译分析的阶段。

通过此操作,sea.js 避免了前置定义依赖,实现了在哪用则在哪 require。而 require.js 需要前置定义依赖,且会预先执行 (require.js 的模块若无依赖或其依赖已全部加载,那么回调会立即执行)。可以说,sea.js 是提前加载 + 懒执行,require.js是提前加载 + 提前执行

总结

文中主要描述了我认为的 requrie.js 和 sea.js 最大的不同之处,总的来说,有以下:

  1. 规范不同,导致的 api 不同
    入口:require(), seajs.use()
    模块定义规范:AMD, CMD
  2. 模块加载的方式不同
    require.js 预加载,预执行
    sea.js 预加载,懒执行。

require.js 和 sea.js 其实也存在很多相似之处,从源码的角度来看,对于某些问题的处理方式基本是一致的,例如 路径处理模块命名模块下载CommonJS Module等。

更多不同可参考该 issue (与 RequireJS 的异同)

😁😂😃😄😅😆😇😈😉😐😑😒😓😔😕😖😗😘😙😠😡😢😣😤😥😦😧😨😩😰😱😲😳😴😵😶😷😸😹🙀🙁🙂🙃🙄🙅🙆🙇🙈
🙂