Sea.js 使用指南

2020-04-26 10:01 更新

使用指南

剛才的示例很簡(jiǎn)單?實(shí)際上Sea.js本身小巧而不失靈活,讓我們?cè)賮?lái)深入地了解下如何使用Sea.js!

定義模塊

Sea.js是CMD這個(gè)模塊系統(tǒng)的一個(gè)運(yùn)行時(shí),Sea.js可以加載的模塊,就是CMD規(guī)范里所指明的。那我們?cè)撊绾尉帉懸粋€(gè)CMD模塊呢?

Sea.js提供了一個(gè)全局方法——define,用來(lái)定義一個(gè)CMD模塊。

define(factory)

define(function(require, exports, module) {
    // 模塊代碼
    // 使用require獲取依賴模塊的接口
    // 使用exports或者module來(lái)暴露該模塊的對(duì)外接口
})

factory是這樣一個(gè)函數(shù)function (require?, exports?, module?) {},如果模塊本身既不依賴其他模塊,也不提供接口,require、exportsmodule都可以省略。但通常會(huì)是以下兩種形式:

define(function(require, exports) {
    var Vango = require('vango')
    exports.drawCircle = function () {
        var vango = new Vango(document.body, 100, 100)
        vango.circle(50, 50, 50, {
            fill: true,
            styles:{
                fillStyle:"red"
            }
        })
    }
})

或者:

define(function(require, exports, module) {
    var Vango = require('vango');
    module.exports = {
        drawCircle: function () {
            var vango = new Vango(document.body, 100, 100);
            vango.circle(50, 50, 50, {
                fill: true,
                styles:{
                    fillStyle:"red"
                }
            });
        }
    };
});

注意:必須保證參數(shù)的順序,即需要用到require, exports不能省略;在模塊中exports對(duì)象不可覆蓋,如果需要覆蓋請(qǐng)使用module.exports的形式(這與node的用法一致,在后面的原理介紹會(huì)有相關(guān)的解釋)。你可以使用module.exports來(lái)export任意的對(duì)象(包括字符串、數(shù)字等等)。

define(id?, dependencies?, factory)

id:String 模塊標(biāo)識(shí)

dependencies:Array 模塊依賴的模塊標(biāo)識(shí)

這種寫法屬于Modules/Transport/D規(guī)范。

define('drawCircle', ['vango'], function(require, exports) {
    var Vango = require('vango');
    exports.drawCircle = function () {
        var vango = new Vango(document.body, 100, 100);
        vango.circle(50, 50, 50, {
            fill: true,
            styles:{
                fillStyle:"red"
            }
        });
    };
})

與CMD的define沒(méi)有本質(zhì)區(qū)別,我更情愿把它稱作“具名模塊”。Sea.js從用于生產(chǎn)的角度來(lái)說(shuō),必須支持具名模塊,因?yàn)殚_發(fā)時(shí)模塊拆得太小,生產(chǎn)環(huán)境必須把這些模塊文件打包為一個(gè)文件,如果模塊都是匿名的,那就傻了。

所以Sea.js支持具名模塊也是無(wú)奈之舉。

define(anythingelse)

除去以上兩種形式,在CMD標(biāo)準(zhǔn)中,可以給define傳入任意的字符串或者對(duì)象,表示接口就是對(duì)象或者字符串。不過(guò)這只是包含在標(biāo)準(zhǔn)中,在Sea.js并沒(méi)有相關(guān)的實(shí)現(xiàn)。

配置Sea.js

Sea.js為了能夠使用起來(lái)更靈活,提供了配置的接口??膳渲玫膬?nèi)容包括靜態(tài)服務(wù)的位置,簡(jiǎn)化模塊標(biāo)識(shí)或路徑。接下來(lái)我們來(lái)詳細(xì)地了解下這些內(nèi)容。

seajs.config(config)

config:Object,配置鍵值對(duì)。

Sea.js通過(guò).configAPI來(lái)進(jìn)行配置。你甚至可以在多個(gè)地方調(diào)用seajs.config來(lái)配置。Sea.js會(huì)mix傳入的多個(gè)config對(duì)象。

seajs.config({
    alias: {
        'jquery': 'path/to/jquery.js',
        'a': 'path/to/a.js'
    },
    preload: ['seajs-text']
})
seajs.config({
    alias: {
        'underscore': 'path/to/underscore.js',
        'a': 'path/to/biz/a.js'
    },
    preload: ['seajs-combo']
})

上面兩個(gè)配置會(huì)合并為:

{
    alias: {
        'jquery': 'path/to/jquery.js',
        'underscore': 'path/to/underscore.js',
        'a': 'path/to/biz/a.js'
    },
    preload: ['seajs-text', 'seajs-combo']

}

config可以配置的鍵入下:

base

base:String,在解析絕對(duì)路徑標(biāo)識(shí)的模塊時(shí)所使用的base路徑。

默認(rèn)地,在不配置base的情況下,base與sea.js的引用路徑。如果引用路徑為http://example.com/assets/sea.js,則base為http://example.com/assets/

在閱讀Sea.js這份文檔時(shí)看到:
當(dāng) sea.js 的訪問(wèn)路徑中含有版本號(hào)時(shí),base 不會(huì)包含 seajs/x.y.z 字串。 當(dāng) sea.js 有多個(gè)版本時(shí),這樣會(huì)很方便。
即如果sea.js的引用路徑為http://example.com/assets/1.0.0/sea.js,則base仍為http://example.com/assets/。這種方便性,我覺(jué)得過(guò)了點(diǎn)。

使用base配置,根本上可以分離靜態(tài)文件的位置,比如使用CDN等等。

seajs.config({
    base: 'http://g.tbcdn.cn/tcc/'
})

如果我們有三個(gè)CDN域名,如何將靜態(tài)資源散列到這三個(gè)域名上呢?

paths

paths:Object,如果目錄太深,可以使用paths這個(gè)配置項(xiàng)來(lái)縮寫,可以在require時(shí)少寫些代碼。

如果:

seajs.config({
    base: 'http://g.tbcdn.cn/tcc/',
    paths: {
        'index': 's/js/index'
    }
})

則:

define(function(require, exports, module) {
    // http://g.tbcdn.cn/tcc/s/js/index/switch.js
    var Switch = require('index/switch')
});

alias

alias:Object,本質(zhì)上看不出和paths有什么區(qū)別,區(qū)別就在使用的概念上。

seajs.config({
    alias: {
        'jquery': 'jquery/jquery/1.10.1/jquery'
    }
})

然后:

define(function(require, exports, module) {
    // jquery/jquery/1.10.1/jquery
    var $ = require('jquery');
});

看出使用概念的區(qū)別了么?

preload

preload配置項(xiàng)可以讓你在加載普通模塊之前提前加載一些模塊。既然所有模塊都是在use之后才加載的,preload有何意義?然,看下面這段:

seajs.config({
    preload: [
        Function.prototype.bind ? '' : 'es5-safe',
        this.JSON ? '' : 'json'
    ]
});

preload比較適合用來(lái)加載一些核心模塊,或者是shim模塊。這是一個(gè)全局的配置,使用者無(wú)需關(guān)系核心模塊或者是shim模塊的加載,把注意力放在核心功能即可。

還有一些別的配置,比如vars、map等,可以參考配置。

使用模塊

seajs.use(id)

Sea.js通過(guò)use方法來(lái)啟動(dòng)一個(gè)模塊。

seajs.use('./main')

在這里,./main是main模塊的id,Sea.js在main模塊LOADED之后,執(zhí)行這個(gè)模塊。

Sea.js還有另外一種啟動(dòng)模塊的方式:

seajs.use(ids, callbacks)

seajs.use('./main', function(main) {
    main.init()
})

Sea.js執(zhí)行ids中的所有模塊,然后傳遞給callback使用。

插件

Sea.js官方提供了7個(gè)插件,對(duì)Sea.js的功能進(jìn)行了補(bǔ)充。

  • seajs-text:用來(lái)加載HTML或者模板文件;
  • seajs-style:提供了importStyle,動(dòng)態(tài)地向頁(yè)面中插入css;
  • seajs-combo:該插件提供了依賴combo的功能,能把多個(gè)依賴的模塊uri combo,減少HTTP請(qǐng)求;
  • seajs-flush:該插件是對(duì)seajs-combo的補(bǔ)充,或者是大殺器,可以先hold住前面的模塊請(qǐng)求,最后將請(qǐng)求的模塊combo成一個(gè)url,一次加載hold住的模塊;
  • seajs-debug:Fiddler用過(guò)么?這個(gè)插件基本就是提供了這樣一種功能,可以通過(guò)修改config,將線上文件proxy到本地服務(wù)器,便于線上開發(fā)調(diào)試和排錯(cuò);
  • seajs-log:提供一個(gè)seajs.log API,私覺(jué)得比較雞肋;
  • seajs-health:目標(biāo)功能是,分析當(dāng)前網(wǎng)頁(yè)的模塊健康情況。

由此可見(jiàn),Sea.js的插件主要是解決一些附加問(wèn)題,或者是給Sea.js添加一些額外的功能。私覺(jué)得有些功能并不合適讓Sea.js來(lái)處理。

插件機(jī)制

總結(jié)一下,插件機(jī)制大概就是兩種:

  • 使用Sea.js在加載過(guò)程中的事件,注入一些插件代碼,修改Sea.js的運(yùn)行流程,實(shí)現(xiàn)插件的功能;
  • 給seajs加入一些方法,提供一些額外的功能。

私還是覺(jué)得Sea.js應(yīng)該保持純潔;為了實(shí)現(xiàn)插件,在Sea.js中加入的代碼,感覺(jué)有點(diǎn)不值;combo這種事情,更希望采取別的方式來(lái)實(shí)現(xiàn)。Sea.js應(yīng)該做好運(yùn)行時(shí)。

構(gòu)建與部署

很多時(shí)候,某個(gè)工具或者類庫(kù),玩玩可以,但是一用到生產(chǎn)環(huán)境,就感覺(jué)力不從心了。就拿Sea.js來(lái)說(shuō),開發(fā)的時(shí)候根據(jù)業(yè)務(wù)將邏輯拆分到很多小模塊,邏輯清晰,開發(fā)方便。但是上線后,模塊太多,HTTP請(qǐng)求太多,就會(huì)拖慢頁(yè)面速度。

所以我們必須對(duì)模塊進(jìn)行打包壓縮。這也是SPM的初衷。

SPM是什么?

使用者認(rèn)為SPM是Sea.js Package Manager,但是實(shí)際上代表的是Static Package Manager,及靜態(tài)包管理工具。如果大家有用過(guò)npm,你可以認(rèn)為SPM是一個(gè)針對(duì)前端模塊的包管理工具。當(dāng)然它不僅僅如此。

SPM包括:

  • 源服務(wù):類似于npm源服務(wù)器的源服務(wù);
  • 包管理工具:相當(dāng)于npm的命令行,安裝、發(fā)布模塊,解決模塊依賴;
  • 構(gòu)建工具:模塊CMD化、合并模塊、壓縮等;都是針對(duì)我們一開始提到的問(wèn)題;
  • 配置管理:管理配置;
  • 輔助功能:比較像Yeoman,以插件提供一些便于平時(shí)開發(fā)的組件。

SPM心很大,SPM囊括yo、bower和grunt這三個(gè)工具。

spm

spm is a package manager, it is not build tools.

這句話來(lái)自github上spm2的README文件。spm是一個(gè)包管理工具,不是構(gòu)建工具!,它與npm非常相似。

spm的包規(guī)范

一個(gè)spm的模塊至少包含:

-- dist
    -- overlay.js
    -- overlay.min.js
-- package.json
package.json

在模塊中必須提供一個(gè)package.json,該文件遵循Common Module Definition模塊標(biāo)準(zhǔn)。與node的package.json兼容。在此基礎(chǔ)上添加了兩個(gè)key。

  • family,即是包發(fā)布者在spmjs.org上的用戶名;
  • spm,針對(duì)spm的配置。

一個(gè)典型的package.json文件:

{
    "family": "arale",
    "name": "base",
    "version": "1.0.0",
    "description": "base is ....",
    "homepage": "http://aralejs.org/base/",
    "repository": {
        "type": "git",
        "url": "https://github.com/aralejs/base.git"
    },
    "keywords": ["class"],

    "spm": {
        "source": "src",
        "output": ["base.js", "i18n/*"],
        "alias": {
            "class": "arale/class/1.0.0/class",
            "events": "arale/events/1.0.0/events"
        }
    }
}
dist

dist目錄包含了模塊必要的模塊代碼;可能是使用spm-build打包的,當(dāng)然只要滿足兩個(gè)條件,就是一個(gè)spm的包。

安裝

$ npm install spm -g

安裝好了spm,那該如何使用spm呢?讓我們從help命令開始:

help

我們可以運(yùn)行spm help查看spm所包含的功能:

$ spm help

  Static Package Manager

  Usage: spm <command> [options]

  Options:

    -h, --help     output usage information
    -V, --version  output the version number

  System Commands:

    plugin         plugin system for spm
    config         configuration for spm
    help           show help information

  Package Commands:

    tree           show dependencies tree
    info           information of a module
    login          login your account
    search         search modules
    install        install a module
    publish        publish a module
    unpublish      unpublish a module

  Plugin Commands:

    init           init a template
    build          Build a standar cmd module.

spm包含三種命令,系統(tǒng)命令,即與spm本身相關(guān)(配置、插件和幫助),包命令,與包管理相關(guān),插件命令,插件并不屬于spm的核心內(nèi)容,目前有兩個(gè)插件initbuild。

也可以使用help來(lái)查看單個(gè)命令的用法:

$ spm help install

  Usage: spm-install [options] family/name[@version]

  Options:

    -h, --help               output usage information
    -s, --source [name]      the source repo name
    -d, --destination [dir]  the destination, default: sea-modules
    -g, --global             install the package to ~/.spm/sea-modules
    -f, --force              force to download a unstable module
    -v, --verbose            show more logs
    -q, --quiet              show less logs
    --parallel [number]      parallel installation
    --no-color               disable colorful print

  Examples:

   $ spm install jquery
   $ spm install jquery/jquery arale/class
   $ spm install jquery/jquery@1.8.2

config

我們可以使用config來(lái)配置用戶信息、安裝方式以及源。

; Config username
$ spm config user.name island205

; Or, config default source 
$ spm config source.default.url http://spmjs.org

search

spm是一個(gè)包管理工具,與npm類似,有自己的源服務(wù)器。我們可以使用search命令來(lái)查看源提供的包。

由于spm在包規(guī)范中加入了family的概念,常常想運(yùn)行spm install backbone,發(fā)現(xiàn)并沒(méi)有backbone這個(gè)包。原因就是backbone是放在gallery這族下的。

$ spm search backbone

  1 result

  gallery/backbone
  keys: model view controller router server client browser
  desc: Give your JS App some Backbone with Models, Views, Collections, and Events.

install

然后我們就可以使用install來(lái)安裝了,注意我們必須使用包的全名,即族名/包名。

$ spm install gallery/backbone

        install: gallery/backbone@stable
          fetch: gallery/backbone@stable
       download: repository/gallery/backbone/1.0.0/backbone-1.0.0.tar.gz
           save: c:\Users\zhi.cun\.spm\cache\gallery\backbone\1.0.0\backbone-1.0.0.tar.gz
        extract: c:\Users\zhi.cun\.spm\cache\gallery\backbone\1.0.0\backbone-1.0.0.tar.gz
          found: dist in the package
      installed: sea-modules\gallery\backbone\1.0.0
        depends: gallery/underscore@1.4.4

        install: gallery/underscore@1.4.4
          fetch: gallery/underscore@1.4.4
       download: repository/gallery/underscore/1.4.4/underscore-1.4.4.tar.gz
           save: c:\Users\zhi.cun\.spm\cache\gallery\underscore\1.4.4\underscore-1.4.4.tar.gz
        extract: c:\Users\zhi.cun\.spm\cache\gallery\underscore\1.4.4\underscore-1.4.4.tar.gz
          found: dist in the package
      installed: sea-modules\gallery\underscore\1.4.4

spm將模塊安裝在了sea_modules中,并且在~/.spm/cache中做了緩存。

`~sea-modules/
  `~gallery/
    |~backbone/
    | `~1.0.0/
    |   |-backbone-debug.js
    |   |-backbone.js
    |   `-package.json
    `~underscore/
      `~1.4.4/
        |-package.json
        |-underscore-debug.js
        `-underscore.js

spm還加載了backbone的依賴underscore。

當(dāng)然,Sea.js也是一個(gè)模塊,你可以通過(guò)下面的命令來(lái)安裝:

$ spm install seajs/seajs

seajs的安裝路徑為sea_modules/seajs/seajs/2.1.1/sea.js,看到這里,結(jié)合seajs頂級(jí)模塊定位的方式,對(duì)于seajs在計(jì)算base路徑的時(shí),去掉了seajs/seajs/2.1.1/的原因。

build

spm并不是以構(gòu)建工具為目標(biāo),它本身是一個(gè)包管理器。所以spm將構(gòu)建的功能以插件的功能提供出來(lái)。我們可以通過(guò)plugin命令來(lái)安裝build

$ spm plugin install build

安裝好之后,如果你使用的是標(biāo)準(zhǔn)的spm包模式,就可以直接運(yùn)行spm build來(lái)進(jìn)行標(biāo)準(zhǔn)的打包。

SPM2的功能和命令就介紹到這里,更多的命令在之后的實(shí)踐中介紹。

spm與spm2

spm與spm2

其實(shí)之前介紹的spm是其第二個(gè)版本spm2。spm的第一個(gè)版本可以在這里找到。

spm與spm2同樣都是包管理工具,那它們之間有什么不同呢?

  • 從定位上,spm2更加強(qiáng)調(diào)該工具是一個(gè)CMD包管理工具;
  • 從提供的用戶接口(cmd命令)spm2比起spm更加規(guī)范,作為包管理工具,在使用方式和命令都更趨同于npm;
  • 在spm2中,構(gòu)建命令以插件的方式獨(dú)立出來(lái),并且分層清晰;Transport和Concat封裝成了grunt,便于自定義build方式;基于基礎(chǔ)的grunt,構(gòu)建了一個(gè)標(biāo)準(zhǔn)的spm-build工具,用于構(gòu)建標(biāo)準(zhǔn)的CMD模塊;
  • 與此類似,deploy和init的功能都是以插件的形式提供的;
  • 修改了package.json規(guī)范。

為什么作者對(duì)spm進(jìn)行了大量的重構(gòu)?

之所以進(jìn)行大量的重構(gòu),就是為了保持spm作為包管理工具的特征。如npm一般,只指定最少的規(guī)范(package.json),提供包管理的命令,但是這個(gè)包如何構(gòu)建,代碼如何壓縮并不是spm關(guān)心的事情。

只有規(guī)則簡(jiǎn)單合理,只定義接口,不關(guān)心具體實(shí)現(xiàn),才有更廣的實(shí)用性。

spm本身是從業(yè)務(wù)需求成長(zhǎng)起來(lái)的一個(gè)包管理工具,spm1更多的是一些需求功能的堆砌,而spm2就是對(duì)這些功能的提煉,形成一套適用于業(yè)界的工具。

apm

apm的全稱是:

Alipay package manager

即支付寶的包管理工具。

apm是基于spm的一套專門為支付寶開發(fā)的工具集。我們可以這么看,spm2和apm是spm升級(jí)后的兩個(gè)產(chǎn)物,spm2更加專注于包管理和普適性,而apm更加專注于支付寶業(yè)務(wù)。由于業(yè)務(wù)細(xì)節(jié)和規(guī)模的不同,apm可能并不適合其他公司所用,所以需要spm2,而又因?yàn)橹Ц秾殬I(yè)務(wù)的特殊性和基因,必須apm。

謝謝 @lepture 的指正:

不一定要用 apm,只是 apm 把所有要用到的插件都打包好了,同時(shí)相應(yīng)的默認(rèn)配置也為支付寶做了處理,方便內(nèi)部員工使用,不用再配置了。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)