使用指南

2018-02-24 16:03 更新

剛才的示例很簡單?實際上Sea.js本身小巧而不失靈活,讓我們再來深入地了解下如何使用Sea.js!

定義模塊

Sea.js是CMD這個模塊系統(tǒng)的一個運行時,Sea.js可以加載的模塊,就是CMD規(guī)范里所指明的。那我們該如何編寫一個CMD模塊呢?

Sea.js提供了一個全局方法——define,用來定義一個CMD模塊。

define(factory)

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

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

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對象不可覆蓋,如果需要覆蓋請使用module.exports的形式(這與node的用法一致,在后面的原理介紹會有相關(guān)的解釋)。你可以使用module.exports來export任意的對象(包括字符串、數(shù)字等等)。

define(id?, dependencies?, factory)

id:String 模塊標識

dependencies:Array 模塊依賴的模塊標識

這種寫法屬于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沒有本質(zhì)區(qū)別,我更情愿把它稱作“具名模塊”。Sea.js從用于生產(chǎn)的角度來說,必須支持具名模塊,因為開發(fā)時模塊拆得太小,生產(chǎn)環(huán)境必須把這些模塊文件打包為一個文件,如果模塊都是匿名的,那就傻逼了。(為什么會傻逼?

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

define(anythingelse)

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

配置Sea.js

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

seajs.config(config)

config:Object,配置鍵值對。

Sea.js通過.configAPI來進行配置。你甚至可以在多個地方調(diào)用seajs.config來配置。Sea.js會mix傳入的多個config對象。

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']
})

上面兩個配置會合并為:

{
    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,在解析絕對路徑標識的模塊時所使用的base路徑。

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

在閱讀Sea.js這份文檔時看到:
當 sea.js 的訪問路徑中含有版本號時,base 不會包含 seajs/x.y.z 字串。 當 sea.js 有多個版本時,這樣會很方便。
即如果sea.js的引用路徑為http://example.com/assets/1.0.0/sea.js,則base仍為http://example.com/assets/。這種方便性,我覺得過了點

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

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

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

paths

paths:Object,如果目錄太深,可以使用paths這個配置項來縮寫,可以在require時少寫些代碼。

如果:

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配置項可以讓你在加載普通模塊之前提前加載一些模塊。既然所有模塊都是在use之后才加載的,preload有何意義?然,看下面這段:

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

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

還有一些別的配置,比如varsmap等,可以參考配置

使用模塊

seajs.use(id)

Sea.js通過use方法來啟動一個模塊。

seajs.use('./main')

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

Sea.js還有另外一種啟動模塊的方式:

seajs.use(ids, callbacks)

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

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

插件

Sea.js官方提供了7個插件,對Sea.js的功能進行了補充。

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

由此可見,Sea.js的插件主要是解決一些附加問題,或者是給Sea.js添加一些額外的功能。私覺得有些功能并不合適讓Sea.js來處理。

插件機制

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

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

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

構(gòu)建與部署

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

所以我們必須對模塊進行打包壓縮。這也是SPM的初衷。

SPM是什么?

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

SPM包括:

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

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

spm

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

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

spm的包規(guī)范

一個spm的模塊至少包含:

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

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

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

一個典型的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打包的,當然只要滿足兩個條件,就是一個spm的包。

安裝

$ npm install spm -g

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

help

我們可以運行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)容,目前有兩個插件initbuild。

也可以使用help來查看單個命令的用法:

$ 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來配置用戶信息、安裝方式以及源。

; Config username
$ spm config user.name island205

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

search

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

由于spm在包規(guī)范中加入了family的概念,常常想運行spm install backbone,發(fā)現(xiàn)并沒有backbone這個包。原因就是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來安裝了,注意我們必須使用包的全名,即族名/包名

$ 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

當然,Sea.js也是一個模塊,你可以通過下面的命令來安裝:

$ spm install seajs/seajs

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

build

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

$ spm plugin install build

安裝好之后,如果你使用的是標準的spm包模式,就可以直接運行spm build來進行標準的打包。

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

spm與spm2

spm與spm2

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

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

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

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

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

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

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

apm

apm的全稱是:

Alipay package manager

即支付寶的包管理工具。

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

謝謝 @lepture 的指正:

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

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號