SeaJS代码层分析之一

SeaJS 2.2.1解析

seajs 1.1.0 代码结构和数据结构nuysoft

Alt text

文件结构

  • util-lang.js
  • util-event.js
  • util-path.js
  • util-request.js
  • util-deps.js
  • module.js - The core of module loader

util-lang

判断数据的类型

  • javascript数据类型
    • 原始数据类型:String、Number、Boolean
    • 组合数据类型:Object、Array
    • 特殊数据类型:Null、Undefined
  • 判断的方式
        function isType(type) {
            return function (obj) {
                return {}.toString.call(obj) == "[object " + type + "]"
            }
        }
        var isObject = isType("Object")
  • jquery判断方式
    type: function( obj ) {
		if ( obj == null ) {
			return String( obj );
		}
		return typeof obj === "object" || typeof obj === "function" ?
			class2type[ core_toString.call(obj) ] || "object" :
			typeof obj;
	},
    // Populate the class2type map
    jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
    	class2type[ "[object " + name + "]" ] = name.toLowerCase();
    });
  • 好处:
    • 非对外提供接口,内部使用,精简。
    • 封装直接产生是否是该类型的结果,不用频繁在代码里进行判断,好理解。(像jq判断$.type('')==="object")。
    • jquery使用seajs的方式的话,那么像isUndefined的函数就不能用isType("Undefined")来生成了. 而且枚举这些类型isXXX太繁琐。

util-events

该事件的实现,不是给开发者用于给自己定义的对象添加事件。而是为了通过事件提供可拓展接口 seajs插件指南,因此实现上比较简单

Sea.js 内部提供了 8 种事件(每个事件触发时会带上相关联的数据):
resolve -- 将 id 解析成为 uri 时触发
load -- 开始加载文件时触发
fetch -- 具体获取某个 uri 时触发
request -- 发送请求时触发
define -- 执行 define 方法时触发
exec -- 执行 module.factory 时触发
config -- 调用 seajs.config 时触发
error -- 加载脚本文件出现 404 或其他错误时触发


util-path.js

  • seajs.resolve 转换id到正常的文件的路径:
    function id2Uri(id, refUri) {
        if (!id) return ""

        id = parseAlias(id) //对id中包含的配置的别名进行处理
        id = parsePaths(id) //添加id对应的路径
        id = parseVars(id) //添加配置的路径可变变量,详见config
        id = normalize(id) //标准化文件路径,添加文件对应的js

        var uri = addBase(id, refUri) //添加id对应的地址,如果没有基准地址,则使用seajs文件的加载地址。
        /* 基准地址的获取
        var cwd = dirname(doc.URL)
         var loaderDir = dirname(getScriptAbsoluteSrc(loaderScript) || cwd)
            function getScriptAbsoluteSrc(node) {
                return node.hasAttribute ? // non-IE6/7
                    node.src :
                    // see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx
                    node.getAttribute("src", 4)
            }
         */
        uri = parseMap(uri) //url对配置的rule进行逐个map处理

        return uri
    }

util-request

加载js和css样式文件

  • HTML5支持使用docuemnt.head获取head
    var head = doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement
  • 加载js需要考虑获取base标签,因为在ie6非闭合的base存在时,head.appendChild(script)。移除时head.removeChild会出错。 script.parentNode.removeChild( script )就可以使用. 所以存在base,因插入在base之前
  • 加载js后需要进行移除,不然大量的js标签讲造成内存泄漏 Dynamic script generation and memory leaks
  • 函数
    • function request(url, callback, charset)
    • function addOnload(node, callback, isCSS, url)
    • function pollCss(node, callback) //css的回调函数是采用setTimeout定期检测加载的样式的属性
    • function getCurrentScript() //特别的地方:state interactive, ref: http://goo.gl/JHfFW

util-deps

解析代码里脚本的依赖(require)


module.js

Module模块

  1. Module接口
  • 静态方法
    • resolve : define/use/prototype.resolve => seajs.resolve==id2Uri
      • 转换id为加载的uri
    • define
      • 多参数解决方式,判断arguments.length,交换变量的值
        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
                }
    * 正则匹配,解析require依赖的模块:
    `deps = parseDependencies(factory.toString())`
    * 在 ie 6-9 浏览器中可以拿到当前运行js的路径 但是在标准浏览器中 这不可行 所以暂时先把 元信息赋值给anonymousModuleMeta = meta, 但由于是匿名模块,该模块的 uri 信息,需要等到 onload 触发后才能获取到。:
            meta.uri ? Module.save(meta.uri, meta) :
            // Save information for "saving" work in the script onload event
            anonymousMeta = meta
* save
* get
    * *获取一个已经存在在缓存里的模块或者创建一个新模块*
* preload 预加载. 用于seajs配置中进行预加载的模块
        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: 
        Module.use = function (ids, callback, uri) {
            var mod = Module.get(uri, isArray(ids) ? ids : [ids])
            
            //从缓存模块里面取依赖的模块并执行,取出里面的exports,并传给实际的回调
            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: 不具体分散到外部进行实例化,而采用Module自己的静态方法get创建,可以从中获取或缓存模块,并只对状态进行存储
            function Module(uri, deps) {
                this.uri = uri //保存id引用以便之后进行加载
                this.dependencies = deps || [] //依赖模块引用
                this.exports = null //导出接口对象引用
                this.status = 0 //当前模块的状态
        
                // Who depends on me
                this._waitings = {} //依赖于我的模块
        
                // The number of unloaded dependencies
                this._remain = 0 //未加载模块数
                
                // this.id is set when saving
                // this.dependencies is set when saving
                // this.factory is set when saving
                // this.exports is set when compiling
            }
            Module.get = function (uri, deps) {
                return cachedMods[uri] || (cachedMods[uri] = new Module(uri, deps))
            }
* resolve: 返回 分解出的依赖的模块的地址
* load
    * seajs.use => module=>preload=>use=>load
    * 
* onload
* fetch
* exec
  • define
    • 多参数解决方式,判断arguments.length,交换变量的值
        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
                }

posted on 2014-12-08 18:03  overview  阅读(196)  评论(0)    收藏  举报

导航