RequireJS
源码地址:http://www.wcscj.xyz:3000/wcs/requirejs-tutorial
加载JavaScript文件
在一些大的项目里面通常会有很多个js文件,通常使用<script>标签逐个加载。另外一切js的文件可能会依赖于其他的文件,这里我们需要保证被依赖的文件先于当前的文件加载。比如说jquery,很多的库都依赖核心的jquery模块。我们通过模拟真实的环境来讲解requirejs的加载。
purchase.js
function purchaseProduct() {
console.log('Function :purchaseProduct')
var credis = getCredits();
if(credis>0){
reserveProduct();
return true;
}
return false;
}
products.js
function purchaseProduct() {
console.log('Function :purchaseProduct')
var credis = getCredits();
if(credis>0){
reserveProduct();
return true;
}
return false;
}
credits.js
function getCredits(){
console.log('Function : getCredits');
var credits = "100";
return credits
}
在这个例子里面,我们试图购买一个产品。首先检查是否有足够的信用可用于购买产品。然后,在确认信用之后保留产品。
现在我们通过main.js通过调用 purchaseProduct()来初始化代码。如下所示
**main.js **
var result=purchaseProduct();
为什么出错
在上面的三个文件中,我们可以发现purchase.js依赖credits.js和products.js模块。所以credits.js和products.js必须先于调用purchaseProduct()前引入。但是我们故意调整错误引入次序,会发现什么事?
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="products.js"></script>
<script src="purchase.js"></script>
<script src="main.js"></script>
<script src="credits.js"></script>
</head>
<body>
</body>
</html>
通过运行上面的html,你会发现浏览器并不会如预期输出信息,而是打印了一条错误信息。
purchase.js:3 Uncaught ReferenceError: getCredits is not defined at purchaseProduct (purchase.js:3) at main.js:1
这是因为credits.js是在main.js之后,在我们调用 purchaseProduct()的函数时候并没有加载credits.js文件,浏览器找不到对应的函数的声明。
上面仅仅是3个js的文件,而一个大型的项目当中可能存在着几十个js的文件,你要时刻去关注每个js文件的依赖,是不是想想就觉得头痛。
requireJS简介
按照官方的解释,RequireJS是一个javaScript文件和模块加载器。不仅能够优化浏览器环境。同时也能用于Rhino和Node.RequireJS不仅能够异步进行模块的加载,同时可以定义模块间的依赖关系,通过配置能够优化程序的运行速度以及质量。
在我们进行项目的讲解之前,您需要去github或者官网上面下载最新的requirejs文件并且放置在项目的scripts目录下面。
假设我们的项目结构如下图所示
所有的javascript文件(包括RequireJS文件)都位于脚本文件夹内。main.js文件用于初始化,其他文件包含了应用程序的业务逻辑。我们看看script的文件如何应用到html文件当中。
<script data-main="scripts/main" src="scripts/require.js"></script>
上面的代码段是我们使用requireJS文件唯一需要加入的代码。上面的代码块中data-main忏悔定义应用程序的入口点。在上面的案例中我们使用main.js文件。RequireJS会利用main.js文件自动查找其他脚本以及依赖。
现在我们来看看加入requirejs后的main.js文件的代码
// var result=purchaseProduct();
require(['purchase'],function(purchase){
purchase.purchaseProduct();
})
在RequireJS,所有的代码都被封装在require()和define()函数。上面的require函数的第一个参数指定了依赖。在我们的示例中我们需要调用 purchaseProduct函数,这个函数定义在purchase.js,所以我们需要进行加载。
第二个参数是一个匿名函数,上面的代码我们仅加载了purchase模块,函数函数中的purchase对应的就是purchase文件中暴露的文件。同样的requireJS支持多个依赖的加载如:
require(['a','b','c'],function(a,b,c))
通过RequireJS创建程序
上面的代码我们我们已经介绍了main.js如何转为RequireJS使用。现在我们继续讨论其他文件的定义。
purchase.js
define(["credits", "products"], function(credits, products) {
console.log("Function : purchaseProduct");
return {
purchaseProduct: function() {
var credit = credits.getCredits();
if (credit > 0) {
products.reserveProduct();
return true;
}
return false;
}
};
});
purchase需要调用getCredits()和reserveProduct(),所以必须加载credits.js和products.js文件
products.js
define(function(products) {
return {
reserveProduct: function() {
console.log("function : reserveProduct");
return true;
}
};
});
credits.js
define(function() {
return {
getCredits: function() {
console.log("Function : getCredits");
var credits = "100";
return credits;
}
};
});
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script data-main="scripts/main" src="scripts/require.js"></script>
</head>
<body>
</body>
</html>
上面的两个文件被配置成独立的模块,意味着不依赖于其他任何的东西。同样的我们发现我们用的是define()而不是require()。有关require()和define()之间的选择,我们在下节中来讨论。
require() VS define()
之前我们提到可以同时使用require()和define()来加载依赖关系。理解这两个函数之间的差异对于管理依赖关系至关重要。require()用于运行即时功能,而define()用于定义多个位置的模块。在main.js文件中我们需要立即调用**purchaseProduct()**函数,所以我们使用require,而其他文件是可征用的模块,仅被其他文件调用,所以我们使用define()
管理相关文件的顺序
RequireJS使用异步模块加载(AMD)来加载文件。每个依赖模块通过给定的顺序异步请求开始加载。尽管考虑了文件的顺序,由于 异步的特点,我们无法保证第一个文件会在第二个文件之前被加载。因为RequireJS需要通过shim配置来定义按正确的顺序加载文件。我们来看看RequireJS中如何创建配置选项。
requirejs.config({
shim: {
source1: ["dependency1", "dependency2"],
source2: ["source1"],
backbone: {
//backbone加载前需要加载的模块
deps: ["underscore", "jquery"],
exports: "Backbone"//暴露全局的名称
},
'jquery.scroll': {
deps: ['jquery'],
exports: 'jQuery.fn.scroll'//将scroll挂在jQuery的原型上
},
},
});
RequireJS允许我们使用config()函数提供配置选项。按接收一个shim的参数,我们来使用它来定义强制的依赖关系序列。
deps:表示当前模块需要依赖哪些库
exports:暴露的全局名称
项目真实的配置
require.config({
urlArgs: "v=" + requirejs.s.contexts._.config.config.site.version,
packages: [{
name: 'moment',
location: '../libs/moment',
main: 'moment'
}
],
//在打包压缩时将会把include中的模块合并到主文件中
include: ['css', 'layer', 'toastr', 'fast', 'backend', 'backend-init', 'table', 'form', 'dragsort', 'drag', 'drop', 'addtabs', 'selectpage'],
paths: {
'lang': "empty:",
'form': 'require-form',
'table': 'require-table',
'upload': 'require-upload',
'validator': 'require-validator',
'drag': 'jquery.drag.min',
'drop': 'jquery.drop.min',
'echarts': 'echarts.min',
'echarts-theme': 'echarts-theme',
'adminlte': 'adminlte',
'bootstrap-table-commonsearch': 'bootstrap-table-commonsearch',
'bootstrap-table-template': 'bootstrap-table-template',
//
// 以下的包从bower的libs目录加载
'jquery': '../libs/jquery/dist/jquery.min',
'bootstrap': '../libs/bootstrap/dist/js/bootstrap.min',
'bootstrap-datetimepicker': '../libs/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min',
'bootstrap-daterangepicker': '../libs/bootstrap-daterangepicker/daterangepicker',
'bootstrap-select': '../libs/bootstrap-select/dist/js/bootstrap-select.min',
'bootstrap-select-lang': '../libs/bootstrap-select/dist/js/i18n/defaults-zh_CN',
'bootstrap-table': '../libs/bootstrap-table/dist/bootstrap-table.min',
'bootstrap-table-export': '../libs/bootstrap-table/dist/extensions/export/bootstrap-table-export.min',
'bootstrap-table-mobile': '../libs/bootstrap-table/dist/extensions/mobile/bootstrap-table-mobile',
'bootstrap-table-lang': '../libs/bootstrap-table/dist/locale/bootstrap-table-zh-CN',
'tableexport': '../libs/tableExport.jquery.plugin/tableExport.min',
'dragsort': '../libs/fastadmin-dragsort/jquery.dragsort',
'sortable': '../libs/Sortable/Sortable.min',
'addtabs': '../libs/fastadmin-addtabs/jquery.addtabs',
'slimscroll': '../libs/jquery-slimscroll/jquery.slimscroll',
'validator-core': '../libs/nice-validator/dist/jquery.validator',
'validator-lang': '../libs/nice-validator/dist/local/zh-CN',
'plupload': '../libs/plupload/js/plupload.min',
'toastr': '../libs/toastr/toastr',
'jstree': '../libs/jstree/dist/jstree.min',
'layer': '../libs/layer/dist/layer',
'cookie': '../libs/jquery.cookie/jquery.cookie',
'cxselect': '../libs/fastadmin-cxselect/js/jquery.cxselect',
'template': '../libs/art-template/dist/template-native',
'selectpage': '../libs/fastadmin-selectpage/selectpage',
'citypicker': '../libs/city-picker/dist/js/city-picker.min',
'citypicker-data': '../libs/city-picker/dist/js/city-picker.data',
},
// shim依赖配置
shim: {
'addons': ['backend'],
'bootstrap': ['jquery'],
'bootstrap-table': {
deps: [
'bootstrap',
// 'css!../libs/bootstrap-table/dist/bootstrap-table.min.css'
],
exports: '$.fn.bootstrapTable'
},
'bootstrap-table-lang': {
deps: ['bootstrap-table'],
exports: '$.fn.bootstrapTable.defaults'
},
'bootstrap-table-export': {
deps: ['bootstrap-table', 'tableexport'],
exports: '$.fn.bootstrapTable.defaults'
},
'bootstrap-table-mobile': {
deps: ['bootstrap-table'],
exports: '$.fn.bootstrapTable.defaults'
},
'bootstrap-table-advancedsearch': {
deps: ['bootstrap-table'],
exports: '$.fn.bootstrapTable.defaults'
},
'bootstrap-table-commonsearch': {
deps: ['bootstrap-table'],
exports: '$.fn.bootstrapTable.defaults'
},
'bootstrap-table-template': {
deps: ['bootstrap-table', 'template'],
exports: '$.fn.bootstrapTable.defaults'
},
'tableexport': {
deps: ['jquery'],
exports: '$.fn.extend'
},
'slimscroll': {
deps: ['jquery'],
exports: '$.fn.extend'
},
'adminlte': {
deps: ['bootstrap', 'slimscroll'],
exports: '$.AdminLTE'
},
'bootstrap-datetimepicker': [
'moment/locale/zh-cn',
// 'css!../libs/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css',
],
'bootstrap-select': ['css!../libs/bootstrap-select/dist/css/bootstrap-select.min.css', ],
'bootstrap-select-lang': ['bootstrap-select'],
// 'toastr': ['css!../libs/toastr/toastr.min.css'],
'jstree': ['css!../libs/jstree/dist/themes/default/style.css', ],
'plupload': {
deps: ['../libs/plupload/js/moxie.min'],
exports: "plupload"
},
// 'layer': ['css!../libs/layer/dist/theme/default/layer.css'],
// 'validator-core': ['css!../libs/nice-validator/dist/jquery.validator.css'],
'validator-lang': ['validator-core'],
// 'selectpage': ['css!../libs/fastadmin-selectpage/selectpage.css'],
'citypicker': ['citypicker-data', 'css!../libs/city-picker/dist/css/city-picker.css']
},
baseUrl: requirejs.s.contexts._.config.config.site.cdnurl + '/assets/js/', //资源基础路径
map: {
'*': {
'css': '../libs/require-css/css.min'
}
},
waitSeconds: 30,
charset: 'utf-8' // 文件编码
});