前端框架 seajs 使用总结

CMD 模块定义规范

  • seajs中,所有的javascript都遵循CMD模块定义规范。该规范明确定义了模块的定义格式和模块依赖的规则说明。
  • define.cmd : 一个空对象,可以用来判断当前页面是否存在cmd模块加载器,调用方法如下:
if(typeof define.cmd ==="undefined" || define.cmd){
    //Seajs存在cmd模块加载器
}
  • 与 RequireJS 的 AMD 规范相比,CMD 规范尽量保持简单,并与 CommonJS 和 Node.js 的 Modules 规范保持了很大的兼容性

seajs的特点


  • 简单友好的模块定义规范:遵循CMD规范。
  • 简单直观的代码组织方式:依赖自动加载,配置简洁清晰。

seajs的兼容性


Chrome 3+ ✔ Firefox 2+ ✔ Safari 3.2+ ✔ Opera 10+ ✔ IE 5.5+ ✔

理论上适用于任何浏览器,包括 Mobile 。
 
 

seajs的配置

seajs.config({
    // 别名配置
    alias: {
        'es5-safe': 'gallery/es5-safe/0.9.3/es5-safe',
        'json': 'gallery/json/1.0.2/json',
        'jquery': 'jquery/jquery/1.10.1/jquery'
    },
    // 路径配置
    paths: {
        'gallery': 'https://a.alipayobjects.com/gallery'
    },
    // 变量配置
    vars: {
        'locale': 'zh-cn'
    },
    // 映射配置
    map: [
        ['http://example.com/js/app/', 'http://localhost/js/app/']
    ],
    // 预加载项
    preload: [
        Function.prototype.bind ? '' : 'es5-safe',
        this.JSON ? '' : 'json'
    ],
    // 调试模式
    debug: true,
    // Sea.js 的基础路径
    base: 'http://example.com/path/to/base/',
    // 文件编码
    charset: 'utf-8'
});
 
 
 
 

alias Object

可以简化较长模块标示的书写。
seajs.config({
   // 别名配置
    alias: {
        'es5-safe': 'gallery/es5-safe/0.9.3/es5-safe',
        'json': 'gallery/json/1.0.2/json',
        'jquery': 'jquery/jquery/1.10.1/jquery'
    }
});
模块定义:
define(function(require, exports , module){
    var $ = require("jquery");
    //==>加载的是http://example.com/path/to/base/jquery/jquery/1.10.1/jquery.js
});
 

path Object

如果依赖层次较深,或者跨目录调用模块,path可以简化书写。
path 可以结合alias一起使用。
seajs.config({
    // 路径配置
    paths: {
        'gallery': 'https://a.alipayobjects.com/gallery'
    },
});
模块定义:
define(function(require,exports, module){
    var json = require("gallery/jsonparser");
    //==> 加载的是https://a.alipayobjects.com/gallery/jsonparser.js
});
 
vars Object
有些模块需要在运行时才能确定,可以使用vars指定。
vars 配置的是模块标识中的变量值,在模块标识中用 {key} 来表示变量。
 
seajs.config({
// 变量配置
    vars: {
        'local': 'zh-cn'
    },
});
模块定义
define(function(require, exports, module){
    var language_config = require("language/{local}.js");
    console.log(language_config);//获取中文配置文件
});
 
map Array
路由模块路径。
seajs.config({
    // 映射配置
    map: [
        ['http://example.com/js/app/', 'http://localhost/js/app/'],
        ['.js','-debg.js']
    ],
});
 
define(function(require, exports, module){
    var m_a = require("./a");
    //==>加载的是./a-debug.js
});
 

preLoad Array

预加载一些公共模块或者指定模块
seajs.config({
    // 预加载项
    preload: [
        Function.prototype.bind ? '' : 'es5-safe',
        this.JSON ? '' : 'json'
    ],
});
preLoad中的配置,需要等到use时才加载
seajs.use("./b",function(){
    //在加载b模块之前,预加载项已经加载完成。
});
preLoad中的配置,无法保证模块定义中已经加载完成并执行完成。
 

debug String

值为 true 时,加载器不会删除动态插入的 script 标签。插件也可以根据 debug 配置,来决策 log 等信息的输出。
 
base String
Sea.js 在解析顶级标识时,会相对 base 路径来解析。
 
charset String | Function
获取模块文件时,<script> 或 <link> 标签的 charset 属性。 默认是 utf-8
charset还可以是函数:
seajs.config({
    charset: function(url) {
        // xxx 目录下的文件用 gbk 编码加载
        if (url.indexOf('http://example.com/js/xxx') === 0) {
            return 'gbk';
        }
        // 其他文件用 utf-8 编码
        return 'utf-8';
    }
});
 
注意:seajs.config 可以多次执行,每次执行都会对配置项进行合并操作。
config 会自动合并不存在的项,对存在的项则进行覆盖。
 

建议seajs.config所在的js文件 

独立成一个文件时,一般通过 script 标签在页面中同步引入。
 
模块定义(define Function)
 
定义函数:
函数: define(factory)
其中,define方法是全局方法,作用域是window;factory可以是字符串,对象或者函数。
 
 
Factory类型:
 
  • 字符串或者对象。
   define({"username":"fol","age":"23"});    //factory是对象类型
    define("I love zhuzhou");    //factory是字符串类型
 
    如果模块的factory类型是字符串或者对象,那么该模块对外提供的接口(输出)就是字符串或者对象本身。
  • 函数
        定义格式1:
        define(function(require, exports, module){
        //todo
   });
 
      如果factory的类型是函数,那么给函数就是该模块的构造函数,执行该函数可以得到模块对外提供接口。
      factory默认传入的参数是require, exports, module。
      这种模块定义方式未指定模块id,模块依赖列表。
    
      定义格式2:
      define(id? , deps?, factory?)
      模块定义也可以接受两个以上参数,其中id定义模块的id,deps声明模块依赖的其他模块列表(Array)。
       define("user", ["jquery", "ajax", "upload", "json"], function(require, exports, module){
       // todo 
   });
 
    注意:id 和 deps参数是可选参数,可以通过构建工具自动生成。
    
 
模块引用(require Function)
 
 
模块加载函数:
 
函数: require( id? )
其中,require对象是factory的第一个参数,同样,require函数也是全局函数,作用域是window;
id是模块标识,一般在模块定义函数中被调用,用来获取其他模块提供的接口。
define(function( require, exports, module ) {
    var $ = require("jquery");  //引入jquery模块
    $(document).ready(function(){
        $("#bt").on("click", function(){
              console.log("the bt is clicked");
        });
    });
});
 
异步加载:
 
函数: require.async( id?, callback? )
其中,id是模块的标识,callback是模块异步加载完成后的回调函数。
 
define(function(require, exports, module){
    //异步加载单个模块
    require.async("plugins/bootstrap", function(b){
        b.doSomething();
    });
    //异步加载多个模块
    require.async(["./utils/datepicker","./utils/colorpicker"], function(datepicker, colorpicker){
        datepicker.init();
        colorpicker.init();
    });
});
注意:require()是同步执行的,require.async()是异步回调执行,require.async用来执行可以延迟加载执行的模块。
 
 
 
路径解析:
函数: require.resolve(id?)
其中,id 是模块的唯一标识,只用来解析模块的绝对路径。
define(function(require, exports, module){
    val jqueryPath = require.resolve("jquery");    //解析jquery的绝对路径    
    //http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.js
});
 
seajs 三个对象
 
exports Object
exports 是一个对象,负责对外提供模块接口。
define(function(require, exports, module){
      function trim() {
            return this.replace(/(^\s*)|(\s*$)/g, "")
      }
      exports.trim = trim;    //把内部函数暴露给其他模块
      exports.config = {"username":"foo", "age":"23"};//把内部对象暴露给其他模块
});
 
除了使用exports 对象对外提供接口外,还可以使用return直接对外提供接口
define(function(require, exports, module){
      function trim() {
            return this.replace(/(^\s*)|(\s*$)/g, "")
      }
      return {
        "trim":trim,
        "config":{"username":"foo", "age":23}
      }
});
如果模块中return语句是唯一的代码,那么可以使用define({})简化模块定义:
define({
   "trim":function(){
         
return this.replace(/(^\s*)|(\s*$)/g, "")
    },
    "config":{"username":"foo","age":23}
);
上面的格式可以适合定义JSONP模块。
注意:不能对exports重新赋值,这样虽然不会影响到module.exports 。但是无法对外提供接口,可以赋值module.exports达到对外提供接口的目的。
define(function(require, exports , module){
    module.exports = {
        trim:function(){ return this.replace(/(^\s*)|(\s*$)/g, "");},
        config:{"username":"foo", "age":23}
    }
});
 
module Object
module是一个对象,其中存储着于当前模块相关的一些方法和属性。
module.id:返回模块的标识
module.uri:返回模块的绝对路径。
define(function(require, exports, module){
    var module_uri = module.uri;
    //==> 
http://example.com/path/to/this/file.js
});
一般情况下,如果没有定义模块id, 那么module.id == module.uri,两者完全相同。
 
 
module.dependencies Array :返回当前模块依赖的模块列表。
module.exports :返回当前模块对外提供的接口对象。
注意:传递给factory方法的参数中exports 只是module.exports 的一个引用,只能通过exports来对外提供接口,但是module.exports可以是一个类的实例。
而且,对module.exports 赋值不能通过回调函数等方法异步执行,只能同步执行。
define(function(require, exports, module) {
  // exports 是 module.exports 的一个引用
  console.log(module.exports === exports); // true

  // 重新给 module.exports 赋值
  module.exports = new SomeClass();

  // exports 不再等于 module.exports
  console.log(module.exports === exports); // false
  //module.exports 不能异步执行
  setTimeout(function(){
    module.exports = {"username":"foo", "age":23};    //错误,无法对外提供接口
  }, 1000);

});

seajs.use Function 
函数:seajs.use(ids?, callback?)
作用:用在页面上加载其他模块。
 
//加载一个模块
seajs.use("./a");
 
//加载一个模块并回调
seajs.use("./a", function(a){
    a.doSomething();
});
 
//加载多个模块并回调
seajs.use(["jquery","./a"], function($, a){
    $("#bt").click(function(){
        //doSomething
    });
    a.doSomething();
});
注意:seajs.use方法和document.ready()方法没有必然关系,如果某些操作需要在document ready后才能执行操作,需要借助jquery等依赖模块。
 
 

seajs.cache Function

函数:seajs.cache();
可以用来查看当前页面加载的依赖模块列表。
 
seajs.resolve Function
函数:seajs.resolve(id?);
可以用来获取依赖模块的绝对路径。
 
 

seajs.data Function

函数:seajs.data();
可以用来seajs的所有配置以及一些内部变量,可以用在插件开发中。
 
 

模块标识与路径关系

模块标识:
模块标识主要以小驼峰,. 或者 .. 为主。
 
//在http://example.com/my/js/user.js中
define(function(require, exports, module){
    var path = require.resolve("./json");
    //==>路径为http://example.com/my/js/json.js
    
    var path2 = require.resolve("../json");
    //==> 路径为http://example.com/my/json.js
});
注意:以小驼峰开头的模块标识是顶级标识,以base为根目录加载模块文件;以.和..开头的模块标识是相对标识,以当前模块文件所在的位置为基础根目录加载。
 
//假设base 是http://examle.com/my/js
define(function(require, exports, module){
    var path = require.resolve("json");
    //==>路径为http://example.com/my/js/json.js
});
 
 
注意:页面中基于当前页面为根目录加载模块文件。
 
 

构建工具那些事

构建过程描述:
  • 提取操作
提取模块的标识id以及模块的其他依赖dependencies。
 
//a.js
define(function(require, exports, module){
    var b = require("./b");
});
 
经过提取操作,a.js文件会变成临时文件:
 
define("xxx/1.0.0/a",["./b"],function(require, exports, module){
    var b = require("./b");
});
  • 压缩操作
经过上面的提取操作后,构建工具就可以调用任何 JS 压缩工具来进行压缩了,require 参数也可以被压缩成任意字符。相比于其他的压缩工具,CMD模块的构建过程增加了id和dependencies的提取过程。
 
为什么要提取模块标识Id:
 
我们在模块定义过程中,可能会合并两个模块定义文件,使得模块管理更加方便和易用。
//a.js
define(function(require, exports, module){
    //todoSomething
});
 
//b.js
define(function(require, exports, module){
    //todoSomething
});
 
如果我们希望合并以上两个文件,那么会出现模块定义不清楚,无法确认加载哪个模块的问题,所以在模块构建过程中需要提取模块标识id。
 

此外,即便不合并,保持一个文件一个模块,如果压缩时不提取 id,那么在 IE6-9 下也有可能会出现问题。

 
为什么要提取模块依赖dependencies:
 
为了保证压缩文件随意压缩代码,构建工具在提取id的同时,也会提取dependencies数组。这样seajs不会再通过factory.toString()借助于正则匹配来获取依赖,直接可以通过factory函数的第二个参数拿到依赖数组。
注意:一旦factory的第二个参数定义了依赖数组,那么seajs将不会使用正则匹配的方式去分析并获取依赖,而是直接使用factory第二个参数提供的依赖数组作为所有的依赖。
posted @ 2020-12-15 18:48  星火卓越  阅读(620)  评论(0编辑  收藏  举报