zoukankan      html  css  js  c++  java
  • 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('
    ');
      }
    //  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 }
    View Code

    常用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
    }

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

     最后附上我写过注释的源码
  • 相关阅读:
    还得还得学啊
    感觉自己写的东西很死板啊
    好烦啊,不知道选哪个?
    222
    111
    愁人啊
    ssm+ajax实现登陆
    ssm框架搭建
    抽象类与接口
    代理设计模式
  • 原文地址:https://www.cnblogs.com/tzyy/p/4905585.html
Copyright © 2011-2022 走看看