seajs源码理解(一)

seajs用于管理和加载js模块化(其实也可以用来加载css的),他的api非常简单,重要的就use,define等几个,源码一共也不到1000行。

源码目录结构如下:

config.js:seajs.config()api,data配置变量

intro.js:全局window注入,闭包开始

module.js:核心api,包管理api,包括seajs对象下的use,require和module加载的各种实现api

outro.js:传入window,闭包结束

sea.js:seajs对象申明

util-deps.js:处理依赖的工具函数(匹配factory中的require,获取依赖)

util-events.js:seajs中的事件触发

util-lang.js:类型判断工具函数

util-path.js:路径处理工具函数

util-request.js:处理加载工具函数

其中最核心的是seajs和modulejs两个文件。

接下来,对每个进行单独解读。

introoutro比较简单,跟jq等类库类似,申明自执行闭包函数,传入window和undefined对象,为了压缩和效率。

util-deps.js代码只有20行,其中最重要的是一个正则

var REQUIRE_RE = /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^\/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*require|(?:^|[^$])\brequire\s*\(\s*(["'])(.+?)\1\s*\)/g

这个正则其实就是用来匹配require关键字,并找到require对应的模块提取出来,供加载使用。这个正则表达式非常巧妙,真正匹配的在最后一部分

(?:^|[^$])\brequire\s*\(\s*(["'])(.+?)\1\s*\)

取到require()括号内的模块名称,而前边的都可以认为是过滤器。因为运用了replace的高级函数,通过传入函数获取匹配到的值,前边的匹配规则是为了过滤代码中双引号、单引号、多行注释、单行注释、正则表达式中包含的require,这些require其实都是无效的,所以过滤掉。

 util-events.js为了支持加载模块各个过程中的事件处理和调试,seajs自己实现了事件机制,实现原理是内部定义事件对象(挂载在data上的events),把绑定的事件挂载到events上,触发和删除其实都是对event对象的操作。简易代码结构:

 

var events = data.events = {}

seajs.on = function(name, callback) {}

seajs.off = function(name, callback) {}

var emit = seajs.emit = function(name, data) {}

 

seajs中申明的事件有:config、error、load、fetch、request、exec、resolve、define

util-lang.js为内部提供类型判断函数,以及内部实现id自增,每调用一次id加1

function isType(type) {
  return function(obj) {
    return {}.toString.call(obj) == "[object " + type + "]"
  }
}

var isObject = isType("Object")
var isString = isType("String")
var isArray = Array.isArray || isType("Array")
var isFunction = isType("Function")
var isUndefined = isType("Undefined")

var _cid = 0
function cid() {
  return _cid++
}

util-path.js处理内部所有关于路径的转换,seajs对模块的查找都必须是绝对路径,所以针对用户的输入,需要对各种情况转为绝对路径来操作。

    // dirname("a/b/c.js?t=123#xx/zz") ==> "a/b/"
    function dirname(path) {}

   // realpath("http://test.com/a//./b/../c") ==> "http://test.com/a/c"
    function realpath(path) {}

    // normalize("path/to/a") ==> "path/to/a.js"
    // NOTICE: substring is faster than negative slice and RegExp
    function normalize(path) {}
    
    //处理config中的各个路径配置
    function parseAlias(id) {}

    function parsePaths(id) {}

    function parseVars(id) {}

    function parseMap(uri) {}
    //给传入的模块生成绝对路径
    function addBase(id, refUri) {}
   //这个函数内部调用了parseAlias、parsePaths、parseVars、parseMap
    function id2Uri(id, refUri) {}
    //内部挂载,最后都是通过resolve来访问
    seajs.resolve = id2Uri

util-request.js是真正的底层加载模块函数,seajs对模块的加载,也就是js或者css的加载,底层都是通过往页面内插入标签实现,就是依赖这个工具库。

    function request(url, callback, charset, crossorigin) {
       /*内部根据传入参数判断css还是js文件,然后往页面head插入对应的link和script标签,并调用addOnload*/
        addOnload(node, callback, isCSS, url)
    }

    //这个函数需要判断css或js有没有加载完,加载完毕之后才会执行移除script标签操作并执行回调。内部做了兼容处理,判断css加载完做了特殊处理,通过内部函数pollCss实现。js的加载通过script节点的onload事件或者onreadystatechange执行相应回调即可。
    function addOnload(node, callback, isCSS, url) {}

    //内部函数,判断css是否加载完,通过link标签的sheet的属性(兼容性需要判断到link标签下sheet属性下的cssRule属性)是否存在来判断,内部做了循环,每毫秒去监测一次这个值
    pollCss()

   //内部函数,在IE6-9下onload判断script有兼容性问题,触发的时机不对,所以需要通过interactive属性来判断
    getCurrentScript()
// For Developers
    seajs.request = request

sea.js申明版本信息和seajs对象。

var seajs = global.seajs = {
  // The current version of Sea.js being used
  version: "@VERSION"
}

var data = seajs.data = {}

config.js中做了两件事:1.为data对象挂载几个有用的属性。2.seajs.config函数保存配置到data对象下

// If loaderUri is `http://test.com/libs/seajs/[??][seajs/1.2.3/]sea.js`, the
// baseUri should be `http://test.com/libs/`
data.base = (loaderDir.match(BASE_RE) || ["", loaderDir])[1]

// The loader directory
data.dir = loaderDir

// The current working directory
data.cwd = cwd

// The charset for requesting files
data.charset = "utf-8"

// 这里有个nb用法,url参数如果带有?seajs-aaa,首先回去在所有模块之前去加载aaa模块
data.preload = (function() {
  return plugins
})()

//这个api的实现实际就是把传入的配置保存到data对象内
seajs.config = function(configData) {})

 

 除了最重要的module.js,其他源码已经分析完毕,modulejs我们下一篇专门分析。

转载于:https://www.cnblogs.com/weislywang/p/6667615.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值