SeaJS入门教程系列之使用SeaJS(二)

下载及安装

要在项目中使用SeaJS,你所有需要做的准备工作就是下载sea.js然后放到你项目的某个位置。
SeaJS项目目前托管在GitHub上,主页为 https://github.com/seajs/seajs/ 。可以到其git库的build目录下下载sea.js(已压缩)或sea-debug.js(未压缩)。
下载完成后放到项目的相应位置,然后在页面中通过<script>标签引入,你就可以使用SeaJS了。

SeaJS基本开发原则

在讨论SeaJS的具体使用前,先介绍一下SeaJS的模块化理念和开发原则。
使用SeaJS开发JavaScript的基本原则就是:一切皆为模块。引入SeaJS后,编写JavaScript代码就变成了编写一个又一个模块,SeaJS中模块的概念有点类似于面向对象中的类——模块可以拥有数据和方法,数据和方法可以定义为公共或私有,公共数据和方法可以供别的模块调用。
另外,每个模块应该都定义在一个单独js文件中,即一个对应一个模块。
下面介绍模块的编写和调用。

模块的定义及编写

模块定义函数define
SeaJS中使用“define”函数定义一个模块。因为SeaJS的文档并没有关于define的完整参考,所以我阅读了SeaJS源代码,发现define可以接收三个参数:


代码如下:

/**
* Defines a module.
* @param {string=} id The module id.
* @param {Array.|string=} deps The module dependencies.
* @param {function()|Object} factory The module factory function.
*/
fn.define = function(id, deps, factory) {
    //code of function…
}

上面是我从SeaJS源码中摘录出来的,define可以接收的参数分别是模块ID,依赖模块数组及工厂函数。我阅读源代码后发现define对于不同参数个数的解析规则如下:
如果只有一个参数,则赋值给factory。
如果有两个参数,第二个赋值给factory;第一个如果是array则赋值给deps,否则赋值给id。
如果有三个参数,则分别赋值给id,deps和factory。
但是,包括SeaJS的官方示例在内几乎所有用到define的地方都只传递一个工厂函数进去,类似与如下代码:


代码如下:

define(function(require, exports, module) {
    //code of the module...
});

个人建议遵循SeaJS官方示例的标准,用一个参数的define定义模块。那么id和deps会怎么处理呢?
id是一个模块的标识字符串,define只有一个参数时,id会被默认赋值为此js文件的绝对路径。如example.com下的a.js文件中使用define定义模块,则这个模块的ID会赋值为 http://example.com/a.js ,没有特别的必要建议不要传入id。deps一般也不需要传入,需要用到的模块用require加载即可。

工厂函数factory解析

工厂函数是模块的主体和重点。在只传递一个参数给define时(推荐写法),这个参数就是工厂函数,此时工厂函数的三个参数分别是:
1.require——模块加载函数,用于记载依赖模块。
2.exports——接口点,将数据或方法定义在其上则将其暴露给外部调用。
3.module——模块的元数据。
这三个参数可以根据需要选择是否需要显示指定。
下面说一下module。module是一个对象,存储了模块的元信息,具体如下:
1.module.id——模块的ID。
2.module.dependencies——一个数组,存储了此模块依赖的所有模块的ID列表。
3.module.exports——与exports指向同一个对象。

三种编写模块的模式

第一种定义模块的模式是基于exports的模式:


代码如下:

define(function(require, exports, module) {
    var a = require('a'); //引入a模块
    var b = require('b'); //引入b模块

var data1 = 1; //私有数据

var func1 = function() { //私有方法
        return a.run(data1);
    }

exports.data2 = 2; //公共数据

exports.func2 = function() { //公共方法
        return 'hello';
    }
});

上面是一种比较“正宗”的模块定义模式。除了将公共数据和方法附加在exports上,也可以直接返回一个对象表示模块,如下面的代码与上面的代码功能相同:


代码如下:

define(function(require) {
    var a = require('a'); //引入a模块
    var b = require('b'); //引入b模块

var data1 = 1; //私有数据

var func1 = function() { //私有方法
        return a.run(data1);
    }

return {
        data2: 2,
        func2: function() {
            return 'hello';
        }
    };
});

如果模块定义没有其它代码,只返回一个对象,还可以有如下简化写法:


代码如下:

define({
    data: 1,
    func: function() {
        return 'hello';
    }
});

第三种方法对于定义纯JSON数据的模块非常合适。

模块的载入和引用

模块的寻址算法
上文说过一个模块对应一个js文件,而载入模块时一般都是提供一个字符串参数告诉载入函数需要的模块,所以就需要有一套从字符串标识到实际模块所在文件路径的解析算法。SeaJS支持如下标识:
绝对地址——给出js文件的绝对路径。

如:


代码如下:

require("http://example/js/a");

就代表载入 http://example/js/a.js 。
相对地址——用相对调用载入函数所在js文件的相对地址寻找模块。
例如在 http://example/js/b.js 中载入


代码如下:

require("./c");

则载入 http://example/js/c.js 。
基址地址——如果载入字符串标识既不是绝对路径也不是以”./”开头,则相对SeaJS全局配置中的“base”来寻址,这种方法稍后讨论。
注意上面在载入模块时都不用传递后缀名“.js”,SeaJS会自动添加“.js”。但是下面三种情况下不会添加:
载入css时,如:


代码如下:

require("./module1-style.css");

路径中含有”?”时,如:


代码如下:

require(<a href="http://example/js/a.json?cb=func">http://example/js/a.json?cb=func</a>);

路径以”#”结尾时,如:


代码如下:

require("http://example/js/a.json#");

根据应用场景的不同,SeaJS提供了三个载入模块的API,分别是seajs.use,require和require.async,下面分别介绍。

seajs.use

seajs.use主要用于载入入口模块。入口模块相当于C程序的main函数,同时也是整个模块依赖树的根。上面在TinyApp小例子中,init就是入口模块。seajs.use用法如下:


代码如下:

//单一模式
seajs.use('./a');

//回调模式
seajs.use('./a', function(a) {
  a.run();
});

//多模块模式
seajs.use(['./a', './b'], function(a, b) {
  a.run();
  b.run();
});

一般seajs.use只用在页面载入入口模块,SeaJS会顺着入口模块解析所有依赖模块并将它们加载。如果入口模块只有一个,也可以通过给引入sea.js的script标签加入”data-main”属性来省略seajs.use,例如,上面TinyApp的index.html也可以改为如下写法:


代码如下:

<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>TinyApp</title>
</head>
<body>
    <p class="content"></p>
    <script src="./sea.js" data-main="./init"></script>
</body>
</html>

这种写法会令html更加简洁。

require

require是SeaJS主要的模块加载方法,当在一个模块中需要用到其它模块时一般用require加载:


代码如下:

var m = require('/path/to/module/file');

这里简要介绍一下SeaJS的自动加载机制。上文说过,使用SeaJS后html只要包含sea.js即可,那么其它js文件是如何加载进来的呢?SeaJS会首先下载入口模块,然后顺着入口模块使用正则表达式匹配代码中所有的require,再根据require中的文件路径标识下载相应的js文件,对下载来的js文件再迭代进行类似操作。整个过程类似图的遍历操作(因为可能存在交叉循环依赖所以整个依赖数据结构是一个图而不是树)。
明白了上面这一点,下面的规则就很好理解了:
传给require的路径标识必须是字符串字面量,不能是表达式,如下面使用require的方法是错误的:


代码如下:

require('module' + '1');
require('Module'.toLowerCase());

这都会造成SeaJS无法进行正确的正则匹配以下载相应的js文件。

require.async

上文说过SeaJS会在html页面打开时通过静态分析一次性记载所有需要的js文件,如果想要某个js文件在用到时才下载,可以使用require.async:


代码如下:

require.async('/path/to/module/file', function(m) {
    //code of callback...
});

这样只有在用到这个模块时,对应的js文件才会被下载,也就实现了JavaScript代码的按需加载。

SeaJS的全局配置
SeaJS提供了一个seajs.config方法可以设置全局配置,接收一个表示全局配置的配置对象。具体使用方法如下:


代码如下:

seajs.config({
    base: 'path/to/jslib/',
    alias: {
      'app': 'path/to/app/'
    },
    charset: 'utf-8',
    timeout: 20000,
    debug: false
});

其中base表示基址寻址时的基址路径。例如base设置为 http://example.com/js/3-party/ ,则:


代码如下:

var $ = require('jquery');

会载入 http://example.com/js/3-party/jquery.js 。
alias可以对较长的常用路径设置缩写。
charset表示下载js时script标签的charset属性。
timeout表示下载文件的最大时长,以毫秒为单位。
debug表示是否工作在调试模式下。

SeaJS如何与现有JS库配合使用

要将现有JS库如jQuery与SeaJS一起使用,只需根据SeaJS的的模块定义规则对现有库进行一个封装。例如,下面是对jQuery的封装方法:


代码如下:

define(function() {

//{{{jQuery原有代码开始
/*! 
 * jQuery JavaScript Library v1.6.1
 * http://jquery.com/
 *
 * Copyright 2011, John Resig
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * Includes Sizzle.js
 * http://sizzlejs.com/
 * Copyright 2011, The Dojo Foundation
 * Released under the MIT, BSD, and GPL Licenses.
 *
 * Date: Thu May 12 15:04:36 2011 -0400
 */
//...
//}}}jQuery原有代码结束

return $.noConflict();
});

SeaJS项目的打包部署

SeaJS本来集成了一个打包部署工具spm,后来作者为了更KISS一点,将spm拆出了SeaJS而成为了一个单独的项目。spm的核心思想是将所有模块的代码都合并压缩后并入入口模块,由于SeaJS本身的特性,html不需要做任何改动就可以很方便的在开发环境和生产环境间切换。但是由于spm目前并没有发布正式版本,所以本文不打算详细介绍,有兴趣的朋友可以参看其github项目主页 https://github.com/seajs/spm/。
其实,由于每个项目所用的JS合并和压缩工具不尽相同,所以spm可能并不是完全适合每个项目。在了解了SeaJS原理后,完全可以自己写一个符合自己项目特征的合并打包脚本。

(0)

相关推荐

  • Sea.JS知识总结

    SeaJS是一个遵循CommonJS规范的JavaScript模块加载框架.是一款现代的用于Web开发的模块加载工具,提供简单.极致的模块化体验.Sea.js 由阿里.腾讯等公司共同维护. 使用 Sea.js的好处: Sea.js 追求简单.自然的代码书写和组织方式,具有以下核心特性: 简单友好的模块定义规范:Sea.js 遵循 CMD 规范,可以像 Node.js 一般书写模块代码. 自然直观的代码组织方式:依赖的自动加载.配置的简洁清晰,可以让我们更多地享受编码的乐趣. Sea.js 还提供

  • seajs1.3.0源码解析之module依赖有序加载

    这里是seajs loader的核心部分,有些IE兼容的部分还不是很明白,主要是理解各个模块如何依赖有序加载,以及CMD规范. 代码有点长,需要耐心看: 复制代码 代码如下: /** * The core of loader */ ;(function(seajs, util, config) { // 模块缓存 var cachedModules = {} // 接口修改缓存 var cachedModifiers = {} // 编译队列 var compileStack = [] // 模

  • Seajs的学习笔记

    1.简介 Seajs,一个Web模块加载框架,追求简单.自然的代码书写和组织方式,:Sea.js 遵循 CMD 规范,模块化JS代码.依赖的自动加载.配置的简洁清晰,可以让程序员更多地专注编码. 2.优缺点 优点:1).提高可维护性.2).模块化编程.3).动态加载,前端性能优化 缺点:1).学习文档偏少且混乱,会更改团队使用JS的编写习惯,必须使用模块化编程.2).不太适合团队目前的情况,多JS文件但少改动,动态加载优势和模块化优势不明显.3). 需要配套使用SPM工具,JS的打包和管理工具.

  • SeaJS入门教程系列之SeaJS介绍(一)

    前言SeaJS是一个遵循CommonJS规范的JavaScript模块加载框架,可以实现JavaScript的模块化开发及加载机制.与jQuery等JavaScript框架不同,SeaJS不会扩展封装语言特性,而只是实现JavaScript的模块化及按模块加载.SeaJS的主要目的是令JavaScript开发模块化并可以轻松愉悦进行加载,将前端工程师从繁重的JavaScript文件及对象依赖处理中解放出来,可以专注于代码本身的逻辑.SeaJS可以与jQuery这类框架完美集成.使用SeaJS可以

  • SeaJS入门教程系列之完整示例(三)

    一个完整的例子上文说了那么多,知识点比较分散,所以最后我打算用一个完整的SeaJS例子把这些知识点串起来,方便朋友们归纳回顾.这个例子包含如下文件: 1.index.html--主页面.2.sea.js--SeaJS脚本.3.init.js--init模块,入口模块,依赖data.jquery.style三个模块.由主页面载入.4.data.js--data模块,纯json数据模块,由init载入.5.jquery.js--jquery模块,对 jQuery库的模块化封装,由init载入.6.s

  • seaJs的模块定义和模块加载浅析

    SeaJS 是由玉伯开发的一个遵循 CommonJS 规范的模块加载框架,可用来轻松愉悦地加载任意 JavaScript 模块和css模块样式.SeaJS非常小巧,小巧在于压缩和gzip后体积只有4K,而且接口和方法也非常少,SeaJS 就两个核心:模块定义和 模块的加载及依赖关系.SeaJS非常强大,SeaJS可以加载任意 JavaScript 模块和css模块样式,SeaJS会保证你在使用一个模块时,已经将所依赖的其他模块载入到脚本运行环境中.玉伯的说法,SeaJS可以让你享受写代码的乐趣,

  • 深入探寻seajs的模块化与加载方式

    由于一直在使用,所以了解了下seajs的源代码.这里是我对下面几个问题的理解: 1.seajs的require(XXX)的方法是怎样实现模块加载的? 2.为什么需要预加载? 3.为什么需要构建工具? 4.构建前后的代码究竟有些什么区别,为什么要这么做? 问题1: seajs的require(XXX)的方法是怎样实现模块加载的? 代码逻辑比较绕,对源代码的理解放在文章的末尾,这里先简单梳理下模块加载的逻辑: 1.从seajs.use方法入口,开始加载use到的模块. 2.use到的模块这时mod缓

  • seajs模块之间依赖的加载以及模块的执行

    本文介绍的是seajs模块之间依赖的加载以及模块的执行,下面话不多说直接来看详细的介绍. 入口方法 每个程序都有个入口方法,类似于c的main函数,seajs也不例外.系列一的demo在首页使用了seajs.use() ,这便是入口方法.入口方法可以接受2个参数,第一个参数为模块名称,第二个为回调函数.入口方法定义了一个新的模块,这个新定义的模块依赖入参提供的模块.然后设置新模块的回调函数,用以在loaded状态之后调用.该回调函数主要是执行所有依赖模块的工厂函数,最后在执行入口方法提供的回调.

  • seajs中模块的解析规则详解和模块使用总结

    seajs github 模块标识已经说的相对清楚了.但并没有面面俱到,特别是当你需要手写 [模块ID]和[模块依赖]的时候,或者自己写自动化工具来做 transport 的时候(ps:spm貌似适应性不是很强也不易用,毕竟每个项目的目录结构可能相差很大,且不易改变.当然如果他的定位是包管理工具就别指望它来做你的项目的自动化构建工具了),ID的解析规则就需要了解透彻了.注意事项:1. 顶级标识始终相对 base 基础路径解析.2. 绝对路径和根路径始终相对当前页面解析.3. require 和

  • seajs实现强制刷新本地缓存的方法分析

    本文实例讲述了seajs实现强制刷新本地缓存的方法.分享给大家供大家参考,具体如下: 1.为什么 由于每次上传js文件到服务器后用户本机存在本地缓存,导致用户需要强制清除缓存或者等待缓存失效才能使用新功能,极其不友好. 2.原理 seajs配置参数中有map属性为文件映射功能,其作用是通过seajs加载的文件映射为一个新的名称加载,形如 var version="0.0.1"; seajs.config({//seajs配置声明 map:[ [".js","

  • SeaJS 与 RequireJS 的差异对比

    "历史不是过去,历史正在上演.随着 W3C 等规范.以及浏览器的飞速发展,前端的模块化开发会逐步成为基础设施.一切终究都会成为历史,未来会更好."--引用玉伯原文最后一段话,我个人也非常赞同.既然谈到了"未来",我个人认为:前端 js 模块如果继续发展,其模块格式很可能会成为未来 WEB 一种标准规范,产生多种实现方式.就好比 JSON 格式一样,最终成为标准.被浏览器原生实现. 谁更有能成为未来的异步模块标准?SeaJS 遵循 CMD 规范,RequireJS 遵

  • 把jQuery的类、插件封装成seajs的模块的方法

    注:本文使用的seajs版本是2.1.1一.把Jquery封装成seajs的模块 复制代码 代码如下: define(function () { //这里放置jquery代码 把你喜欢的jquery版本放进来就好了 return $.noConflict();}); 调用方法:这样引进就可以像以前一样使用jquery 复制代码 代码如下: define(function (require, exports, module) {    var $ = require('./js/jquery');

随机推荐