【web前端自动化工作环境配置】10. RequireJS总结

RequireJS已经流行很久了,我们在项目中也曾经使用过它,现在有空就做一下总结:

1、声明不同js之间的依赖

2、可以按需,并行,延时载入js库

3、可以让我们的代码以模块化的方式组织

初看起来并不复杂

一、在html中引入require.js

在html中,添加这样的<script>标签

    <script data-main="../resource/main" src="../resource/require.js"></script>
通常使用requirejs的话,我们只需要导入requirejs即可,不需要显式导入其他的js库,因为这个工作会交给requirejs来做,属性data-main是告诉requirejs:你下载完以后,马上去载入真正的入口文件。它一般用来对requirejs进行配置,并且载入真正的程序模块。

二、在config.js中配置requirejs

config.js通常用来做两件事情:

(1)配置requirejs比如项目中用到哪些模块,文件路径是什么

(2)载入程序主模块

'use strict';
require.config({
    baseUrl:'../resource/app/',
    paths:{
        'css':'../lib/css.min',
        'jquery':'../lib/jquery.min',
        'angular':'../lib/angular.min',
        'bootstrap':'../lib/bootstrap.min',
        'util':'util',

    }
});
require(['jquery','util'],function($,hd){
    $('body').html('testing...').css({'backgroundColor':'red'});
    hd.show();
});
在paths中,我们声明了一个名为util的模块,以及它对应的js文件地址,在最理想的情况下,util.js的内容,应该使用requirejs的方法来定义模块:

'use strict';
define([],function(){
    return {
        show:function(){
            console.log('util');
        },
        message:function(){
            console.log('util.js');
        }
    }
});
这里的define是requirejs提供的函数。requirejs一共提供了两个全局变量:

(1)requirejs/require:用来配置requirejs及载入入口模块,如果其中一个命名被其他库使用了,我们可以用另外一个。

(2)define定义一个模块

另外还可以把require当做依赖的模块,然后调用它的方法

define(["require"], function(require) {
    var cssUrl = require.toUrl("./style.css");
});
三、依赖一个不使用requirejs方式的库

前面的代码是最理想的情况,即依赖的js文件,里面用了define(...)这样的方式来组织代码的。如果没用这种方式会出现什么情况?

比如这个hello.js

function hello() {
  alert("hello, world~");
}
它就按最普通的方式定义了一个函数,我们能在requirejs里使用它吗?

先看下面不能正常工作的代码:

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    hello: 'hello'
  }
});

requirejs(['hello'], function(hello) {
  hello();
});
这段代码会报错,提示

Uncaught TypeError: undefined is not a function 
原因是最后调用hello()的时候,这个hello是个undefined。这说明,虽然我们依赖了一个js库(它会被载入),但requirejs无法从中拿到代表它的对象注入进来供我们使用。在这种情况下,我们使用shim,将某个依赖中的某个全局变量暴露给requirejs,当做这个模块本身的引用。

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    hello: 'hello'
  },
  shim: {
    hello: { exports: 'hello' }
  }
});

requirejs(['hello'], function(hello) {
  hello();
});
再运行就正常了。

上面代码exports:‘hello’中的hello,是我们在hello.js中定义的hello函数。当我们使用function hello(){}的方法定义一个函数的时候,它就是全局可用的。如果我们选择了把它export给requirejs,那当我们的代码依赖于hello模块的时候,就可以拿到这个hello函数的引用了。所以exports可以把某个非requirejs方式代码中的某一个全局变量暴露出去,当做该模块以引用。

四、暴露多个变量:init

但如果我要同时暴露多个全局变量呢?比如:hello.js的定义其实是这样的:

function hello(){
    alert('hello world!');
}
function hello2(){
    alert('hello world again');
}
它定义了两个函数,而我两个都想要,这时就不能再用exports了,必须换成init函数:

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    hello: 'hello'
  },
  shim: {
    hello: {
      init: function() {
        return {
          hello: hello,
          hello2: hello2
        }
      }
    }
  }
});

requirejs(['hello'], function(hello) {
  hello.hello1();
  hello.hello2();
});
当exports与init同时存在的时候,exports将被忽略。

五、无主的与有主的模块

我遇到了一个折腾我不少时间的问题:为什么我只能使用 jquery 来依赖jquery, 而不能用其它的名字?
比如下面这段代码:

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    myjquery: 'lib/jquery/jquery'
  }
});

requirejs(['myjquery'], function(jq) {
  alert(jq);
});
会提示我:

jq is undefined
但是我仅仅改了个名字

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    jquery: 'lib/jquery/jquery'
  }
});

requirejs(['jquery'], function(jq) {
  alert(jq);
});
就一切正常了,能打印出jq的对象了。为什么?我始终没搞清楚问题在哪儿

(1)有主的模块

经研究,发现原来在jquery中已经定义了:

define('jquery', [], function() { ... });
它这里的 define 跟我们前面看到的 app.js 不同,在于它多了第一个参数 'jquery' ,表示给当前这个模块起了名字 jquery ,它已经是有主的了,只能属于 jquery .所以当我们使用另一个名字:

myjquery: 'lib/jquery/jquery'

去引用这个库的时候,它会发现,在 jquery.js 里声明的模块名 jquery 与我自己使用的模块名 myjquery 不能,便不会把它赋给 myjquery ,所以 myjquery 的值是 undefined 。所以我们在使用一个第三方的时候,一定要注意它是否声明了一个确定的模块名。

(2)无主的模块

如果我们不指明模块名,就像这样:

define([...], function() {
  ...
});
那么它就是无主的模块,我们可以在require.config里,使用任意一个模块名来引用它,这样的话,就让我们的命名非常自由,大部分的模块就是无主的。

六、为什么有的有主,有的无主

可以看到,无主的模块使用起来非常自由,为什么某些库(jquery,undercore)要把自己声明为有主的呢?

按照某些说法,这样做是出于性能的考虑,因为像jQuery,undercore这样的基础库,经常被其他的库依赖,如果声明为无主的,那么其他的库很可能起不同的模块名,这样当我们使用它们时,就可能会多次载入jQuery、undercode。

而把它们声明为主的,那么所有的模块只能使用同一个名字引用它们,这样系统就只会载入它们一次。

七、挖墙脚

对于有主的模块,我们还有一种方式可以挖墙脚:不把它们当做满足requirejs规范的模块,而当做普通js库,然后在shim中导出它们定义的全局变量。

requirejs.config({
  baseUrl: '/public/js',
  paths: {
    myjquery: 'lib/jquery/jquery'
  },
  shim: {
    myjquery: { exports: 'jQuery' }
  }
});

requirejs(['myjquery'], function(jq) {
  alert(jq);
});
这样通过暴露jquery这个全局变量给myjquery,我们就能正常的使用它了,不过我们完全没有必要这样挖墙脚,因为对于我们来说,似乎没有任何好处。























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值