足球盘口软件
当前位置: 足球盘口软件 > 前端 >
优化RequireJS项目的相关技巧总结足球盘口赔率分析软件

优化RequireJS项目的相关技巧总结,requirejs项目

 本文将演示如何合并与压缩一个基于RequireJS的项目。本文中将用到苦干个工具,这其中就包括Node.js。 因此,如果你手头上还没有Node.js可以点击此处下载一个。
动机

关于RequireJS已经有很多文章介绍过了。这个工具可以将你的JavaScript代码轻易的分割成苦干个模块(module)并且保持你的代码模块化与易维护性。这样,你将获得一些具有互相依赖关系的JavaScript文件。仅仅需要在你的HTML文档中引用一个基于RequireJS的脚本文件,所有必须的文件都将会被自动引用到这个页面上.

但是,在生产环境中将所有的JavaScript文件分离,这是一个不好的做法。这会导致很多次请求(requests),即使这个些文件都很小,也会浪费很多时间。 可以通过合并这些脚本文件,以减少请求的次数达到节省加载时间的目的。

另一种节省加载时间的技巧是缩小这些被加载文件的大小,相对小一些的文件会传输的更快一些。这个过程叫作最小化 (minification) ,它是通过小心的改变脚本文件的代码结构并且不改变代码的形为(behavior)和功能(functionality)来实现的。例如这些:去除不必要的空格,缩短(mangling,或都压缩)变量(variables)名与函数(methods,或者叫方法)名,等等。这种合并并压缩文件的过程叫做代码优化( optimization)。这种方法除了用于优化(optimization)JavaScript文件,同样适用于CSS文件的优化。

RequireJS有两个主要方法(method): define()和require()。这两个方法基本上拥有相同的定义(declaration) 并且它们都知道如何加载的依赖关系,然后执行一个回调函数(callback function)。与require()不同的是, define()用来存储代码作为一个已命名的模块。 因此define()的回调函数需要有一个返回值作为这个模块定义。这些类似被定义的模块叫作AMD (Asynchronous Module Definition,异步模块定义)。

如果你不大熟悉RequireJS或者不太明白我写的东西 - 不要担心。下面有一个关于这些的例子。
 
JavaScript应用程序的优化

在本小节中我将向大家展示如何优化Addy Osmani的TodoMVC Backbone.js + RequireJS 项目。 由于TodoMVC项目在不同的框架下包含许多TodoMVC实现,我下载了1.1.0版并提取出Backbone.js + RequireJS应用程序。点击这里下载该应用程序并解压下载到的zip文件。todo-mvc的解压目录将是我们这个例子的根目录(root path),从现在起我将把这个目录引用为<root>。

查看<root>/index.html的源代码,你会发现它仅仅包含了一个script标签(另外一个是当你使用Internet Explorer时引用的):
index.html引用脚本文件的代码  

<script data-main="js/main" src="js/lib/require/require.js"></script>
<!--[if IE]>
  <script src="js/lib/ie.js"></script>
<![endif]-->

其实,整个项目只需要引用require.js这个脚本文件。如果你在浏览器中运行这个项目,并且在你喜欢的(擅长的)调试工具的network标签中, 你就会发现浏览器同时也加载了其它的JavaScript文件:

足球盘口赔率分析软件 1

所有在红线边框里面的脚本文件都是由RequireJS自动加载的。

我们将用RequireJS Optimizer(RequireJS优化器)来优化这个项目。根据已下载的说明文件,找到r.js并将其复制到<root>目录。 jrburke的r.js是一个能运行基于AMD的项目的命令行工具,但更重要的是,它包含RequireJS Optimizer允许我们对脚本文件(scripts)合并与压缩。

RequireJS Optimizer有很多用处。它不仅能够优化单个JavaScript或单个CSS文件,它还可以优化整个项目或只是其中的一部分,甚至多页应用程序(multi-page application)。它还可以使用不同的缩小引擎(minification engines)或者干脆什么都不用(no minification at all),等等。本文无意于涵盖RequireJS Optimizer的所有可能性,在此仅演示它的一种用法。

正如我之前所提到的,我们将用到Node.js来运行优化器(optimizer)。用如下的命令运行它(optimizer):
运行RequireJS Optimizer  

$ node r.js -o <arguments>

有两种方式可以将参数传递给optimizer。一种是在命令行上指定参数:
在命令行上指定参数  

$ node r.js -o baseUrl=. name=main out=main-built.js

另一种方式是构建一个配置文件(相对于执行文件夹)并包含指定的参数 :  

$ node r.js -o build.js

build.js的内容:配置文件中的参数  

({
  baseUrl: ".",
  name: "main",
  out: "main-built.js"
})

我认为构建一个配置文件比在命令行中使用参数的可读性更高,因此我将采用这种方式。接下来我们就为项目创建一个<root>/build.js文件,并且包括以下的参数: <root>/build.j 

({
  appDir: './',
  baseUrl: './js',
  dir: './dist',
  modules: [
    {
      name: 'main'
    }
  ],
  fileExclusionRegExp: /^(r|build).js$/,
  optimizeCss: 'standard',
  removeCombined: true,
  paths: {
    jquery: 'lib/jquery',
    underscore: 'lib/underscore',
    backbone: 'lib/backbone/backbone',
    backboneLocalstorage: 'lib/backbone/backbone.localStorage',
    text: 'lib/require/text'
  },
  shim: {
    underscore: {
      exports: '_'
    },
    backbone: {
      deps: [
        'underscore',
        'jquery'
      ],
      exports: 'Backbone'
    },
    backboneLocalstorage: {
      deps: ['backbone'],
      exports: 'Store'
    }
  }
})

弄明白RequireJS Optimizer的所有配置项并不是本文的目的所在,但我想解释(描述)一下本文中我所采用的参数:

足球盘口赔率分析软件 2

 了解RequireJS Optimizer的更多介绍以及更多高级应用,除了其网页早先提供的资料,你可以点击此处查阅所有可用配置选项的详细的信息。

既然现在已经有了构建文件(build file),那么就可以运行优化器(optimizer)了。进入<root> 目录并执行如下命令:
运行优化器(optimizer)
 
$ node r.js -o build.js
一个新的文件夹会被生成:<root>/dist。重要的是要注意到,现在<root>/dist/js/main.js包含了所有已合并与压缩的具有依赖关系的文件。 此外,<root>/dist/css/base.css也被优化了。

运行优化后的项目,它看起来与未优化之前的项目完全一样。再检查一下该页面的网络传输(network traffic)信息,会发现仅有两个JavaScript文件被加载。

足球盘口赔率分析软件 3

 RequireJs Optimizer将服务器上的脚本文件从13个减少到2个并且将文件的总大小从164KB减少到58.6KB(require.js与main.js)。

开销

显然,在优化之后,我们再也没有必要引用require.js文件了。因为已经没有被分离的脚本文件了并且所有具有依赖关系的文件也已被加载。

尽管如此,优化过程将我们所有的脚本合并生成了一个优化后的脚本文件,其中包含了很多次define() 和require()调用。 因此,为了保证应用程序能够正常运行,define()和require()必须指定并实施到应用程序的某处(即包含这些文件)。

这会导致一个众所周知的开销:我们总是会有一些代码实现define()和require()。这些代码并不是应用程序的一部分,它们的存在仅仅是为我们的基础建设考虑(infrastructure considerations)。 当我们开发一个JavaScript库(JavaScript library)时,这个问题变得尤为巨大。相比RequireJS,这些库通常都很小,因此在库中包含它会造成一笔巨大的开销。

在我写这篇文章的时候,对于这方面的开销还没有一个完整的解决方案,但是我们可以使用almond来缓解这个问题。Almond是一个极简单的AMD加载器,它实现了RequireJS接口(API)。因此,可以用来在已优化过的代码中替代RequireJS实现,我们可以在项目中包含almond。
如令,我正致力于开发一个优化器(optimizer),它将能够优化RequireJS应用程序,而无需开销,但它仍然是一个新的项目(处于开发的初期阶段)因此这里没有任何关于它的展示。
下载与总结

  •     下载 未经优化的TodoMVC Backbone.js + RequireJS 项目或者查看它。
  •     下载 优化后的TodoMVC Backbone.js + RequireJS 项目(位于dist文件夹下)或查看它。

本文将演示如何合并与压缩一个基于RequireJS的项目。本文中将用到苦干个工具,这其中就包括...

 

目录

  • 概述
  • define方法:定义模块
  • require方法:调用模块
  • AMD模式小结
  • 配置require.js:config方法
  • 插件
  • 优化器r.js
  • 参考链接

概述

RequireJS是一个工具库,主要用于客户端的模块管理。它可以让客户端的代码分成一个个模块,实现异步或动态加载,从而提高代码的性能和可维护性。它的模块管理遵守AMD规范(Asynchronous Module Definition)。

RequireJS的基本思想是,通过define方法,将代码定义为模块;通过require方法,实现代码的模块加载。

首先,将require.js嵌入网页,然后就能在网页中进行模块化编程了。

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

上面代码的data-main属性不可省略,用于指定主代码所在的脚本文件,在上例中为scripts子目录下的main.js文件。用户自定义的代码就放在这个main.js文件中。

define方法:定义模块

define方法用于定义模块,RequireJS要求每个模块放在一个单独的文件里。

按照是否依赖其他模块,可以分成两种情况讨论。第一种情况是定义独立模块,即所定义的模块不依赖其他模块;第二种情况是定义非独立模块,即所定义的模块依赖于其他模块。

(1)独立模块

如果被定义的模块是一个独立模块,不需要依赖任何其他模块,可以直接用define方法生成。

define({

    method1: function() {},

    method2: function() {},

});

上面代码生成了一个拥有method1、method2两个方法的模块。

另一种等价的写法是,把对象写成一个函数,该函数的返回值就是输出的模块。

define(function () {

    return {

        method1: function() {},

        method2: function() {},

    };

});

 

后一种写法的自由度更高一点,可以在函数体内写一些模块初始化代码。

值得指出的是,define定义的模块可以返回任何值,不限于对象。

(2)非独立模块

如果被定义的模块需要依赖其他模块,则define方法必须采用下面的格式。

define(['module1', 'module2'], function(m1, m2) {

   ...

});

define方法的第一个参数是一个数组,它的成员是当前模块所依赖的模块。比如,['module1', 'module2']表示我们定义的这个新模块依赖于module1模块和module2模块,只有先加载这两个模块,新模块才能正常运行。一般情况下,module1模块和module2模块指的是,当前目录下的module1.js文件和module2.js文件,等同于写成['./module1', './module2']。

define方法的第二个参数是一个函数,当前面数组的所有成员加载成功后,它将被调用。它的参数与数组的成员一一对应,比如function(m1, m2)就表示,这个函数的第一个参数m1对应module1模块,第二个参数m2对应module2模块。这个函数必须返回一个对象,供其他模块调用。

define(['module1', 'module2'], function(m1, m2) {

 

    return {

        method: function() {

            m1.methodA();

            m2.methodB();

        }

    };

 

});

上面代码表示新模块返回一个对象,该对象的method方法就是外部调用的接口,menthod方法内部调用了m1模块的methodA方法和m2模块的methodB方法。

需要注意的是,回调函数必须返回一个对象,这个对象就是你定义的模块。

如果依赖的模块很多,参数与模块一一对应的写法非常麻烦。

define(

    [       'dep1', 'dep2', 'dep3', 'dep4', 'dep5', 'dep6', 'dep7', 'dep8'],

    function(dep1,   dep2,   dep3,   dep4,   dep5,   dep6,   dep7,   dep8){

        ...

    }

);

为了避免像上面代码那样繁琐的写法,RequireJS提供一种更简单的写法。

define(

    function (require) {

        var dep1 = require('dep1'),

            dep2 = require('dep2'),

            dep3 = require('dep3'),

            dep4 = require('dep4'),

            dep5 = require('dep5'),

            dep6 = require('dep6'),

            dep7 = require('dep7'),

            dep8 = require('dep8');

 

            ...

    }

 

});

下面是一个define实际运用的例子。

define(['math', 'graph'], 

    function ( math, graph ) {

        return {

            plot: function(x, y){

                return graph.drawPie(math.randomGrid(x,y));

            }

        }

    };

);

上面代码定义的模块依赖math和graph两个库,然后返回一个具有plot接口的对象。

另一个实际的例子是,通过判断浏览器是否为IE,而选择加载zepto或jQuery。

define(('__proto__' in {} ? ['zepto'] : ['jquery']), function($) {

    return $;

});

 

上面代码定义了一个中间模块,该模块先判断浏览器是否支持__proto__属性(除了IE,其他浏览器都支持),如果返回true,就加载zepto库,否则加载jQuery库。

require方法:调用模块

require方法用于调用模块。它的参数与define方法类似。

require(['foo', 'bar'], function ( foo, bar ) {

        foo.doSomething();

});

上面方法表示加载foo和bar两个模块,当这两个模块都加载成功后,执行一个回调函数。该回调函数就用来完成具体的任务。

require方法的第一个参数,是一个表示依赖关系的数组。这个数组可以写得很灵活,请看下面的例子。

require( [ window.JSON ? undefined : 'util/json2' ], function ( JSON ) {

  JSON = JSON || window.JSON;

 

  console.log( JSON.parse( '{ "JSON" : "HERE" }' ) );

});

上面代码加载JSON模块时,首先判断浏览器是否原生支持JSON对象。如果是的,则将undefined传入回调函数,否则加载util目录下的json2模块。

require方法也可以用在define方法内部。

define(function (require) {

   var otherModule = require('otherModule');

});

下面的例子显示了如何动态加载模块。

define(function ( require ) {

    var isReady = false, foobar;

 

    require(['foo', 'bar'], function (foo, bar) {

        isReady = true;

        foobar = foo() + bar();

    });

 

    return {

        isReady: isReady,

        foobar: foobar

    };

});

 

上面代码所定义的模块,内部加载了foo和bar两个模块,在没有加载完成前,isReady属性值为false,加载完成后就变成了true。因此,可以根据isReady属性的值,决定下一步的动作。

下面的例子是模块的输出结果是一个promise对象。

define(['lib/Deferred'], function( Deferred ){

    var defer = new Deferred(); 

    require(['lib/templates/?index.html','lib/data/?stats'],

        function( template, data ){

            defer.resolve({ template: template, data:data });

        }

    );

    return defer.promise();

});

上面代码的define方法返回一个promise对象,可以在该对象的then方法,指定下一步的动作。

如果服务器端采用JSONP模式,则可以直接在require中调用,方法是指定JSONP的callback参数为define。

require( [ 

    ""

], function (data) {

    console.log(data);

});

require方法允许添加第三个参数,即错误处理的回调函数。

require(

    [ "backbone" ], 

    function ( Backbone ) {

        return Backbone.View.extend({ /* ... */ });

    }, 

    function (err) {

        // ...

    }

);

require方法的第三个参数,即处理错误的回调函数,接受一个error对象作为参数。

require对象还允许指定一个全局性的Error事件的监听函数。所有没有被上面的方法捕获的错误,都会被触发这个监听函数。

requirejs.onError = function (err) {

    // ...

};

AMD模式小结

define和require这两个定义模块、调用模块的方法,合称为AMD模式。它的模块定义的方法非常清晰,不会污染全局环境,能够清楚地显示依赖关系。

AMD模式可以用于浏览器环境,并且允许非同步加载模块,也可以根据需要动态加载模块。

配置require.js:config方法

require方法本身也是一个对象,它带有一个config方法,用来配置require.js运行参数。config方法接受一个对象作为参数。

require.config({

    paths: {

        jquery: [

            '//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js',

            'lib/jquery'

        ]

    }

});

config方法的参数对象有以下主要成员:

(1)paths

paths参数指定各个模块的位置。这个位置可以是同一个服务器上的相对位置,也可以是外部网址。可以为每个模块定义多个位置,如果第一个位置加载失败,则加载第二个位置,上面的示例就表示如果CDN加载失败,则加载服务器上的备用脚本。需要注意的是,指定本地文件路径时,可以省略文件最后的js后缀名。

require(["jquery"], function($) {

    // ...

});

上面代码加载jquery模块,因为jquery的路径已经在paths参数中定义了,所以就会到事先设定的位置下载。

(2)baseUrl

baseUrl参数指定本地模块位置的基准目录,即本地模块的路径是相对于哪个目录的。该属性通常由require.js加载时的data-main属性指定。

(3)shim

有些库不是AMD兼容的,这时就需要指定shim属性的值。shim可以理解成“垫片”,用来帮助require.js加载非AMD规范的库。

require.config({

    paths: {

        "backbone": "vendor/backbone",

        "underscore": "vendor/underscore"

    },

    shim: {

        "backbone": {

            deps: [ "underscore" ],

            exports: "Backbone"

        },

        "underscore": {

            exports: "_"

        }

    }

});

上面代码中的backbone和underscore就是非AMD规范的库。shim指定它们的依赖关系(backbone依赖于underscore),以及输出符号(backbone为“Backbone”,underscore为“_”)。

插件

RequireJS允许使用插件,加载各种格式的数据。完整的插件清单可以查看官方网站。

下面是插入文本数据所使用的text插件的例子。

define([

    'backbone',

    'text!templates.html'

], function( Backbone, template ){

   // ...

});

上面代码加载的第一个模块是backbone,第二个模块则是一个文本,用'text!'表示。该文本作为字符串,存放在回调函数的template变量中。

优化器r.js

RequireJS提供一个基于node.js的命令行工具r.js,用来压缩多个js文件。它的主要作用是将多个模块文件压缩合并成一个脚本文件,以减少网页的HTTP请求数。

第一步是安装r.js(假设已经安装了node.js)。

npm install -g requirejs

然后,使用的时候,直接在命令行键入以下格式的命令。

node r.js -o <arguments>

<argument>表示命令运行时,所需要的一系列参数,比如像下面这样:

node r.js -o baseUrl=. name=main out=main-built.js

除了直接在命令行提供参数设置,也可以将参数写入一个文件,假定文件名为build.js。

({

    baseUrl: ".",

    name: "main",

    out: "main-built.js"

})

然后,在命令行下用r.js运行这个参数文件,就OK了,不需要其他步骤了。

node r.js -o build.js

下面是一个参数文件的范例,假定位置就在根目录下,文件名为build.js。

({

    appDir: './',

    baseUrl: './js',

    dir: './dist',

    modules: [

        {

            name: 'main'

        }

    ],

    fileExclusionRegExp: /^(r|build).js$/,

    optimizeCss: 'standard',

    removeCombined: true,

    paths: {

        jquery: 'lib/jquery',

        underscore: 'lib/underscore',

        backbone: 'lib/backbone/backbone',

        backboneLocalstorage: 'lib/backbone/backbone.localStorage',

        text: 'lib/require/text'

    },

    shim: {

        underscore: {

            exports: '_'

        },

        backbone: {

            deps: [

                'underscore',

                'jquery'

            ],

            exports: 'Backbone'

        },

        backboneLocalstorage: {

            deps: ['backbone'],

            exports: 'Store'

        }

    }

})

上面代码将多个模块压缩合并成一个main.js。

参数文件的主要成员解释如下:

  • appDir:项目目录,相对于参数文件的位置。
  • baseUrl:js文件的位置。
  • dir:输出目录。
  • modules:一个包含对象的数组,每个对象就是一个要被优化的模块。
  • fileExclusionRegExp:凡是匹配这个正则表达式的文件名,都不会被拷贝到输出目录。
  • optimizeCss: 自动压缩CSS文件,可取的值包括“none”, “standard”, “standard.keepLines”, “standard.keepComments”, “standard.keepComments.keepLines”。
  • removeCombined:如果为true,合并后的原文件将不保留在输出目录中。
  • paths:各个模块的相对路径,可以省略js后缀名。
  • shim:配置依赖性关系。如果某一个模块不是AMD模式定义的,就可以用shim属性指定模块的依赖性关系和输出值。
  • generateSourceMaps:是否要生成source map文件。

更详细的解释可以参考官方文档。

运行优化命令后,可以前往dist目录查看优化后的文件。

下面是另一个build.js的例子。

({

    mainConfigFile : "js/main.js",

    baseUrl: "js",

    removeCombined: true,

    findNestedDependencies: true,

    dir: "dist",

    modules: [

        {

            name: "main",

            exclude: [

                "infrastructure"

            ]

        },

        {

            name: "infrastructure"

        }

    ]

})

上面代码将模块文件压缩合并成两个文件,第一个是main.js(指定排除infrastructure.js),第二个则是infrastructure.js。

参考链接

  • NaorYe, Optimize (Concatenate and Minify) RequireJS Projects
  • Jonathan Creamer, Deep dive into Require.js
  • Addy Osmani, Writing Modular JavaScript With AMD, CommonJS & ES Harmony
  • Jim Cowart, Five Helpful Tips When Using RequireJS
  • Jim Cowart, Using r.js to Optimize Your RequireJS Project
返回顶部