RequireJS异步模块加载

从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!

关于作者

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/nodejs-requirejs/

requirejs

前言

随着项目越来越大,依赖越来越多,代码的耦合度也越来越高。我们需要从架构的层面设计和优化代码的组织结构。RequireJS遵循AMD(异步模块定义)规范,从架构层抽象出“模块化”开发方案,并以标准化了模块化开发,同时和其他的开发框架保持兼容。

按照RequireJS的规范,我们能够更容易地实现更复杂,更强大的JS的富客户端程序。

目录

  1. RequireJS介绍
  2. RequireJS安装
  3. RequireJS基本使用
  4. nodejs构建简易的web服务器
  5. RequireJS模块化
  6. 多路径配置: baseUrl,paths
  7. 编译Requirejs模块

1. RequireJS介绍

RequireJS是一个Javascript的模块加载器,倡导的是一种模块化开发理念,核心价值是让 JavaScript 的模块化开发变得更简单自然。RequireJS 遵循的是 AMD(异步模块定义)规范,帮助用户异步按需的加载 JavaScript 代码,并解决 JavaScript 模块间的依赖关系,提升了前端代码的整体质量和性能。

2. RequireJS安装

我的系统环境

  • win7 64bit
  • Nodejs:v0.10.5
  • Npm:1.2.19

通过nodejs安装RequireJS


~ D:\workspace\javascript>mkdir nodejs-require && cd nodejs-require
~ D:\workspace\javascript\nodejs-require>npm install requirejs
npm http GET https://registry.npmjs.org/requirejs
npm http 200 https://registry.npmjs.org/requirejs
requirejs@2.1.8 node_modules\requirejs

全局安装requirejs:使用r.js工具。


~ D:\workspace\javascript\nodejs-require>npm install requirejs -g
npm http GET https://registry.npmjs.org/requirejs
npm http 304 https://registry.npmjs.org/requirejs
D:\toolkit\nodejs\r.js -> D:\toolkit\nodejs\node_modules\requirejs\bin\r.js
npm ERR! peerinvalid The package generator-karma does not satisfy its siblings' peerDependencies requirements!
npm ERR! peerinvalid Peer generator-angular@0.4.0 wants generator-karma@~0.5.0

npm ERR! System Windows_NT 6.1.7601
npm ERR! command "D:\\toolkit\\nodejs\\\\node.exe" "D:\\toolkit\\nodejs\\node_modules\\npm\\bin\\npm-cli.js" "install" "
requirejs" "-g"
npm ERR! cwd D:\workspace\javascript\nodejs-require
npm ERR! node -v v0.10.5
npm ERR! npm -v 1.2.19
npm ERR! code EPEERINVALID
npm ERR!
npm ERR! Additional logging details can be found in:
npm ERR!     D:\workspace\javascript\nodejs-require\npm-debug.log
npm ERR! not ok code 0

运行r.js命令失败


~ D:\workspace\javascript\nodejs-require>r.js

requirejs-rjs

我的win7环境中有错误,所以我只能在当前项目中运行r.js命令.(Linux下面可以全局安装。)


~ D:\workspace\javascript\nodejs-require>node node_modules\requirejs\bin\r.js -h
See https://github.com/jrburke/r.js for usage.

3. RequireJS基本使用

创建项目文件:


~ D:\workspace\javascript\nodejs-require>ls -l
-rwx------  1 4294967295 mkpasswd  65 Sep 19 13:32 a.js
-rwx------  1 4294967295 mkpasswd  59 Sep 18 20:33 b.js
-rwx------  1 4294967295 mkpasswd  82 Sep 18 20:33 c.js
-rwx------  1 4294967295 mkpasswd  69 Sep 18 20:33 d.js
-rwx------  1 4294967295 mkpasswd 206 Sep 19 13:32 index.html
-rwx------  1 4294967295 mkpasswd  48 Sep 19 13:31 main.js
drwx------+ 1 4294967295 mkpasswd   0 Sep 18 20:32 node_modules

对文件的定义:

  • index.html: 用于加载javascript文件,这里只加载RequireJS库
  • main.js: RequireJS的模块组,用来封装JS模块,通过js来加载其他的js文件
  • a.js: 一个JS的功能文件,不依赖其他库
  • b.js: 一个JS的功能文件,不依赖其他库
  • c.js: 一个JS的功能文件,依赖a.js和b.js
  • d.js: 一个JS的功能文件,依赖c.js

新增文件:index.html


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>RequireJS</title>
<script data-main="main" src="node_modules/requirejs/require.js"></script>
</head>
<h1>RequireJS Testing</h1>
<body>
</body>
</html>

新增文件:main.js


require(['a'],function(){
     a();
})

新增文件:a.js


function a(){
    console.log("aaaaaaaaaaaaaaaaaaaaaaaaaaa");
}

新增文件:b.js


function b(){
    console.log("bbbbbbbbbbbbbbbbbbbbb");
}

新增文件:c.js


function c(){
    a();
    b();
    console.log("cccccccccccccccccccccccc");
}

新增文件:d.js


function d(){
    c();
    console.log("ddddddddddddddddddddd");
}

打开浏览器:file:///D:/workspace/javascript/nodejs-require/index.html

requirejs-testing1

我们发现a.js被加载了,但我们并没有在index.html中定义script加载a.js。

  • 1. a.js的加载是RequireJS帮我们做的。
  • 2. 通过在HTML中定义data-main属性,注册一个模块main.js。
  • 3. 在main.js中,通过声明式定义,引入a.js文件
  • 4. 实现了对a.js加载的控制

通过require函数完全成了依赖管理的功能

require(dependencies, callback)
  • dependencies: 是我们要载入的js,使用相对路径。
  • callback: 是封装有程序逻辑。

所以,我们可以发现了RequireJS可以帮我们做,js依赖管理。我们可以再也不用在HTML中,以很土的加载顺序的方式管理JS文件了。这种优雅的注入可以帮我很好的管理全局变量。

4. nodejs构建简易的web服务器

在进行下一步之前,我们要用构建一个简易的web服务器,RequireJS的模块化加载需要web server的支持,通过本地文件的方式是不行的。

安装connect依赖


~ D:\workspace\javascript\nodejs-require>npm install connect
connect@2.9.0 node_modules\connect
├── uid2@0.0.2
├── methods@0.0.1
├── cookie-signature@1.0.1
├── pause@0.0.1
├── fresh@0.2.0
├── bytes@0.2.0
├── qs@0.6.5
├── buffer-crc32@0.2.1
├── cookie@0.1.0
├── debug@0.7.2
├── send@0.1.4 (range-parser@0.0.4, mime@1.2.11)
└── multiparty@2.1.8 (stream-counter@0.1.0, readable-stream@1.0.17)

新建文件:app.js


~ vi app.js

var connect = require('connect');
connect.createServer(
    connect.static(__dirname)
).listen(3000);

开发模式启动服务器:

supervisor app.js

打开浏览器:http://localhost:3000
requirejs-testing2

5. RequireJS模块化

虽然我们可以把所有的程序都写在main.js的callback中,但是程序会变得复杂,关系也不清楚。接下来,我们要做模块化的编程。RequireJS也实现了AMD定义的define的API。

define(id?, dependencies?, factory)
  • id:字符串,模块名称,可选。
  • dependencies: 是我们要载入的js,使用相对路径。
  • factory: 工厂方法,返回一个模块函数

新建ab.js,实现模块ab:


~ vi ab.js

define(function(){
    return {
        a: function(){
            a();
            console.log("ab.a()");
        },
        b:function(){
            b();
            console.log("ab.b()");
        }
    };
});

修改main.js


~ vi main.js

require(['a','b','ab'],function(a,b,ab){
    ab.b();
})

刷新页面:http://localhost:3000/

requirejs-testing3

调用流程:

  • 1. 通过main.js加载了a.js,b.js,ab.js文件
  • 2. ab.js构建一个ab的模块
  • 3. 在ab的模块中,调用b.js的b()函数

通过模块化的封装后,我们可以更优化我们的程序,程序结构更清晰!

6. 多路径配置: baseUrl,paths

如果我们需要加载多个js文件时,文件在不同的目录下面,我们可以通过baseUrl和paths来设置路径的默认位置和路径别名,方便我们定位文件。


#新建目录
~ mkdir folder
~ mkdir folder/f1
~ mkdir folder/f2

#把c.js移动到f1
~ mv c.js folder/f1

#把d.js移动到f2
~ mv d.js folder/f2

修改main.js


~ vi main.js
require.config({
    baseUrl:'./',
    paths:{
        f1:'folder/f1',
        f2:'folder/f2'
    }
});

require([
    'b',
    'a',
    'ab',
    'f1/c',
    'f2/d'
],function(a,b,ab,c){
    d();
})

刷新浏览器:

requirejs-testing4

我们看到c.js, d.js都被正确的加载了。

7. 编译Requirejs模块

最后要讲的是,通过r.js命令,对模块进行翻译优化。

在RequireJS安装部分,我们没有实现全局安装,因些只能在项目中通过相对路径使用r.js的命令。


~ D:\workspace\javascript\nodejs-require>node node_modules\requirejs\bin\r.js -o name=main out=main-build.js baseUrl=. pat
hs.f1="folder/f1" paths.f2="folder/f2"

Tracing dependencies for: main
Uglifying file: D:/workspace/javascript/nodejs-require/main-build.js

D:/workspace/javascript/nodejs-require/main-build.js
----------------
D:/workspace/javascript/nodejs-require/b.js
D:/workspace/javascript/nodejs-require/a.js
D:/workspace/javascript/nodejs-require/ab.js
D:/workspace/javascript/nodejs-require/folder/f1/c.js
D:/workspace/javascript/nodejs-require/folder/f2/d.js
D:/workspace/javascript/nodejs-require/main.js

我们会发现,新生成一个文件main-build.js


~ cat main-build.js

function b(){console.log("bbbbbbbbbbbbbbbbbbbbb")}function a(){console.log("aaaaaaaaaaaaaaaaaaaaaaaaaaa")}function c(){a(),b(),console.log("cccccccccccccccccccccccc")}function d(){c(),console.log("ddddddddddddddddddddd")}define("b",function(){}),define("a",function(){}),define("ab",[],function(){return{a:function(){a(),console.log("ab.a()")},b:function(){b(),console.log("ab.b()")}}}),define("f1/c",function(){}),define("f2/d",function(){}),require.config({baseUrl:"./",paths:{f1:"folder/f1",f2:"folder/f2"}}),require(["b","a","ab","f1/c","f2/d"],function(e,t,n,r){d()}),define("main",function(){});

新生成的main-build.js可以用来做为发布的功能模块。

本文简单的介绍了RequireJS的使用,如果我们的程序按照AMD的模块化思路去构建,我相信JS的代码,也是健壮的,可维护的,可传递的。

转载请注明出处:
http://blog.fens.me/nodejs-requirejs/

打赏作者

This entry was posted in Javascript语言实践, 架构设计

0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

21 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
lwbjing

要是能解决r.js全局安装失败的问题就好了。

Conan Zhang

这不是什么大问题。Linux都正常的

酷酷虫

requirejs 官网有解决办法

Conan Zhang

不错!回头试试。

eway

i am confused. is RequireJS is for clientside only? other than npm manage the package, any relations connecting RequireJS with NodeJS?

Conan Zhang

你好,希望中文回答你可以看懂。

1. RequireJS不是只为了浏览器端用的。
2. RequireJS提供了一种模块之间的调用关系,define(id?, dependencies?, factory),这种关系可以让js的程序层次更清楚。
3. Node框架通过require()实现这个调用关系,更通过npm实现的依赖。
4. RequireJS与Node虽然都是做同样的事情,但并不冲突,你通过可以在Node项目中使用RequireJS.
5. 从另外一个角度看,你也可以在非Node项目中使用RequireJS。当然,这样的非Node项目,可能是基于Rhino、V8,或者嵌入在其他语言中的js解释器等。

yanhaijing

奇怪的是 我直接安装r.js在全局 win下 却没有错误,是版本的问题吗

Conan Zhang

2个原因:
1. requirejs版本。
2. Win下,.net Framework中依赖包都完成好了。

[…] 工具包:underscore,moment,connet,later,log4js,passport,passport(oAuth),domain,require,reap, commander,retry […]

gjunp

想说,为什么一会windows环境一会Linux的,搞的人好混乱

Conan Zhang

并不是所有包,都可以window平台可以正常运行的。

carly32

请问在github上有完整的例子么。 我还是有很多的不明白。 请问main.js和其他例如a.js他们是放在那里的呢?

carly32

我觉得我失败的原因是,我是在node里面按照你的方法用requirejs的。请问在nodejs项目下,如何使用呢?

carly32

我知道nodejs下如何使用requirejs了。但是我还是有疑问,如何加载jquery或者zepto使全局都可以使用呢?
背景:node建设项目。model下的main.js:
var requirejs = require(‘requirejs’);
requirejs.config({
baseUrl: ‘./public/javascripts’,
nodeRequire: require
});
var zepto = requirejs(‘zepto’)
var transitions = requirejs(‘transitions’);
exports.left = function () {
console.log(‘leftAAAAAAAAA’);
};
ps:zepto.js 和 transitions.js 都是在’./public/javascripts’;
运行后 报错:Error: Evaluating ./public/javascripts/zepto.js as module “zepto” failed with error: ReferenceError: window is not defined

求教,如何在nodejs下用requirejs加载jquery或者zepto呢?

Conan Zhang

一般nodejs项目,不使用requirejs。requirejs主要用于,没有模块加载器的项目,比如纯前端JS项目。

carly32

但是 我现在在用node写一个前端的框架。一般不用但是也是可以用的吧。那请问,node里面jquery或者zepto的正确使用方式是?网上说jqdom,但是它似乎只能用来爬虫。我想要完整的jq或者zepto。

Conan Zhang

NODE里这样就可以加载jquery包了,npm install jquery

pajjket

我有个疑问,requirejs在非nodejs项目中可以用来做js按需加载或者说lazy load。但是这里我们用r.js将所有的js文件都合并成一个build文件了,这样一来除了减少https次数之外,似乎屏蔽了按需加载这个feature啊?请楼主赐教

Conan Zhang

1. 是的,如果合并到一起,就屏蔽了按需加载。
2. 但不一定所有的时候,按需加载都是最好的选择。如果你有100个js小文件(2k以下)需要加载,那么合并成3-4个大文件按需加载,会比分散成100个加载要高效的多。

coder

张老师你好,我在做前台的自动化测试,在karma-的配置文件中frameworks: [‘jasmine’, ‘requirejs’],,并且安装了require模块,之后再运行karma会出现chorm浏览器弹出,但是不跑测试用例的情况,去掉reqirejs就OK了,这是为什么?

Conan Zhang

我的文章中Karma是基于node的npm进行模块管理的,不需要requirejs。

如果在独立的浏览器中进行测试,不使用node环境开发,可以通过requirejs进行模块加载。

21
0
Would love your thoughts, please comment.x
()
x