zoukankan      html  css  js  c++  java
  • 用requireJS进行模块化的网站开发

    用requireJS进行模块化的网站开发

    一.为什么选择requireJS

    现目前网上流行两种能进行模块化管理的模块加载器,一种便是遵循AMD(异步模块定义)规范的requireJS,另一种便是遵循CMD(通用模块定义)规范的seaJS.

    至于什么是AMD规范什么是CMD规范,简单点说就是一种代码的书写格式或者是API的调用形式。具体看:

    两个加载器的模块定义方式略有不同, CMD 推崇依赖就近,AMD 推崇依赖前置。看代码:

     1 // CMD
     2 define(function(require, exports, module) {
     3    var a = require('./a')
     4    a.doSomething()
     5    // 此处略去 100 行
     6    var b = require('./b') // 依赖可以就近书写
     7    b.doSomething()
     8    // ... 
     9 })
    10 
    11 // AMD 默认推荐的是
    12 define(['./a', './b'], function(a, b) {  // 依赖必须一开始就写好
    13     a.doSomething()
    14     // 此处略去 100 行
    15     b.doSomething()
    16     ...
    17 }) 

    在这里选择使用遵循AMD规范的requireJS的一个最重要原因是requireJS有完善的API文档,另外一个就是自己喜好选择了。

    注意:本文档不会详细的介绍AMD规范外的细节,需要了解详细的可以看官方文档

    二.requireJS 如何使用

    在开始页面中引入requireJS,像如下:

    1 <!--下面的data-main属性将会配置一个baseUrl(后面解释)的路径为scripts目录下-->  
    2 <script data-main="scripts/main.js" src="scripts/require.js"></script>

    在requireJS中最好根据baseUrl和path参数来配置模块,而尽量避免使用相对路径的形式,这会给后期的项目优化带来方便。(现在不清楚,后面的解释)

    在创建的项目中,目录结构可能像下面这样:

    在index.html页面中,引入的脚本为:

    <script data-main="js/app.js" src="js/require.js"></script>

    在app.js中代码如下:

     1 requirejs.config({
     2     //baseUrl路径配置加载默认的模块,像jquery之类的库
     3     baseUrl: 'js/lib',
     4     //如果有另外的一些自定义的模块在另一个文件路径下,可以配置paths,配置值后面无需加上后缀.js,加载器会默认将模块认为是js文件,会自动补全。
     5     paths: {
     6         app: '../app'
     7     }
     8 });
     9 
    10 // 在这里开始整个应用的逻辑,应注意线面的第一行中数组的三个模块名对应着callback中的3个参数,顺序不能搞错
    11 requirejs(['jquery', 'canvas', 'app/sub'],
    12 function   ($,        canvas,   sub) {
    13     //jQuery, canvas and the app/sub module are all
    14     //loaded and can be used here now.
    15 });

    三.定义模块

    1.如果模块中不需要依赖其他模块的,而仅仅是一个属性名/值对象字面量,可以像如下方式定义该模块:

    1 define({
    2     color: "black",
    3     size: "unisize"
    4 });

    2.如果该模块需要用到一些方法进行逻辑操作,可以在该模块中添加一个funciton来定义,这也是常用的模块定义方法:

    1 define(function () {
    2  //在这里TODO...
    3 
    4     return {
    5         color: "black",
    6         size: "unisize"
    7     }
    8 });

    3.第三种也就是我们可能最常用的模块定义方式,需要依赖一些模块,并会依据这些模块来进行一些操作:

     1 //文件存放于my/shirt.js
     2 define(["./cart", "./inventory"], function(cart, inventory) {
     3     //return an object to define the "my/shirt" module.
     4     return {
     5         color: "blue",
     6         size: "large",
     7         addToCart: function() {
     8             inventory.decrement(this);
     9             cart.add(this);
    10         }
    11     }
    12 });

    前面提到过,依赖项跟紧跟的函数中的参数一一对应,根据上面代码的依赖关系可以看出目录结构应该是(都位于同一级目录下):

    • my/cart.js
    • my/inventory.js
    • my/shirt.js

    4.如果我们需要定义一个没有返回值的模块,而仅仅作为一个函数使用,可以像下面这样返回一个函数:

     1 define(["my/cart", "my/inventory"],
     2     function(cart, inventory) {
     3         //return a function to define "foo/title".
     4         //It gets or sets the window title.
     5         return function(title) {
     6             return title ? (window.title = title) :
     7                    inventory.storeName + ' ' + cart.name;
     8         }
     9     }
    10 );

    5.模块的定义还可以给它加个模块名,仅仅需要在依赖项数组的前面加上一个字符串而已,像如下:

    1 define("foo/title",
    2     ["my/cart", "my/inventory"],
    3     function(cart, inventory) {
    4         //Define foo/title object in here.
    5    }
    6 );

    不过不推荐这样,这对代码移植来说是绝对是个累赘,官方也不推荐写上模块名。

    模块的一些其他说明:

    1.如果想生成模块的关联urls,可以使用require.toUrl()方法,前提是依赖require:

    1 define(["require"], function(require) {
    2     var cssUrl = require.toUrl("./style.css");
    3 });

    2.在项目开发中,常常需要控制台debug,可以想下面这样来调用对应的模块方法:

    1 require("module/name").callSomeFunction()

    3.循环依赖,如果有两个模块a和b,a需要依赖b,b需要依赖a,以模块b的定义为例:

    1 define(["require", "a"],
    2     function(require, a) {
    3         //"a" in this case will be null if a also asked for b,
    4         //a circular dependency.
    5         return function(title) {
    6             return require("a").doSomething();
    7         }
    8     }
    9 );

    注意到第六行,这是实现循环依赖的关键,就是讲require加为依赖项,看懂就行了。

    4.JSONP依赖,如果服务器端实现了JSONP提供数据,那么我们在定义模块的时候需要像下面这样去写:

    1 require(["http://example.com/api/data.json?callback=define"],
    2     function (data) {
    3         //The data object will be the API response for the
    4         //JSONP data call.
    5         console.log(data);
    6     }
    7 );

    第一行中,依赖项是一条url,并且在url后面手动加上"?callback=define",不包括引号,在后面的函数中便能够在参数中拿到JSONP返回的JSON格式的数据了。

    4.有时候我们可能需要取消模块的定义,利用require.undef()就可以完成这样的事情。

    小结:模块定义无非就是将功能的实现单独拆分出来,类似于在一个函数中return一个对象或者其他的东西,便于其他模块获取模块方法或信息。(模块定义没想象中的那么难,理解其基本原理即可)

    四.配置信息

    配置信息应该写在顶层html页面中,或者顶层的javascript文件中(非模块定义页)。像下面的index.html页面中配置信息。

     1 <script src="scripts/require.js"></script>
     2 <script>
     3   require.config({
     4     baseUrl: "/another/path",
     5     paths: {
     6         "some": "some/v1.0"
     7     },
     8     waitSeconds: 15
     9   });
    10   require( ["some/module", "my/module", "a.js", "b.js"],
    11     function(someModule,    myModule) {
    12         //This function will be called when all the dependencies
    13         //listed above are loaded. Note that this function could
    14         //be called before the page is loaded.
    15         //This callback is optional.
    16     }
    17   );
    18 </script>

    下面我们就来逐个讲解配置的信息:

    1.baseUrl:默认加载的模块路径,一般是脚本库路径,像jQuery.

    2.paths:这里可以定义一些不同于baseUrl路径的代码目录别名,但路径是相对于baseUrl的,这点注意。

    3.waitSeconds:这里设置脚本加载时长,如果超过该时长将放弃加载该脚本。

    4.shim:该参数配置那些非模块化的代码文件。例如jQuery,它的代码格式不是一个requireJS所定义的模块模式,因此,我们需要用shim参数来将jQuery配置成一个可用的模块。看下面的代码:

     1 requirejs.config({
     2     shim: {
     3         'backbone': {
     4             deps: ['underscore', 'jquery'],
     5             exports: 'Backbone'
     6         },
     7         'underscore': {
     8             exports: '_'
     9         },
    10         'foo': {
    11             deps: ['bar'],
    12             exports: 'Foo',
    13             init: function (bar) {
    14                 //类似于初始化,参数要传入依赖项
    15                 return this.Foo.noConflict();
    16             }
    17         }
    18     }
    19 });
    20 
    21 //然后在另一个文件名为MyModel.js的文件中创建模块,依赖项为shim中配置的backbone,这样就可以使用Backbone来编写自己的代码了
    22 define(['backbone'], function (Backbone) {
    23   return Backbone.Model.extend({});
    24 });

    5.map:该参数在项目中需要用到多版本脚本库的时候会非常有用,比方说一个模块需要依赖低版本的foo脚本库,而另一个模块需要依赖高版本的foo脚本库的时候,这时可以用map参数来正确匹配其依赖项。像下面的代码所示:

     1 requirejs.config({
     2     map: {
     3         'some/newmodule': {
     4             'foo': 'foo1.2'
     5         },
     6         'some/oldmodule': {
     7             'foo': 'foo1.0'
     8         }
     9     }
    10 });

    上面的代码中,some/newmodule模块需要依赖foo库的高版本,而some/oldmodule需要依赖其低版本, 目录结构应该是这样的:

    • foo1.0.js
    • foo1.2.js
    • some/
      • newmodule.js
      • oldmodule.js

    或者可以使用通配符*表示所有的模块,像下面代码所示:

     1 requirejs.config({
     2  map: {
     3         '*': {
     4             'foo': 'foo1.2'
     5         },
     6         'some/oldmodule': {
     7             'foo': 'foo1.0'
     8         }
     9     }
    10 });

    通配符匹配了所有的模块依赖高版本的foo库,除了后面紧跟着的some/oldmodule模块使用低版本库外。

    6.config:该参数用来配置模块信息,如下代码所示:

     1 requirejs.config({
     2     config: {
     3         'bar': {
     4             size: 'large'
     5         },
     6         'baz': {
     7             color: 'blue'
     8         }
     9     }
    10 });
    11 
    12 //这里是commonJS规范的写法,不详述
    13 define(function (require, exports, module) {
    14     //size值得到'large'
    15     var size = module.config().size;
    16 });
    17 
    18 define(['module'], function (module) {
    19     //color值得到'blue'
    20     var color = module.config().color;
    21 });

    7.packages:这个单词翻译成中文叫包,有后台语言编程基础的能理解其含义,在这里配置packages就是将整个包(包含一个或多个模块的文件)配置一个别名,默认的包里面最好有一个main.js的模块。像下面这样的目录结构:

    • project-directory/
      • project.html
      • script/
        • cart/
          • main.js
        • store/
          • main.js
          • util.js
        • main.js
        • require.js

    首先我们在project.html中包含require.js

    1 <script data-main="scripts/main" src="scripts/require.js"></script>

    接着我们可以像这样去加载包:

    1 require.config({
    2     "packages": ["cart", "store"]
    3 });
    4 
    5 require(["cart", "store", "store/util"],
    6 function (cart,   store,   util) {
    7     //use the modules as usual.
    8 });

    这里得稍作解释:require(["cart", "store", "store/util"]这行代码中"cart","store"表示了包名,requireJS会默认从包中寻找main.js去加载,因此"cart"=="cart/main.js",同理"store". 因为模块中还需要store包中的util模块,因此需要用包名/路径到模块的方式的去找到所要依赖的模块。

    或者你不想默认去加载main.js这个模块,你把main.js名字改为了store.js,那么你可以像下面这样配置:

    1 require.config({
    2     packages: [
    3         "cart",
    4         {
    5             name: "store",
    6             main: "store"
    7         }
    8     ]
    9 });

    8.context:不描述,因为我也不清楚它是什么意思,看英文原文:

    context: A name to give to a loading context. This allows require.js to load multiple versions of modules in a page, as long as each top-level require call specifies a unique context string.

    9.deps:依赖性,不多复述

    10.callback:这个还是有点用的,在所有依赖项加载完成后,可以在callback中做些啥子测试的都Ok.

    11.enforceDefine:强制定义,这个东西设置为true的时候会在非define()定义模块的时候抛出一些异常,不用过度理会这个配置(我是这么觉得)。

    12.xhtml:不理解,所以不解释,应该用的不多。

    13.urlArgs:这个东东就相当有用了,在开发阶段应该将这个配置加上,等到项目发布的时候再将他去除,切记切记。这里有一个配置例子:

    urlArgs: "bust=" +  (new Date()).getTime()
    

    scriptType:名字就叫脚本类型,看一下这段英文吧,不懂翻译:

    scriptType:Specify the value for the type="" attribute used for script tags inserted into the document by RequireJS. Default is "text/javascript". To use Firefox's JavaScript 1.8 features, use "text/javascript;version=1.8".

    五.高级应用

    1.如果有加载CDN上的脚本情况,我们可以预留一个本地脚本,以防无法加载CDN上的文件,因此我们可以如下配置:

     1 requirejs.config({
     2     //为了能够查看IE中的异常加上enforceDefine
     3     enforceDefine: true,
     4     paths: {
     5         jquery: [
     6             'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min',
     7             //如果CDN加载失败,就加载本地
     8             'lib/jquery'
     9         ]
    10     }
    11 });
    12 
    13 require(['jquery'], function ($) {
    14 });

    2.有时候为了检查错误,我们可以重写requirejs.onError()方法:

    1 requirejs.onError = function (err) {
    2  console.log(err.requireType);
    3     if (err.requireType === 'timeout') {
    4         console.log('modules: ' + err.requireModules);
    5     }
    6 
    7     throw err;
    8 };

    3.requirejs本身不支持dom加载完成才去执行脚本,但有一个已经完成的模块为我们提供了该功能,当然你需要下载该模块到本地:

    1 require(['domReady'], function (domReady) {
    2   domReady(function () {
    3     //TODO...
    4   });
    5 });

    domReady模块能帮我们在dom加载完成后采取执行脚本,但这里有一种跟简便的方式去写这个domReady方法,在依赖项domReady后面紧跟一个!号即可。

    1 require(['domReady!'], function (doc) {
    2 });

    其他更多内容请看requireJS API官方文档

  • 相关阅读:
    短视频直播源码开发,如何实现图标点击态的蒙层效果
    直播商城小程序开发,类似于转盘抽奖功能的实现
    Android语音聊天室源码开发,文本内容的分享发送与接收方法简述
    语音交友软件源码开发,axios+Vue实现上传文件显示进度功能
    一对一直播源码开发,前端如何实现整套视频直播技术流程
    04. 多地办公
    03. git分支
    02. git基本命令(适合于单机使用)
    01. git初识
    多人协作解决方案,git flow的使用
  • 原文地址:https://www.cnblogs.com/moyiqing/p/requirejs_ani.html
Copyright © 2011-2022 走看看