成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

seajs 源碼解讀

LiangJ / 2437人閱讀

摘要:本文主要簡(jiǎn)單地解讀一下的源碼和模塊化原理。其中,是這次源碼解讀的核心,但我也會(huì)順帶介紹一下其他文件的作用的。對(duì)代碼比較簡(jiǎn)單,其實(shí)就是聲明一下全局的命名空間。然而,真正的核心在于處理模塊依賴的問(wèn)題。

seajs 簡(jiǎn)單介紹

seajs是前端應(yīng)用模塊化開發(fā)的一種很好的解決方案。對(duì)于多人協(xié)作開發(fā)的、復(fù)雜龐大的前端項(xiàng)目尤其有用。簡(jiǎn)單的介紹不多說(shuō),大家可以到seajs的官網(wǎng)seajs.org參看介紹。本文主要簡(jiǎn)單地解讀一下seajs的源碼和模塊化原理。如果有描述不實(shí)的地方,希望大家指正和交流。
注:本文的解析是基于seajs的2.2.1版本。

目錄結(jié)構(gòu)

解壓seajs之后的src目錄結(jié)構(gòu)如下:

intro.js             -- 全局閉包頭部
sea.js               -- 基本命名空間

util-lang.js         -- 語(yǔ)言增強(qiáng)
util-events.js       -- 簡(jiǎn)易事件機(jī)制
util-path.js         -- 路徑處理
util-request.js      -- HTTP 請(qǐng)求
util-deps.js         -- 依賴提取

module.js            -- 核心代碼
config.js            -- 配置
outro.js             -- 全局閉包尾部

src目錄存放主要的seajs源代碼。各個(gè)文件的作用也如上面所示。其中,module.js是這次源碼解讀的核心,但我也會(huì)順帶介紹一下其他文件的作用的。
sea.js對(duì)代碼比較簡(jiǎn)單,其實(shí)就是聲明一下全局的seajs命名空間。
intro.js和outro.js則是我們熟悉的匿名函數(shù)包裹基本代碼的方式,只是這里比較特別的是,這段匿名函數(shù)被拆分成intro.js和outro.js兩個(gè)文件。這樣的做法主要是方便調(diào)試,在調(diào)試的環(huán)境下,不引用intro.js和outro.js即可以直接在全局里暴露seajs內(nèi)部的接口,調(diào)試起來(lái)比較方便。intro.js和outro.js合并起來(lái)的代碼如下:

(function(global, undefined) {
    if (global.seajs) {
      return
    }
    // ....
})(this);

其他文件的用途就不一一重復(fù)敘述了,看列表即可。

頁(yè)面如何動(dòng)態(tài)加載js文件

在解析seajs的源碼和原理之前,讓我們來(lái)回憶一下,在沒有seajs或者requirejs的情況下,最原始的動(dòng)態(tài)腳本加載方法是怎樣的。方法很簡(jiǎn)單:其實(shí)就是創(chuàng)建一個(gè)script的標(biāo)簽,設(shè)置了src為你想要加載的腳本url,把script標(biāo)簽append到Dom里去就想了,so easy!沒錯(cuò),絕大部分模塊加載js庫(kù)的原理都是如此。

var script = document.createElement("script");
script.setAttribute("src", "example.js");
script.onload = function() {
    console.log("script loaded!");
};
document.body.appendChild(script);

上述代碼即可以完成一次簡(jiǎn)單的動(dòng)態(tài)腳本加載。然而,seajs真正的核心在于處理模塊依賴的問(wèn)題。在前端JS開發(fā)領(lǐng)域,尤其是復(fù)雜的web應(yīng)用,模塊依賴問(wèn)題一直是令人頭疼的問(wèn)題。
很簡(jiǎn)單的道理,例如A、B、C、D四個(gè)模塊對(duì)應(yīng)于A.js、B.js、C.js、D.js四個(gè)文件。他們之間的依賴關(guān)系例如以下:

A 依賴 B

B 依賴 C和D

問(wèn)題在于,如何找出模塊里的依賴關(guān)系,如何確保A在運(yùn)行前已經(jīng)加載了B等等。這些都是前端模塊化和模塊依賴需要解決的問(wèn)題。

模塊化實(shí)現(xiàn)思路

seajs的模塊化實(shí)現(xiàn)原理,說(shuō)簡(jiǎn)單其實(shí)不簡(jiǎn)單,說(shuō)復(fù)雜其實(shí)也不是很復(fù)雜。主要思路可以用下面這一段代碼來(lái)說(shuō)明:

Module.define = function (id, deps, factory) {
    // 獲取代碼中聲明的依賴關(guān)系
    deps = parseDependencies(factory.toString());
    // 保存
    Module.save();
    // 匹配到url
    var url = Module.resolve(id);
    // 加載腳本
    script.url = url;
    loadScript();
    // 執(zhí)行factory并保存模塊的引用
    ...
};
獲取代碼中聲明的依賴

首先我們來(lái)看看如何獲取代碼中聲明需要依賴的模塊。一般情況下,seajs中同步加載模塊的寫法是類似這樣的:


define("scripts/a", function(require, exports, module) { var factory = function() { var moduleB = require("scripts/b"); ... }; module.exports = factory; });

那么需要獲取依賴的信息,我們可以借助Function的toString方法,一個(gè)函數(shù)的toString方法是會(huì)返回函數(shù)本身的代碼的(對(duì)于JavaScript自身的函數(shù),會(huì)返回[native code])。只需要正則表達(dá)式來(lái)匹配require關(guān)鍵詞后面的引用關(guān)系即可。所以seajs中函數(shù)parseDependencies的寫法就像這樣(這一部分代碼在util-deps.js):

var SLASH_RE = //g
var REQUIRE_RE = /"(?:"|[^"])*"|"(?:"|[^"])*"|/*[Ss]*?*/|/(?:/|[^/
])+/(?=[^/])|//.*|.s*require|(?:^|[^$])requires*(s*([""])(.+?)1s*)/g
function parseDependencies(code) {
  var ret = []
  code.replace(SLASH_RE, "")
        // 匹配require關(guān)鍵詞,找出依賴關(guān)系
      .replace(REQUIRE_RE, function(m, m1, m2) {
        if (m2) {
          ret.push(m2)
        }
      })
  return ret
}
通過(guò)id來(lái)匹配腳本的url地址

然后找出代碼中聲明的依賴id,通過(guò)id來(lái)匹配正確的腳本url地址。這一部分的代碼在util-path.js

function id2Uri(id, refUri) {
  if (!id) return ""

  id = parseAlias(id)
  id = parsePaths(id)
  id = parseVars(id)
  id = normalize(id)

  var uri = addBase(id, refUri)
  uri = parseMap(uri)

  return uri
}

這里有個(gè)特別的地方,類似require("a/b/c")這樣的寫法,seajs是如何知道腳本地址的絕對(duì)路徑的呢?道理很簡(jiǎn)單,就是通過(guò)seajs自己往dom里添加的id為"seajsnode"的script節(jié)點(diǎn)或者是當(dāng)前html中最后一個(gè)script節(jié)點(diǎn),通過(guò)這些節(jié)點(diǎn)的src屬性獲取腳本的絕對(duì)路徑。

模塊加載過(guò)程

讓我們把目光移回到核心的module.js中。seajs為模塊的加載過(guò)程定義了6種狀態(tài)。

var STATUS = Module.STATUS = {
  // 1 - The `module.uri` is being fetched
  FETCHING: 1,
  // 2 - The meta data has been saved to cachedMods
  SAVED: 2,
  // 3 - The `module.dependencies` are being loaded
  LOADING: 3,
  // 4 - The module are ready to execute
  LOADED: 4,
  // 5 - The module is being executed
  EXECUTING: 5,
  // 6 - The `module.exports` is available
  EXECUTED: 6
}

也就是:
* FETCHING 開始加載當(dāng)前模塊
* SAVED 當(dāng)前模塊加載完成并保存模塊數(shù)據(jù)
* LOADING 開始加載依賴的模塊
* LOADED 依賴模塊已經(jīng)加載完成
* EXECUTING 當(dāng)前模塊執(zhí)行中
* EXECUTED 當(dāng)前模塊執(zhí)行完成

其實(shí)這一加載執(zhí)行過(guò)程并非線性的,當(dāng)前模塊在加載所依賴的模塊的是,所依賴的模塊同樣也需要進(jìn)行這一過(guò)程,直到所有的依賴都加載執(zhí)行完畢,當(dāng)前模塊才開始執(zhí)行。

在module.js中seajs中的一些方法說(shuō)明了上述整個(gè)流程。

Module.use 構(gòu)造一個(gè)沒有factory的模塊,開始整個(gè)加載流程,狀態(tài)初始化為FETCHING到SAVED;

Module.prototype.load 通過(guò)load方法,開始加載子模塊,狀態(tài)由SAVED到LOADING;

Module.prototype.onload 當(dāng)子模塊都加載完成后都會(huì)調(diào)用onload方法,狀態(tài)由LOADING到LOADED;

Module.prototype.exec 加載過(guò)程都結(jié)束了,開始執(zhí)行模塊,狀態(tài)由EXECUTING到EXECUTED;

這里每個(gè)方法的詳細(xì)過(guò)程就不一一解析,有興趣的同學(xué)可以去看源碼。
實(shí)際上,seajs會(huì)對(duì)加載過(guò)的模塊保存一份引用在cachedMods中,在require的時(shí)候會(huì)先調(diào)用緩存中的模塊。

seajs.require = function(id) {
  var mod = Module.get(Module.resolve(id))
  if (mod.status < STATUS.EXECUTING) {
    mod.onload()
    mod.exec()
  }
  return mod.exports
}
Module.get = function(uri, deps) {
  return cachedMods[uri] || (cachedMods[uri] = new Module(uri, deps))
}
總結(jié)

前端模塊化一直是前端開發(fā)中比較重要的一點(diǎn)。前端開發(fā)相對(duì)其他語(yǔ)言來(lái)說(shuō)比較特殊,尤其是對(duì)應(yīng)大型Web項(xiàng)目的前端代碼,如何簡(jiǎn)潔優(yōu)雅地劃分模塊,如何管理這些模塊的依賴問(wèn)題,這些都需要花一定的時(shí)間去認(rèn)識(shí)和探討。因此,Common.js(致力于設(shè)計(jì)、規(guī)劃并標(biāo)準(zhǔn)化 JavaScript API)的誕生開啟了“ JavaScript 模塊化的時(shí)代”。前端領(lǐng)域的模塊化方案,像requireJS、SeaJS等都是Common.js的實(shí)踐者,對(duì)我們規(guī)劃前端的代碼很有幫助。然而,問(wèn)題其實(shí)還有很多,seajs依然未能完全滿足前端模塊化開發(fā),在性能問(wèn)題、打包部署等方法還有著不足,不過(guò)技術(shù)的未來(lái)總在進(jìn)步,相信以后會(huì)有更好的解決方法。

參考

http://island205.github.io/HelloSea.js/
http://seajs.org/docs/#docs
http://chuansongme.com/account/wtp-notes

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/78062.html

相關(guān)文章

  • seajs 模塊源碼解讀

    摘要:這里的依賴都是通過(guò)來(lái)異步加載的,加載完畢之后立刻執(zhí)行函數(shù),在模塊文件執(zhí)行完畢后包括和其他代碼,觸發(fā)的事件。 入口 seajs.use seajs.use直接調(diào)用Module.use(),Module.use的源碼如下: // Use function is equal to load a anonymous module // ids:模塊標(biāo)識(shí),uri是dirname + _us...

    e10101 評(píng)論0 收藏0
  • Seajs源碼解讀

    摘要:如果這個(gè)模塊的時(shí)候沒有設(shè)置,就表示是個(gè)匿名模塊,那怎么才能與之前發(fā)起請(qǐng)求的那個(gè)相匹配呢這里就有了一個(gè)全局變量,先將元數(shù)據(jù)放入這個(gè)對(duì)象。模塊加載完畢的回調(diào)保存元數(shù)據(jù)到匿名模塊,為請(qǐng)求的不管是不是匿名模塊,最后都是通過(guò)方法,將元數(shù)據(jù)存入到中。 近幾年前端工程化越來(lái)越完善,打包工具也已經(jīng)是前端標(biāo)配了,像seajs這種老古董早已停止維護(hù),而且使用的人估計(jì)也幾個(gè)了。但這并不能阻止好奇的我,為了了...

    bigdevil_s 評(píng)論0 收藏0
  • JavaScript模塊化發(fā)展

    摘要:所有依賴這個(gè)模塊的語(yǔ)句,都定義在一個(gè)回調(diào)函數(shù)中,等到所有依賴加載完成之后前置依賴,這個(gè)回調(diào)函數(shù)才會(huì)運(yùn)行。如果將前面的代碼改寫成形式,就是下面這樣定義了一個(gè)文件,該文件依賴模塊,當(dāng)模塊加載完畢之后執(zhí)行回調(diào)函數(shù),這里并沒有暴露任何變量。 模塊化是我們?nèi)粘i_發(fā)都要用到的基本技能,使用簡(jiǎn)單且方便,但是很少人能說(shuō)出來(lái)但是的原因及發(fā)展過(guò)程。現(xiàn)在通過(guò)對(duì)比不同時(shí)期的js的發(fā)展,將JavaScript模...

    mengbo 評(píng)論0 收藏0
  • seajs源碼解析

    摘要:最后將執(zhí)行的結(jié)果暴露給對(duì)象。腳本事件在腳本執(zhí)行的時(shí)候不會(huì)立馬觸發(fā)解決辦法是通過(guò)腳本的來(lái)判斷總結(jié)以上就是對(duì)的一個(gè)大致的分析,如有錯(cuò)誤,歡迎指出。 Seajs是一款模塊化開發(fā)框架,遵循CMD規(guī)范。雖然到現(xiàn)在為止很多模塊打包工具比它更加的完善,但還是有必要拜讀一下的,畢竟為前端模塊化的發(fā)展做了很大的貢獻(xiàn),分析一下漲漲姿勢(shì)。文章主要從以下幾個(gè)方面來(lái)分析。有不對(duì)的地方,歡迎大家指出。 1、什么是...

    YPHP 評(píng)論0 收藏0
  • 閱讀sea.js源碼小結(jié)

    摘要:依賴信息是一個(gè)數(shù)組,比如上面的依賴數(shù)組是源碼如下是利用正則解析依賴的一個(gè)函數(shù)時(shí)間出發(fā)函數(shù)主要看這個(gè)部分注釋是防止拷貝該時(shí)間的回調(diào)函數(shù),防止修改,困惑了一下。對(duì)的賦值需要同步執(zhí)行,不能放在回調(diào)函數(shù)里。 sea.js想解決的問(wèn)題 惱人的命名沖突 煩瑣的文件依賴 對(duì)應(yīng)帶來(lái)的好處 Sea.js 帶來(lái)的兩大好處: 通過(guò) exports 暴露接口。這意味著不需要命名空間了,更不需要全局變量。...

    chavesgu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<