侧边栏

requirejs源码分析

发布于 | 分类于 源码分析

RequireJS 是一个实现了 AMD (Asynchronous Module Definition) 规范的 JavaScript 模块加载器,由于目前参与的项目使用了类似于requirejs的运行时微前端架构,需要了解一下整体的原理。

本文将从原理、架构和代码实现三个层面深入剖析 RequireJS 2.3.7 版本的源码。

核心原理

AMD 规范

RequireJS 实现了 AMD 规范,其核心思想是:

  • 异步加载:模块以异步方式加载,不阻塞页面渲染
  • 依赖前置:在模块定义时声明依赖,加载器负责解析和加载
  • 模块化封装:每个模块有独立的作用域,通过 define 定义,通过 require 使用

模块加载流程

mermaid
sequenceDiagram
    participant User as 用户代码
    participant Req as require()
    participant Ctx as Context
    participant Mod as Module
    participant Loader as Script Loader
    participant Script as 脚本文件
    
    User->>Req: require(['moduleA'], callback)
    Req->>Ctx: context.require(deps, callback)
    Ctx->>Mod: getModule(moduleMap)
    Mod->>Mod: init(deps, factory)
    Mod->>Mod: enable()
    
    loop 每个依赖
        Mod->>Ctx: enable(depMap)
        Ctx->>Mod: 创建依赖模块
        Mod->>Mod: fetch()
        Mod->>Loader: load(id, url)
        Loader->>Script: 创建 script 标签
        Script-->>Loader: onload 事件
        Loader->>Script: define(name, deps, factory)
        Script->>Ctx: defQueue.push([name, deps, factory])
    end
    
    Ctx->>Ctx: intakeDefines()
    Ctx->>Mod: check()
    
    alt 依赖全部加载
        Mod->>Mod: execCb(factory, depExports)
        Mod->>Ctx: defined[id] = exports
        Mod->>Mod: emit('defined', exports)
        Mod->>User: callback(exports)
    else 依赖未完成
        Mod->>Mod: 等待依赖
    end

循环依赖处理

RequireJS 通过 exports 提前暴露机制解决循环依赖:

  • 模块 A 依赖模块 B,模块 B 依赖模块 A
  • 当 A 开始加载时,创建 exports 对象
  • B 加载时可以获取到 A 的 exports 引用(虽然此时可能未完全初始化)
  • 通过 breakCycle 函数检测并打破循环

循环依赖处理流程

mermaid
graph TD
    A[模块 A 开始加载] --> B[创建 A 的 exports 对象]
    B --> C[A 依赖模块 B]
    C --> D[开始加载模块 B]
    D --> E[B 依赖模块 A]
    E --> F{A 是否在 traced 中?}
    F -->|是| G[检测到循环依赖]
    F -->|否| H[继续递归检查]
    G --> I[使用 A 的 exports]
    I --> J[B 完成定义]
    J --> K[A 获取 B 的 exports]
    K --> L[A 完成定义]
    
    style G fill:#f96
    style I fill:#9f6

架构设计

整体架构

RequireJS 采用闭包模式,整个库包裹在一个 IIFE 中:

javascript
(function (global, setTimeout) {
    var requirejs, require, define;
    // 核心实现
}(this, (typeof setTimeout === 'undefined' ? undefined : setTimeout)));

架构层次图

mermaid
graph TB
    subgraph "全局 API"
        A[requirejs/require]
        B[define]
        C[requirejs.config]
    end
    
    subgraph "Context 层"
        D[Context Manager]
        E[Default Context]
        F[Custom Context]
    end
    
    subgraph "模块管理层"
        G[Module Registry]
        H[Enabled Registry]
        I[Defined Modules]
        J[DefQueue]
    end
    
    subgraph "模块实例层"
        K[Module Instance]
        L[ModuleMap]
        M[Dependencies]
    end
    
    subgraph "加载层"
        N[Script Loader]
        O[Plugin System]
        P[Event System]
    end
    
    A --> D
    B --> D
    C --> D
    D --> E
    D --> F
    E --> G
    E --> H
    E --> I
    E --> J
    G --> K
    K --> L
    K --> M
    K --> N
    K --> O
    K --> P

核心组件

Context(上下文)

每个 Context 是一个独立的模块加载环境,包含:

  • config: 配置对象(baseUrl, paths, shim 等)
  • registry: 所有模块的注册表
  • defined: 已定义完成的模块
  • enabledRegistry: 已启用的模块
  • defQueue: 定义队列

多个 Context 可以共存,通过 contexts 对象管理:

javascript
contexts = {
    '_': defaultContext,  // 默认上下文
    'custom': customContext
}

Context 结构图

mermaid
classDiagram
    class Context {
        +Object config
        +Object registry
        +Object defined
        +Object enabledRegistry
        +Array defQueue
        +Object defQueueMap
        +configure(cfg)
        +require(deps, callback, errback)
        +makeRequire(relMap, options)
        +enable(depMap, mod)
        +completeLoad(moduleName)
        +nameToUrl(moduleName, ext)
        +load(id, url)
    }
    
    class Module {
        +Object map
        +Array depMaps
        +Array depExports
        +Function factory
        +Boolean enabled
        +Boolean defined
        +init(depMaps, factory, errback)
        +enable()
        +fetch()
        +check()
        +callPlugin()
    }
    
    class ModuleMap {
        +String prefix
        +String name
        +String id
        +String url
        +Boolean isDefine
        +Object parentMap
    }
    
    Context "1" --> "*" Module : manages
    Module "1" --> "1" ModuleMap : has
    Module "1" --> "*" Module : depends on

Module(模块)

Module 是模块的抽象表示,核心属性和方法:

javascript
Module.prototype = {
    init: function(depMaps, factory, errback, options) {},
    enable: function() {},
    fetch: function() {},
    check: function() {},
    callPlugin: function() {}
};

ModuleMap(模块映射)

存储模块的元信息,包括插件前缀、模块名、URL 等。

配置系统

javascript
config = {
    baseUrl: './',           // 基础路径
    paths: {},               // 路径映射
    bundles: {},             // 打包配置
    shim: {},                // 非 AMD 模块配置
    map: {},                 // 模块映射
    config: {},              // 模块特定配置
    waitSeconds: 7,          // 超时时间
    packages: []             // 包配置
}

配置处理流程

mermaid
flowchart TD
    A[requirejs.config] --> B{配置类型}
    B -->|baseUrl| C[规范化 baseUrl<br/>确保以 / 结尾]
    B -->|paths| D[路径映射配置<br/>支持数组 fallback]
    B -->|shim| E[非 AMD 模块配置<br/>deps + exports]
    B -->|map| F[模块映射配置<br/>不同模块使用不同版本]
    B -->|bundles| G[打包配置<br/>多模块合并]
    B -->|packages| H[包配置<br/>main 入口]
    
    C --> I[合并到 context.config]
    D --> I
    E --> I
    F --> I
    G --> I
    H --> I
    
    I --> J[更新已注册模块的 map]
    J --> K[配置生效]

核心代码实现

模块名称规范化

normalize 函数负责将相对路径转换为绝对路径:

javascript
function normalize(name, baseName, applyMap) {
    // 1. 处理相对路径 (./ 和 ../)
    if (name && name[0] === '.') {
        // 基于 baseName 解析相对路径
        normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
        name = normalizedBaseParts.concat(name);
    }
    
    // 2. 去除路径中的 . 和 ..
    trimDots(name);
    
    // 3. 应用 map 配置
    if (applyMap && map) {
        // 查找最长匹配的映射规则
    }
    
    // 4. 处理 packages 配置
    pkgMain = getOwn(config.pkgs, name);
    return pkgMain ? pkgMain : name;
}

模块加载机制

脚本加载(浏览器环境)

javascript
req.load = function (context, moduleName, url) {
    // 创建 script 标签
    node = req.createNode(config, moduleName, url);
    
    // 设置属性
    node.setAttribute('data-requirecontext', context.contextName);
    node.setAttribute('data-requiremodule', moduleName);
    
    // 绑定事件监听器
    if (node.attachEvent && !isOpera) {
        // IE 6-8 使用 attachEvent
        useInteractive = true;
        node.attachEvent('onreadystatechange', context.onScriptLoad);
    } else {
        // 现代浏览器使用 addEventListener
        node.addEventListener('load', context.onScriptLoad, false);
        node.addEventListener('error', context.onScriptError, false);
    }
    
    node.src = url;
    head.appendChild(node);
};

Web Worker 环境

javascript
if (isWebWorker) {
    setTimeout(function() {}, 0);  // 避免 WebKit GC 问题
    importScripts(url);
    context.completeLoad(moduleName);
}

define 函数实现

define 参数处理流程

mermaid
flowchart TD
    A[define 调用] --> B{参数类型检查}
    B -->|name 非字符串| C[匿名模块<br/>name = null]
    B -->|name 是字符串| D[具名模块]
    
    C --> E{deps 是否为数组?}
    D --> E
    
    E -->|否| F[deps = null<br/>callback = deps]
    E -->|是| G[保持 deps]
    
    F --> H{callback 是函数?}
    G --> I[准备就绪]
    
    H -->|是| J[扫描函数体<br/>提取 require 调用]
    H -->|否| I
    
    J --> K[添加 require/exports/module<br/>到 deps]
    K --> I
    
    I --> L{useInteractive?}
    L -->|是 IE 6-8| M[获取交互式脚本<br/>提取 name 和 context]
    L -->|否| N[使用当前 context]
    
    M --> O[推入 defQueue]
    N --> O
    O --> P[等待 onScriptLoad<br/>触发 intakeDefines]
javascript
define = function (name, deps, callback) {
    // 1. 参数归一化
    if (typeof name !== 'string') {
        callback = deps;
        deps = name;
        name = null;  // 匿名模块
    }
    
    if (!isArray(deps)) {
        callback = deps;
        deps = null;
    }
    
    // 2. CommonJS 风格依赖检测
    if (!deps && isFunction(callback)) {
        deps = [];
        callback.toString()
            .replace(commentRegExp, '')
            .replace(cjsRequireRegExp, function (match, dep) {
                deps.push(dep);
            });
        
        // 添加 require, exports, module
        deps = ['require', 'exports', 'module'].concat(deps);
    }
    
    // 3. IE 6-8 交互式脚本处理
    if (useInteractive) {
        node = currentlyAddingScript || getInteractiveScript();
        if (node) {
            if (!name) {
                name = node.getAttribute('data-requiremodule');
            }
            context = contexts[node.getAttribute('data-requirecontext')];
        }
    }
    
    // 4. 将定义推入队列
    if (context) {
        context.defQueue.push([name, deps, callback]);
        context.defQueueMap[name] = true;
    } else {
        globalDefQueue.push([name, deps, callback]);
    }
};

define.amd = { jQuery: true };  // AMD 标识

require 函数实现

javascript
req = requirejs = function (deps, callback, errback, optional) {
    var context, config, contextName = defContextName;
    
    // 1. 参数解析(支持配置对象)
    if (!isArray(deps) && typeof deps !== 'string') {
        config = deps;
        if (isArray(callback)) {
            deps = callback;
            callback = errback;
            errback = optional;
        } else {
            deps = [];
        }
    }
    
    // 2. 获取或创建 context
    if (config && config.context) {
        contextName = config.context;
    }
    context = getOwn(contexts, contextName);
    if (!context) {
        context = contexts[contextName] = req.s.newContext(contextName);
    }
    
    // 3. 应用配置
    if (config) {
        context.configure(config);
    }
    
    // 4. 执行 require
    return context.require(deps, callback, errback);
};

模块执行流程

Module.check() - 核心执行逻辑

mermaid
stateDiagram-v2
    [*] --> 检查状态
    
    检查状态 --> 未初始化: !inited
    检查状态 --> 有错误: error
    检查状态 --> 执行中: defining
    检查状态 --> 准备执行: else
    
    未初始化 --> 触发fetch: !defQueueMap[id]
    未初始化 --> 等待: defQueueMap[id]
    触发fetch --> [*]
    
    有错误 --> 触发error事件
    触发error事件 --> [*]
    
    执行中 --> [*]: 避免重入
    
    准备执行 --> 检查依赖: depCount < 1
    准备执行 --> 等待依赖: depCount >= 1
    等待依赖 --> [*]
    
    检查依赖 --> 执行工厂函数: isFunction(factory)
    检查依赖 --> 使用字面量: else
    
    执行工厂函数 --> 处理返回值
    使用字面量 --> 保存exports
    
    处理返回值 --> 检查返回值: exports === undefined
    检查返回值 --> 使用module.exports: cjsModule
    检查返回值 --> 使用this.exports: usingExports
    检查返回值 --> 保存exports: else
    
    使用module.exports --> 保存exports
    使用this.exports --> 保存exports
    
    保存exports --> 标记已定义
    标记已定义 --> 触发defined事件
    触发defined事件 --> [*]
javascript
check: function () {
    if (!this.enabled || this.enabling) return;
    
    var id = this.map.id,
        depExports = this.depExports,
        exports = this.exports,
        factory = this.factory;
    
    if (!this.inited) {
        // 未初始化,触发 fetch
        if (!hasProp(context.defQueueMap, id)) {
            this.fetch();
        }
    } else if (this.error) {
        // 有错误,触发 error 事件
        this.emit('error', this.error);
    } else if (!this.defining) {
        this.defining = true;
        
        // 依赖全部加载完成
        if (this.depCount < 1 && !this.defined) {
            if (isFunction(factory)) {
                // 执行工厂函数
                try {
                    exports = context.execCb(id, factory, depExports, exports);
                } catch (e) {
                    err = e;
                }
                
                // 处理返回值
                if (this.map.isDefine && exports === undefined) {
                    cjsModule = this.module;
                    if (cjsModule) {
                        exports = cjsModule.exports;
                    } else if (this.usingExports) {
                        exports = this.exports;
                    }
                }
            } else {
                // 字面量值
                exports = factory;
            }
            
            this.exports = exports;
            
            // 标记为已定义
            if (this.map.isDefine && !this.ignore) {
                defined[id] = exports;
            }
            
            cleanRegistry(id);
            this.defined = true;
        }
        
        this.defining = false;
        
        if (this.defined && !this.defineEmitted) {
            this.defineEmitted = true;
            this.emit('defined', this.exports);
        }
    }
}

依赖管理

Module.enable() - 启用模块及其依赖

mermaid
flowchart TD
    A[enable 调用] --> B[标记 enabled = true]
    B --> C[标记 enabling = true]
    C --> D[遍历 depMaps]
    
    D --> E{depMap 类型?}
    E -->|字符串| F[转换为 ModuleMap]
    E -->|对象| G[已是 ModuleMap]
    
    F --> H{是特殊依赖?}
    G --> H
    
    H -->|require/exports/module| I[使用 handlers 处理]
    H -->|普通依赖| J[depCount += 1]
    
    I --> K[直接返回特殊对象]
    K --> D
    
    J --> L[监听 defined 事件]
    L --> M[监听 error 事件]
    M --> N{依赖已注册?}
    
    N -->|是| O{依赖已启用?}
    N -->|否| D
    
    O -->|否| P[递归 enable 依赖]
    O -->|是| D
    
    P --> D
    
    D --> Q[遍历完成]
    Q --> R[启用插件依赖]
    R --> S[enabling = false]
    S --> T[调用 check]
    T --> U[结束]
javascript
enable: function () {
    enabledRegistry[this.map.id] = this;
    this.enabled = true;
    this.enabling = true;
    
    // 启用每个依赖
    each(this.depMaps, bind(this, function (depMap, i) {
        var id, mod, handler;
        
        if (typeof depMap === 'string') {
            // 转换为 depMap
            depMap = makeModuleMap(depMap, this.map.parentMap, false, !this.skipMap);
            this.depMaps[i] = depMap;
            
            // 处理特殊依赖(require, exports, module)
            handler = getOwn(handlers, depMap.id);
            if (handler) {
                this.depExports[i] = handler(this);
                return;
            }
            
            this.depCount += 1;
            
            // 监听依赖的 defined 事件
            on(depMap, 'defined', bind(this, function (depExports) {
                if (this.undefed) return;
                this.defineDep(i, depExports);
                this.check();
            }));
            
            // 错误处理
            if (this.errback) {
                on(depMap, 'error', bind(this, this.errback));
            }
        }
        
        id = depMap.id;
        mod = registry[id];
        
        // 递归启用依赖
        if (!hasProp(handlers, id) && mod && !mod.enabled) {
            context.enable(depMap, this);
        }
    }));
    
    this.enabling = false;
    this.check();
}

特殊依赖处理

javascript
handlers = {
    'require': function (mod) {
        return mod.require || (mod.require = context.makeRequire(mod.map));
    },
    'exports': function (mod) {
        mod.usingExports = true;
        if (mod.map.isDefine) {
            return mod.exports || (mod.exports = defined[mod.map.id] = {});
        }
    },
    'module': function (mod) {
        return mod.module || (mod.module = {
            id: mod.map.id,
            uri: mod.map.url,
            config: function () {
                return getOwn(config.config, mod.map.id) || {};
            },
            exports: mod.exports || (mod.exports = {})
        });
    }
};

循环依赖检测

javascript
function breakCycle(mod, traced, processed) {
    var id = mod.map.id;
    
    if (mod.error) {
        mod.emit('error', mod.error);
    } else {
        traced[id] = true;
        
        each(mod.depMaps, function (depMap, i) {
            var depId = depMap.id,
                dep = getOwn(registry, depId);
            
            if (dep && !mod.depMatched[i] && !processed[depId]) {
                if (getOwn(traced, depId)) {
                    // 检测到循环依赖
                    mod.defineDep(i, defined[depId]);
                    mod.check();
                } else {
                    // 递归检查
                    breakCycle(dep, traced, processed);
                }
            }
        });
        
        processed[id] = true;
    }
}

超时检测

javascript
function checkLoaded() {
    var waitInterval = config.waitSeconds * 1000,
        expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(),
        noLoads = [],
        stillLoading = false;
    
    if (inCheckLoaded) return;
    inCheckLoaded = true;
    
    // 检查所有启用的模块
    eachProp(enabledRegistry, function (mod) {
        if (!mod.enabled) return;
        
        if (!mod.error) {
            if (!mod.inited && expired) {
                // 超时未加载
                if (hasPathFallback(modId)) {
                    stillLoading = true;
                } else {
                    noLoads.push(modId);
                    removeScript(modId);
                }
            } else if (!mod.inited && mod.fetched && map.isDefine) {
                stillLoading = true;
            }
        }
    });
    
    if (expired && noLoads.length) {
        // 抛出超时错误
        err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads);
        return onError(err);
    }
    
    // 检查循环依赖
    if (needCycleCheck) {
        each(reqCalls, function (mod) {
            breakCycle(mod, {}, {});
        });
    }
    
    // 继续等待
    if ((!expired || usingPathFallback) && stillLoading) {
        if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) {
            checkLoadedTimeoutId = setTimeout(function () {
                checkLoadedTimeoutId = 0;
                checkLoaded();
            }, 50);
        }
    }
    
    inCheckLoaded = false;
}

插件系统

RequireJS 支持插件扩展,插件通过 ! 分隔符标识:

javascript
// 使用示例
require(['text!template.html'], function(html) {
    // html 是模板内容
});

插件加载流程图

mermaid
sequenceDiagram
    participant Mod as Module
    participant Plugin as Plugin Module
    participant Loader as Plugin Loader
    participant Resource as Resource
    
    Mod->>Mod: callPlugin()
    Mod->>Plugin: 加载插件模块<br/>(text, i18n 等)
    
    activate Plugin
    Plugin-->>Mod: plugin.normalize(name)
    Mod->>Mod: 创建规范化的 ModuleMap
    
    Mod->>Plugin: plugin.load(name, require, load, config)
    
    Plugin->>Resource: 获取资源<br/>(AJAX, 文件读取等)
    Resource-->>Plugin: 资源内容
    
    alt 动态生成模块
        Plugin->>Loader: load.fromText(code)
        Loader->>Loader: req.exec(code)
        Loader->>Mod: completeLoad()
    else 直接返回
        Plugin->>Loader: load(value)
        Loader->>Mod: init([], function() { return value })
    end
    
    deactivate Plugin
    
    Mod->>Mod: emit('defined', value)

插件加载流程

javascript
callPlugin: function () {
    var map = this.map,
        id = map.id,
        pluginMap = makeModuleMap(map.prefix);
    
    // 监听插件加载完成
    on(pluginMap, 'defined', bind(this, function (plugin) {
        var load, normalizedMap, normalizedMod,
            name = this.map.name,
            parentName = this.map.parentMap ? this.map.parentMap.name : null;
        
        // 如果插件支持 normalize,规范化资源名称
        if (plugin.normalize) {
            name = plugin.normalize(name, function (name) {
                return normalize(name, parentName, true);
            }) || '';
        }
        
        // 创建规范化的模块映射
        normalizedMap = makeModuleMap(map.prefix + '!' + name, 
                                      this.map.parentMap, true);
        
        // load 回调
        load = bind(this, function (value) {
            this.init([], function () { return value; }, null, {
                enabled: true
            });
        });
        
        load.error = bind(this, function (err) {
            this.inited = true;
            this.error = err;
            onError(err);
        });
        
        // load.fromText 支持动态生成模块
        load.fromText = bind(this, function (text, textAlt) {
            var moduleName = map.name,
                moduleMap = makeModuleMap(moduleName);
            
            getModule(moduleMap);
            
            try {
                req.exec(text);  // 执行动态代码
            } catch (e) {
                return onError(makeError('fromtexteval', 
                    'fromText eval for ' + id + ' failed: ' + e, e, [id]));
            }
            
            context.completeLoad(moduleName);
            localRequire([moduleName], load);
        });
        
        // 调用插件的 load 方法
        plugin.load(map.name, localRequire, load, config);
    }));
    
    context.enable(pluginMap, this);
    this.pluginMaps[pluginMap.id] = pluginMap;
}

Shim 配置

Shim 用于加载非 AMD 模块(如传统的全局变量库):

javascript
requirejs.config({
    shim: {
        'backbone': {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        }
    }
});

Shim 处理逻辑

javascript
makeShimExports: function (value) {
    function fn() {
        var ret;
        if (value.init) {
            ret = value.init.apply(global, arguments);
        }
        return ret || (value.exports && getGlobal(value.exports));
    }
    return fn;
}

// 在 completeLoad 中处理 shim
if (shim) {
    context.makeRequire(this.map, {
        enableBuildCallback: true
    })(shim.deps || [], bind(this, function () {
        return map.prefix ? this.callPlugin() : this.load();
    }));
}

路径解析

nameToUrl - 模块名转 URL

mermaid
flowchart TD
    A[nameToUrl 调用] --> B{是 package?}
    B -->|是| C[使用 package main]
    B -->|否| D{是 bundle?}
    
    C --> D
    D -->|是| E[使用 bundle ID]
    D -->|否| F{是 URL 格式?}
    
    E --> A
    F -->|是<br/>含 :/ 或 .js| G[直接使用 + ext]
    F -->|否| H[应用 paths 配置]
    
    H --> I[分割模块名为数组]
    I --> J[从长到短匹配 paths]
    J --> K{找到匹配?}
    
    K -->|是| L[替换路径前缀]
    K -->|否| M[保持原样]
    
    L --> N[拼接路径]
    M --> N
    
    N --> O{需要 .js 后缀?}
    O -->|是| P[添加 .js]
    O -->|否| Q[保持原样]
    
    P --> R{需要 baseUrl?}
    Q --> R
    
    R -->|是| S[添加 baseUrl 前缀]
    R -->|否| T[保持原样]
    
    S --> U{有 urlArgs?}
    T --> U
    
    U -->|是| V[添加 urlArgs 参数]
    U -->|否| W[返回 URL]
    
    V --> W
javascript
nameToUrl: function (moduleName, ext, skipExt) {
    var paths, syms, i, parentModule, url, pkgMain;
    
    // 1. 处理 packages
    pkgMain = getOwn(config.pkgs, moduleName);
    if (pkgMain) {
        moduleName = pkgMain;
    }
    
    // 2. 处理 bundles
    bundleId = getOwn(bundlesMap, moduleName);
    if (bundleId) {
        return context.nameToUrl(bundleId, ext, skipExt);
    }
    
    // 3. 如果是 URL 格式,直接返回
    if (req.jsExtRegExp.test(moduleName)) {
        url = moduleName + (ext || '');
    } else {
        // 4. 应用 paths 配置
        paths = config.paths;
        syms = moduleName.split('/');
        
        for (i = syms.length; i > 0; i -= 1) {
            parentModule = syms.slice(0, i).join('/');
            parentPath = getOwn(paths, parentModule);
            
            if (parentPath) {
                if (isArray(parentPath)) {
                    parentPath = parentPath[0];  // 支持 fallback
                }
                syms.splice(0, i, parentPath);
                break;
            }
        }
        
        // 5. 拼接 URL
        url = syms.join('/');
        url += (ext || (/^data\:|^blob\:|\?/.test(url) || skipExt ? '' : '.js'));
        url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;
    }
    
    // 6. 添加 urlArgs
    return config.urlArgs && !/^blob\:/.test(url) ?
           url + config.urlArgs(moduleName, url) : url;
}

使用示例

基本使用

javascript
// 定义模块
define('moduleA', ['jquery'], function($) {
    return {
        init: function() {
            $('body').append('<div>Module A</div>');
        }
    };
});

// 使用模块
require(['moduleA'], function(moduleA) {
    moduleA.init();
});

配置示例

javascript
requirejs.config({
    baseUrl: '/js/lib',
    paths: {
        'jquery': 'jquery-3.6.0.min',
        'backbone': 'backbone-1.4.0',
        'underscore': 'underscore-1.13.1'
    },
    shim: {
        'backbone': {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        }
    },
    map: {
        '*': {
            'jquery': 'jquery-private'
        },
        'jquery-private': {
            'jquery': 'jquery'
        }
    },
    waitSeconds: 15
});

插件使用

javascript
// 加载文本文件
require(['text!template.html'], function(template) {
    document.body.innerHTML = template;
});

// 国际化
require(['i18n!nls/messages'], function(messages) {
    console.log(messages.greeting);
});

// DOM 就绪
require(['domReady!'], function(doc) {
    console.log('DOM ready');
});

CommonJS 风格

javascript
define(function(require, exports, module) {
    var $ = require('jquery');
    var _ = require('underscore');
    
    exports.helper = function() {
        return _.map([1, 2, 3], function(n) {
            return n * 2;
        });
    };
    
    module.exports = {
        helper: exports.helper
    };
});

总结

尽管 RequireJS 在现代前端开发中使用减少,但其源码仍有很高的学习价值:

  1. 模块化思想:理解模块化的本质和演进
  2. 异步编程:学习异步加载和依赖管理
  3. 兼容性处理:了解如何处理浏览器差异
  4. 架构设计:学习大型库的架构设计思路
  5. 代码质量:精简、高效的代码实现

参考:

你要请我喝一杯奶茶?

版权声明:自由转载-非商用-保持署名和原文链接。

本站文章均为本人原创,参考文章我都会在文中进行声明,也请您转载时附上署名。