资讯专栏INFORMATION COLUMN

seajs源码解析

YPHP / 1540人阅读

摘要:最后将执行的结果暴露给对象。脚本事件在脚本执行的时候不会立马触发解决办法是通过脚本的来判断总结以上就是对的一个大致的分析,如有错误,欢迎指出。

Seajs是一款模块化开发框架,遵循CMD规范。虽然到现在为止很多模块打包工具比它更加的完善,但还是有必要拜读一下的,毕竟为前端模块化的发展做了很大的贡献,分析一下涨涨姿势。
文章主要从以下几个方面来分析。有不对的地方,欢迎大家指出。

1、什么是CMD规范

CMD(Common Module Definition)是seajs在推广中规范出来的。详情请看 CMD模块定义规范

2、模块化开发的好处

2.1、提高代码可维护性

2.2、按需加载

2.3、避免变量污染

2.4、为前端工程化发展打下基础

3、seajs是如何加载模块,如何设计api的

先在浏览器控制台中打印下seajs这个全局变量,可以看到它挂载的一些对象和方法。让我们先对它有个整体的感受

seajs 从use函数开始,到加载module的过程大致如下:

模块加载完后浏览器会立马执行define函数,这个函数比较有意思,先看源代码

// Resolve id to uri
Module.resolve = function(id, refUri) {
  // Emit `resolve` event for plugins such as text plugin
  var emitData = { id: id, refUri: refUri }
  emit("resolve", emitData)

  return emitData.uri || seajs.resolve(emitData.id, refUri)
}

// Define a module
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
    }
  }

  // Parse dependencies according to the module factory code
  if (!isArray(deps) && isFunction(factory)) {
    deps = parseDependencies(factory.toString())
  }

  var meta = {
    id: id,
    uri: Module.resolve(id),
    deps: deps,
    factory: factory
  }

  // Try to derive uri in IE6-9 for anonymous modules
  if (!meta.uri && doc.attachEvent) {
    var script = getCurrentScript()

    if (script) {
      meta.uri = script.src
    }

    // NOTE: If the id-deriving methods above is failed, then falls back
    // to use onload event to get the uri
  }

  // Emit `define` event, used in nocache plugin, seajs node version etc
  emit("define", meta)

  meta.uri ? Module.save(meta.uri, meta) :
      // Save information for "saving" work in the script onload event
      anonymousMeta = meta
}

有意思的是如果factory是个函数,同时deps不是一个数组,那么会将factory序列化,然后通过正则匹配从其中解析出依赖

var REQUIRE_RE = /"(?:"|[^"])*"|"(?:"|[^"])*"|/*[Ss]*?*/|/(?:/|[^/
])+/(?=[^/])|//.*|.s*require|(?:^|[^$])requires*(s*([""])(.+?)1s*)/g
var SLASH_RE = //g

function parseDependencies(code) {
  var ret = []

  code.replace(SLASH_RE, "")
      .replace(REQUIRE_RE, function(m, m1, m2) {
        if (m2) {
          ret.push(m2)
        }
      })

  return ret
}

解析依赖后会扔到模块缓存系统中。之前模块加载的时候就可以看到,主模块加载完之后会执行factory函数。在执行factory函数的时候,会执行require加载的依赖。而require函数会判断模块状态是否已经执行过了,如果不是那么就加载依赖,加载完后执行依赖。最后将执行的结果暴露给exports对象。
到此,seajs的整个加载执行过程已经分析完毕。相比requirejs,seajs代码不是很多,但是能够感受到代码组织起来的精妙之处。

4、seajs是如何解决模块互相依赖问题

seajs解决模块的互相依赖是通过缓存系统和模块的状态来实现的。

5、seajs如何解决浏览器兼容性问题

5.1、onload 事件在 webkit<535.23和firfox<9.0中不支持

// `onload` event is not supported in WebKit < 535.23 and Firefox < 9.0
// ref:
//  - https://bugs.webkit.org/show_activity.cgi?id=38995
//  - https://bugzilla.mozilla.org/show_bug.cgi?id=185236
//  - https://developer.mozilla.org/en/HTML/Element/link#Stylesheet_load_events
var isOldWebKit = +navigator.userAgent
    .replace(/.*(?:AppleWebKit|AndroidWebKit)/(d+).*/, "$1") < 536

解决的办法是通过一个定时器,去监听link节点是否已经加载,并且解析完。

5.2、脚本onload事件在脚本执行的时候不会立马触发

function getCurrentScript() {
  if (currentlyAddingScript) {
    return currentlyAddingScript
  }

  // For IE6-9 browsers, the script onload event may not fire right
  // after the script is evaluated. Kris Zyp found that it
  // could query the script nodes and the one that is in "interactive"
  // mode indicates the current script
  // ref: http://goo.gl/JHfFW
  if (interactiveScript && interactiveScript.readyState === "interactive") {
    return interactiveScript
  }

  var scripts = head.getElementsByTagName("script")

  for (var i = scripts.length - 1; i >= 0; i--) {
    var script = scripts[i]
    if (script.readyState === "interactive") {
      interactiveScript = script
      return interactiveScript
    }
  }
}

解决办法是通过脚本的readyState===‘interactive’来判断

6、总结

以上就是对seajs的一个大致的分析,如有错误,欢迎指出。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93001.html

相关文章

  • seajs 源码解读

    摘要:本文主要简单地解读一下的源码和模块化原理。其中,是这次源码解读的核心,但我也会顺带介绍一下其他文件的作用的。对代码比较简单,其实就是声明一下全局的命名空间。然而,真正的核心在于处理模块依赖的问题。 seajs 简单介绍 seajs是前端应用模块化开发的一种很好的解决方案。对于多人协作开发的、复杂庞大的前端项目尤其有用。简单的介绍不多说,大家可以到seajs的官网seajs.org参看...

    LiangJ 评论0 收藏0
  • 阅读sea.js源码小结

    摘要:依赖信息是一个数组,比如上面的依赖数组是源码如下是利用正则解析依赖的一个函数时间出发函数主要看这个部分注释是防止拷贝该时间的回调函数,防止修改,困惑了一下。对的赋值需要同步执行,不能放在回调函数里。 sea.js想解决的问题 恼人的命名冲突 烦琐的文件依赖 对应带来的好处 Sea.js 带来的两大好处: 通过 exports 暴露接口。这意味着不需要命名空间了,更不需要全局变量。...

    chavesgu 评论0 收藏0
  • seajs 模块源码解读

    摘要:这里的依赖都是通过来异步加载的,加载完毕之后立刻执行函数,在模块文件执行完毕后包括和其他代码,触发的事件。 入口 seajs.use seajs.use直接调用Module.use(),Module.use的源码如下: // Use function is equal to load a anonymous module // ids:模块标识,uri是dirname + _us...

    e10101 评论0 收藏0
  • seajs入门教程

    摘要:一般也不需要传入,需要用到的模块用加载即可。根据应用场景的不同,提供了三个载入模块的,分别是和。主要用于载入入口模块。入口模块相当于语言的函数,同时也是整个模块依赖树的根。表示下载文件的最大时长,以毫秒为单位。 本文转自张洋,因为SeaJS更新版本很快,所以原文中很多地方不太适用,在这里发布一个更新版。 如何使用SeaJS 下载及安装在这里不赘述了,不了解的请查询官网。 基本开发原则 ...

    n7then 评论0 收藏0
  • Seajs源码解读

    摘要:如果这个模块的时候没有设置,就表示是个匿名模块,那怎么才能与之前发起请求的那个相匹配呢这里就有了一个全局变量,先将元数据放入这个对象。模块加载完毕的回调保存元数据到匿名模块,为请求的不管是不是匿名模块,最后都是通过方法,将元数据存入到中。 近几年前端工程化越来越完善,打包工具也已经是前端标配了,像seajs这种老古董早已停止维护,而且使用的人估计也几个了。但这并不能阻止好奇的我,为了了...

    bigdevil_s 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<