seajs3.0.0源码分析记录

自己边读变加了一些注释,理解了一下seajs3.0.0工作的流程。正则没有一个个去理解,插件模块也没看, 以后有时间了可以补充完整~

事件系统中事件队列的获取&定义方法

var list = events[name] || (events[name] = [])

以前自己写都是

if(!events[name]){
    events[name]=[];
}
var list=events[name];

加载模块文件的方法

webworker环境下加载模块文件

获取seajs的加载路径:

var stack;
  try {
    var up = new Error();
    throw up;
  } catch (e) {
    // IE won‘t set Error.stack until thrown
    stack = e.stack.split(‘\n‘);
  }
//  at Error (native) <- Here‘s your problem
//  at http://localhost:8000/_site/dist/sea.js:2:4334 <- What we want
//  at http://localhost:8000/_site/dist/sea.js:2:8386
//  at http://localhost:8000/_site/tests/specs/web-worker/worker.js:3:1

根据url加载模块,使用的是webworker全局环境下的importScripts方法

function requestFromWebWorker(url, callback, charset) {
    // Load with importScripts
    var error;
    try {
      importScripts(url);
    } catch (e) {
      error = e;
    }
    callback(error);
  }
  // For Developers
  seajs.request = requestFromWebWorker;

浏览器js线程中异步加载模块文件

  // 通过script标签加载脚本
  function request(url, callback, charset) {
    var node = doc.createElement("script")

    if (charset) {
      var cs = isFunction(charset) ? charset(url) : charset
      if (cs) {
        node.charset = cs
      }
    }

    addOnload(node, callback, url)

    node.async = true
    node.src = url

    // For some cache cases in IE 6-8, the script executes IMMEDIATELY after
    // the end of the insert execution, so use `currentlyAddingScript` to
    // hold current node, for deriving url in `define` call
    currentlyAddingScript = node

    // ref: #185 & http://dev.jquery.com/ticket/2709
    baseElement ?
        head.insertBefore(node, baseElement) :
        head.appendChild(node)

    currentlyAddingScript = null
  }
  //添加加载完成之后的处理,包括报错、清除对象、移除dom等操作
  function addOnload(node, callback, url) {
    var supportOnload = "onload" in node

    if (supportOnload) {
      node.onload = onload
      node.onerror = function() {
        emit("error", { uri: url, node: node })
        onload(true)
      }
    }
    else {
      node.onreadystatechange = function() {
        if (/loaded|complete/.test(node.readyState)) {
          onload()
        }
      }
    }

    function onload(error) {
      // Ensure only run once and handle memory leak in IE
      node.onload = node.onerror = node.onreadystatechange = null

      // Remove the script to reduce memory leak
      // 添加了又去除掉
      if (!data.debug) {
        head.removeChild(node)
      }

      // Dereference the node
      node = null

      callback(error)
    }
  }

  // For Developers
  seajs.request = request

核心模块类 Module

Module类定义

//定义module类
function Module(uri, deps) {
  //模块路径
  this.uri = uri
  //这是一个字符串数组,内容是该模块依赖模块的id列表
  this.dependencies = deps || []
  //这是该模块所依赖的模块的一个map key为id,value为Module对象
  this.deps = {} // Ref the dependence modules
  //模块状态
  this.status = 0
  //用来辅助load的一个数组,load完成后会删掉
  this._entry = []
}

模块缓存

var cachedMods = seajs.cache = {}

原型方法列表

// 获取模块中依赖模块的uri数组
Module.prototype.resolve 
// 一个辅助模块load过程的函数
Module.prototype.pass
//加载并执行模块
Module.prototype.load
//模块加载完成后触发
Module.prototype.onload
//模块404时候触发
Module.prototype.error
//执行模块
Module.prototype.exec
//用来获取模块文件,每调用一次给一个缓存对象设置一个key value,value是一个函数,调用即开始加载文件

函数方法列表

//通过id获取文件uri
Module.resolve = function(id, refUri)
//用来定义一个模块
Module.define= function (id, deps, factory)
//将id、依赖、 factory 、模块加载状态设置给缓存中的模块
Module.save= function(uri, meta)
////根据uri,从缓存中获取模块,或者以接收的uri和deps参数创建一个模块返回,并加入缓存
Module.get=function(uri, deps)
//用来加载并运行模块
Module.use = function(ids, callback, uri)

暴露出来的API

 1 // Public API
 2
 3 seajs.use = function(ids, callback) {
 4   //use 实际上创建了一个id为data.cwd + "_use_" + cid()的模块,这个模块的依赖模块是待启动的模块
 5   Module.use(ids, callback, data.cwd + "_use_" + cid())
 6   return seajs
 7 }
 8
 9 Module.define.cmd = {}
10 global.define = Module.define
11
12
13 // For Developers
14
15 seajs.Module = Module
16 data.fetchedList = fetchedList
17 data.cid = cid
18
19 seajs.require = function(id) {
20   var mod = Module.get(Module.resolve(id))
21   if (mod.status < STATUS.EXECUTING) {
22     mod.onload()
23     mod.exec()
24   }
25   return mod.exports
26 }
27
28 /**
29  * config.js - The configuration for the loader
30  */
31
32 // The root path to use for id2uri parsing
33 data.base = loaderDir
34
35 // The loader directory
36 data.dir = loaderDir
37
38 // The loader‘s full path
39 data.loader = loaderPath
40
41 // The current working directory
42 data.cwd = cwd
43
44 // The charset for requesting files
45 data.charset = "utf-8"
46
47 // data.alias - An object containing shorthands of module id
48 // data.paths - An object containing path shorthands in module id
49 // data.vars - The {xxx} variables in module id
50 // data.map - An array containing rules to map module uri
51 // data.debug - Debug mode. The default value is false
52
53 seajs.config = function(configData) {
54
55   for (var key in configData) {
56     var curr = configData[key]
57     var prev = data[key]
58
59     // Merge object config such as alias, vars
60     if (prev && isObject(prev)) {
61       for (var k in curr) {
62         prev[k] = curr[k]
63       }
64     }
65     else {
66       // Concat array config such as map
67       if (isArray(prev)) {
68         curr = prev.concat(curr)
69       }
70       // Make sure that `data.base` is an absolute path
71       else if (key === "base") {
72         // Make sure end with "/"
73         if (curr.slice(-1) !== "/") {
74           curr += "/"
75         }
76         curr = addBase(curr)
77       }
78
79       // Set config
80       data[key] = curr
81     }
82   }
83
84   emit("config", configData)
85   return seajs
86 }

常用API和特性的理解

seajs.use原理

seajs.use ("index.js")=Module.use(["index.js"],undefined,"http://xxxx.com/xxx/xxx/_use_i")

① 通过Module.get创建模块mod,

② 通过Module.prototype.load加载mod模块,为mode定义history、remain、callback属性,设置_entry

Module.prototype.load中:

①通过Module.prototype.resolve函数获取模块的依赖模块uri数组。

Module.prototype.resolve会遍历mod对象的dependencies中的模块id,挨个通过Module.resolve函数获取其uri,最后返回数组

② 遍历上一步获取的uri,通过Module.get获取模块,给mod的deps属性设置值,key为依赖模块id,value为依赖模块对象

④ 通过Module.prototype.fetch 获取所有依赖模块的模块文件

⑤ 对子模块重复以上过程

⑥mod模块加载完成后,执行onload回调,在onload中执行Module.use的回调callback,在callback中执行Module.prototype.exec来运行该模块

require、exports、module的注入&懒执行

Module.use的回调callback中会运行模块的Module.prototype.exec ,返回module的exports对象。

这个过程中,若factory是function,则调用factory,参数传入require,mod.exports,mod。

require:

  function require(id) {
    var m = mod.deps[id] || Module.get(require.resolve(id))
    if (m.status == STATUS.ERROR) {
      throw new Error(‘module was broken: ‘ + m.uri);
    }
    return m.exec()
  }

注入的过程:

// Exec factory
  var factory = mod.factory
  //执行factory的代码,这个时候才执行,懒执行
  var exports = isFunction(factory) ?
    factory(require, mod.exports = {}, mod) :
    factory

可以看到,require的作用是从缓存中获取模块,然后exec。也就是说,只有require函数被执行的时候,模块代码才会被执行。这就是seajs的as lazy as posible ,懒执行。

global.define

define函数的作用是从参数中获取 id 、dependences 、  factory,然后从模块缓存中获取模块对象,或者创建新模块加入缓存,将id、dependences、factory设置给模块。

贴上关键代码:

Module.define = function (id, deps, factory) {
  var argsLen = arguments.length
  // define(factory)
  if (argsLen === 1) {
    factory = id
    id = undefined
  }
  else if (argsLen === 2) {
    factory = deps
    // define(deps, factory)
    if (isArray(id)) {
      deps = id
      id = undefined
    }
    // define(id, factory)
    else {
      deps = undefined
    }
  }
  // 从function字符串中获取依赖模块id
  if (!isArray(deps) && isFunction(factory)) {
    deps = typeof parseDependencies === "undefined" ? [] : parseDependencies(factory.toString())
  }
  var meta = {
    id: id,
    uri: Module.resolve(id),
    deps: deps,
    factory: factory
  }
  meta.uri ? Module.save(meta.uri, meta) :
    // Save information for "saving" work in the script onload event
    anonymousMeta = meta
}

(为了方便理解,源码中一些辅助代码被我剪切掉了)

最后附上我写过注释的源码

sea3.0.0-learn

时间: 2024-12-15 07:22:47

seajs3.0.0源码分析记录的相关文章

Spark2.1.0之源码分析——事件总线

阅读提示:阅读本文前,最好先阅读<Spark2.1.0之源码分析--事件总线>.<Spark2.1.0事件总线分析--ListenerBus的继承体系>及<Spark2.1.0事件总线分析--SparkListenerBus详解>几篇文章的内容. LiveListenerBus继承了SparkListenerBus,并实现了将事件异步投递给监听器,达到实时刷新UI界面数据的效果.LiveListenerBus主要由以下部分组成: eventQueue:是SparkLis

转载Aaron ---- jQuery 2.0.3 源码分析core - 选择器

jQuery 2.0.3 源码分析core - 选择器(02) 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 打开jQuery源码,一眼看去到处都充斥着正则表达式,jQuery框架的基础就是查询了,查询文档元素对象,所以狭隘的说呢,jQuery就是一个选择器,并这个基础上构建和运行查询过滤器! 工欲善其事,必先利其器,所以先从正则入手 我们来分解一个表达式 // A simple way to check for HTML strings // Prioritize

MyVoix2.0.js 源码分析 WebSpeech与WebAudio篇

楔 子 随着移动互联网时代的开启,各种移动设备走进了我们的生活.无论是日常生活中人手一部的手机,还是夜跑者必备的各种智能腕带,亦或者是充满未来科技感的google glass云云,它们正渐渐改变着我们的生活习惯以及用户交互习惯.触摸屏取代了实体按键,Siri开始慢慢释放我们的双手,而leap motion之类的硬件更是让我们彻底不需要接触IT设备便能通过手势控制它们.在这样的大背景下,前端的交互将涉及越来越多元的交叉学科,我们正如十几年前人们经历Css的诞生一样,见证着一场带动整个行业乃至社会的

转载Aaron博客 ---- jQuery 2.0.3 源码分析core - 整体架构

jQuery 2.0.3 源码分析core - 整体架构 整体架构 拜读一个开源框架,最想学到的就是设计的思想和实现的技巧. 废话不多说,jquery这么多年了分析都写烂了,老早以前就拜读过, 不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery又给扫一遍 我也不会照本宣科的翻译源码,结合自己的实际经验一起拜读吧! github上最新是jquery-master,加入了AMD规范了,我就以官方最新2.0.3为准 整体架构 jQuery框架的核心就是从HTML文档中匹配元素并

struts1.2源码分析记录

struts思路:1.struts使用的三板斧ActionForm.DispatchAction.ActionMapping2.主要关注点:Form.Action.Forward3.页面如何发送请求参数,后台如何截取.do请求,如何将参数组装成Form,在Action中如何操作Form,如何查找目标页面,如何将Form返回到页面,页面如何通过标签读取Form属性 框架主流程:Struts所有的请求都要走DispatchAction类的public ActionForward execute(Ac

jQuery 2.0.3 源码分析 事件绑定 - bind/live/delegate/on

转:http://www.cnblogs.com/aaronjs/p/3440647.html?winzoom=1 事件(Event)是JavaScript应用跳动的心脏,通过使用JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应 事件的基础就不重复讲解了,本来是定位源码分析实现的, 所以需要有一定的基础才行 为了下一步更好的理解内部的实现,所以首先得清楚的认识到事件接口的划分 网上资料遍地都是,但是作为一个jQuery系列的源码分析,我还是很有必要在重新

jQuery 2.0.3 源码分析 Deferrred概念

转载http://www.cnblogs.com/aaronjs/p/3348569.html JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而回调函数中则包含了后续的工作.这也 是造成异步编程困难的主要原因:我们一直习惯于“线性”地编写代码逻辑,但是大量异步操作所带来的回调函数,会把我们的算法分解地支离破碎.此时我们不能 用if来实现逻辑分支,也不能用while/for/do来实现循环,更不用提异步操作之间的组合.错误处理以及取消操作了.

jQuery 2.0.3 源码分析 Deferred(最细的实现剖析,带图)

转载http://www.cnblogs.com/aaronjs/p/3356505.html Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html ******************构建Deferred对象时候的流程图************************** **********************源码解析********************** 因为callback被剥离出去后,整个deferred

最细的实现剖析:jQuery 2.0.3源码分析Deferred

Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html **构建Deferred对象时候的流程图** **源码解析** 因为callback被剥离出去后,整个deferred就显得非常的精简 jQuery.extend({ Deferred:function(){} when:function() )}对于extend的继承这个东东,在之前就提及过jquery如何处理内部jquery与init相互引用this的问题 对于JQ的

Android7.0 MediaRecorder源码分析

最近在做Camera的方案(双进程打开一个Camera),涉及到使用MediaRecorder 进行录像,还是自己新建一套录像系统.接下来我将记录下本次源码分析的流程.  有关于Client和Server间的binder通信,会在下一篇里分析  (Client)  STEP ONE(Contructor) 1../base/media/java/android/media/MediaRecorder.java  在MediaRecorder的构造中就是使用了native_setup()的方法(N