• requirejs学习笔记


    requirejs

    新接触requirejs, 入门级选手, 把了解东西记录下来, 以备后面查阅
    有误请指正!

    传统的js加载方式是使用多个script标签, 将js文件按依赖顺序依次加载, 这样的加载方式不但会阻塞其它资源的加载, 而且会影响浏览器的渲染.

    requirejs通过声明不同js文件之间的依赖关系, 并采用异步加载回调执行的方式执行js代码, 有效的解决的上述问题.
    并且requirejs是一个模块化的js框架, 鼓励代码的模块化, 鼓励使用脚本时用moduleId替代其URL地址. 一个文件只定义一个模块.

    API

    requirejs常用的三个函数和三个配置项
    三个函数:

    • define: define是一个全局函数, 用于创建一个新的模块.
      此方法可接受3个参数, define(name, deps, callback):
    1. name: 可选参数, 模块名称
    2. deps: 可选参数, 依赖模块数组
    3. call: 必选, 回调函数, 若存在依赖的模块, 则被依赖的模块可以参数(我们称其为模块对象)的形式传入此回调函数, 并且参数的顺序与模块的依赖顺序一致.
    • require: require也是一个全局函数, 用于加载依赖.
      此方法可接受两个参数, require(deps, callback):
    1. 数组, 表示所依赖的模块
    2. 回调函数, 指定的模块加载后, 将调用此函数; 加载的模块会以参数的形式传入此回调函数, 并且参数的顺序与模块的依赖顺序一致.
    • config: 用于配置requirejs

    三个配置项:

    • basrUrl: 加载模块的根路径
    • paths: 映射不在根路径下的模块的路径
    • shim: 对于不符合AMD规范的js模块, 使用此配置可实现requirejs引入

    现在开始上示例

    目录结构及主要代码

    • app目录, 存放各页面功能代码
    • module目录, 存放自定义模块代码
    • lib目录, 存放库文件
    • main.js, 程序入口点, 主要有两个功能, 一是配置所需模板及模板间的依赖关系, 二是加载程序主模块.

    index.html

    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="utf-8" />
    		<title></title>
    		<script data-main="js/main.js" src="js/lib/require/require.js"></script>
    	</head>
    	<body>
    		<div>Hello World</div>
    	</body>
    </html>
    

    main.js

    require.config({
    	baseUrl: "js/",
    	paths: {
    		"jquery": ["lib/jquery/jQuery.v1.11.1.min"]
    	}
    });
    
    require(["jquery"], function(jq){
    	alert(jq().jquery);
    });
    

    浏览器访问index.html
    alert提示jQuery版本号1.11.1, 页面显示Hello World,
    而且js的执行并没有影响页面的渲染.

    示例代码中, 映射关系的key值jquery不能改变, 这是因为在调用define函数创建jquery模块时, 使用了第一个参数, 将此模块命名为jquery, 那么这个模块就只属于jquery, 只能用jquery去引用它, 所以当使用其它名称去引入时, 会提示undefined.

    而, 当定义模块时没有使用第一个参数为其命名, 那么在引用它时就可以使用任意的名称, 使用起来很自由.

    代码解释

    index.html中, 通过script标签引入了requirejs.js库文件
    requirejs通过data-main属性去搜索一个脚本文件(本示例中就是main.js), 此脚本文件中需要为所有脚本文件配置一个根路径, requirejs通过这个根路径去加载相关的模块.

    main.js中,
    首先配置了所有模块的根路径baseUrl,
    然后配置了paths对象, 示例中只映射了jquery的库文件

    需要注意的是:

    1. 库文件不需要再加.js后缀
    2. 如果baseUrl不配置的话, 默认为main.js所在路径, 即其他模块的文件默认与main.js在同一个目录(或其子目录)

    require方法加载了jquery模块, 并将其模块对象作为参数传入到回调函数中,
    最终打印了jquery的版本信息.

    下面使用define函数定义一个无其它依赖的模块, 并调用其功能

    在module目录下新建文件person.js, 内容如下:

    define(function(){
    	return function(){
    		var persons = [{
    			id: 1,
    			name: '张三',
    			age: 23, 
    			sex: '男'
    		}, {
    			id: 2,
    			name: '李四',
    			age: 21, 
    			sex: '女'
    		}, {
    			id: 3,
    			name: '王五',
    			age: 19, 
    			sex: '男'
    		}, {
    			id: 4,
    			name: '刘一',
    			age: 23, 
    			sex: '男'
    		}];
    		
    		var list = function() {
    			// 通常从服务器端获取, 示例就用静态的了
    			return persons;
    		};
    		
    		return {
    			list: list
    		};
    		
    	}();
    });
    

    文件中定义了一个模块, 此模块不依赖任何其他模块, 并反正一个对象, 对象中包含一个list方法.

    main.js文件修改如下:

    require.config({
    	baseUrl: "js/",
    	paths: {
    		"jquery": ["lib/jquery/jQuery.v1.11.1.min"],
    		"person": ["module/person"]
    	}
    });
    
    require(["jquery", "person"], function($, person){
    	var persons = person.list();
    	for(var i=0; i<persons.length; i++) {
    		$('body').append("<div>"+ persons[i].id + ".&nbsp;&nbsp;&nbsp;" + persons[i].name + "</div>");
    	}
    });
    
    1. 添加了person模块的映射
    2. 加载了person模块, 并调用了其list方法, 获取了persons数组, 并将数组内容展示在页面中.
      其中, 回调函数的两个参数($和person)分别为jqueryperson两个模块的模块对象.

    代码运行效果如下:

    通过自定义属性进一步重构代码

    上面的示例中, 主模块main.js直接加载了person模块, 并对其返回数据进行了处理, 这样当页面多时, 并不利于模块化和重用, 代码也会越来越不清晰. 可通过自定义属性进一步重构代码.

    新建person.html页面, 代码如下:

    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="UTF-8">
    		<title></title>
    		<script data-main="js/main.js" src="js/lib/require/require.js"></script>
    	</head>
    	<body my-js="js/app/showperson.js">
    		<div>Hello Person</div>
    	</body>
    </html>
    

    其中, body元素添加了自定义属性my-js, 其值为本页面的功能js文件路径.

    在app路径下新建文件showperson.js, 代码如下:

    require(["jquery", "person"], function($, person){
    	var persons = person.list();
    	for(var i=0; i<persons.length; i++) {
    		$('body').append("<div>"+ persons[i].id + ".&nbsp;&nbsp;&nbsp;<span>" 
    		+ persons[i].name + "</span></div>");
    	}
    });
    

    即将原本写在main.js中的展示代码移动到此文件中.

    修改main.js文件,

    require.config({
    	baseUrl: "js/",
    	paths: {
    		"jquery": ["lib/jquery/jQuery.v1.11.1.min"],
    		"person": ["module/person"]
    	}
    });
    
    require(["jquery"], function($){
    	var jsurl=$('body').attr('my-js');
        require([jsurl], function () {
    		
    	});
    });
    

    在require方法的回调函数中, 访问了body元素的my-js属性的值, 加载该值指向的模块, 即showperson.js, 模块加载完成后会立即执行其中的代码.

    这样就可以复用main.js, 将不同模块的功能从主模块中分离出去, 并且利于扩展.
    代码运行效果如下:

    使用define函数定义一个依赖其它模块的模块

    上面的示例中, 使用define函数定义了一个无依赖的模块, 现定义一个新模块使其依赖上面定义的模块.

    在module目录下新建文件account.js, 内容如下:

    define(["person"], function(person){
    	var persons = person.list();
    	
    	var accounts = [];
    	// 就是根据persons数组, 返回一个accounts数组
    	for(var i=0; i<persons.length; i++) {
    		accounts.push({
    			personId: persons[i].id,
    			psersonName: persons[i].name,
    			accountNo: (Math.random() * 1000000).toFixed()
    		});
    	}
    	
    	var list = function() {
    		return accounts;
    	}
    	
    	return  {
    		list: list
    	}
    });
    

    新定义的模块依赖了person模块, 并通过回调参数调用了person模块中的方法.

    app目录下新建文件showaccount.js, 内容如下:

    require(["jquery", "account"], function($, account){
    	var accounts = account.list();
    	for(var i=0; i<accounts.length; i++) {
    		$('body').append("<div>"+ accounts[i].personId + ".&nbsp;&nbsp;&nbsp;" 
    		+ accounts[i].psersonName + "&nbsp;&nbsp;&nbsp;" + accounts[i].accountNo +"</div>");
    	}
    });
    

    新建account.html,

    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="UTF-8">
    		<title></title>
    		<script data-main="js/main.js" src="js/lib/require/require.js"></script>
    	</head>
    	<body my-js="js/app/showaccount.js">
    		<div>Hello Account</div>
    	</body>
    </html>
    

    运行效果如下图:

    引入非AMD规范的模块

    module目录下新建文件noamd.js
    一般地, 我们编写js工具类或js框架有以下几种形式:

    // 第一种:
    var noamd = (function(){
    	
    	var hi = function(name) {
    		var str = '大家好, 我是' + name;
    		alert(str);
    	}
    	var say = function() {
    		alert('Hello');
    	}
    	
    	return {
    		hi: hi,
    		say: say
    	}
    	
    })();
    // 第二种:
    var noamd = {};
    noamd.hi= function(name) {
    	var str = '大家好, 我是' + name;
    	alert(str);
    }
    noamd.say = function() {
    	alert('Hello');
    }
    // 第三种:
    (function(){
    	
    	var hi= function(name) {
    		var str = '大家好, 我是' + name;
    		alert(str);
    	}
    	var say = function() {
    		alert('Hello');
    	}
    	
    	window.noamd = {
    		hi: hi,
    		say: say
    	}
    	
    })(window);
    

    main.js中, config方法的调用修改为:

    require.config({
    	baseUrl: "js/",
    	paths: {
    		"jquery": ["lib/jquery/jQuery.v1.11.1.min"],
    		"person": ["module/person"],
    		"account": ["module/account"],
    		"mynoamd": ["module/noamd"]
    	},
    	shim: {
    		"mynoamd": {
    			deps: [],
    			exports: "noamd"
    		}
    	}
    });
    
    1. paths中添加了noamd模块的映射
    2. 配置shim属性,
    • mynoamd为paths映射的key
    • deps, 数组, 表示此模块要依赖的其他模块
    • exports的值, 即noamd, 必须与module/noamd.js文件中暴露出去的全局变量名称一致

    修改了showperson.js文件

    require(["jquery", "person", "mynoamd"], function($, person, _){
    	var persons = person.list();
    	for(var i=0; i<persons.length; i++) {
    		$('body').append("<div>"+ persons[i].id 
    		+ ".&nbsp;&nbsp;&nbsp;<span>" + persons[i].name + "</span></div>");
    	}
    	
    	$('span').click(function(){
    		_.hi($(this).text());
    	})
    });
    

    文件加载了新定义的非AMD模块mynoamd, 并将_作为其模块对象传入回调函数中.
    回调函数中, 为每个span注册了click事件, 并调用_对象的hi方法.
    代码运行效果就是, 点击姓名时alert提示: 大家好, 我是xxx

    暴露多个全局变量的非AMD模块

    如果一个文件模块暴露了多个全局变量, 那么就不能使用shim了, 得使用init函数, 例如有如下文件(multi.js):

    function xixi() {
    	alert("xixi...");
    }
    
    function haha() {
    	alert("haha...");
    }
    

    有两个全局变量, 而且都需要, 那么main.js写法如下:

    require.config({
    	baseUrl: "js/",
    	paths: {
    		"jquery": ["lib/jquery/jQuery.v1.11.1.min"],
    		"person": ["module/person"],
    		"account": ["module/account"],
    		"mynoamd": ["module/noamd"],
    		"mymulti": ["module/multi"]
    	},
    	shim: {
    		"mynoamd": {
    			deps: [],
    			exports: "noamd"
    		},
    		"mymulti": {
    			deps: [],
    			init: function() {
    				return {
    					myxixi: xixi,
    					myhaha: haha
    				}
    			}
    		}
    	}
    });
    

    非AMD模块mymulti通过init函数, 对外暴露一个接口对象, 而将原来的两个全局函数映射为接口对象的两个局部函数, 这样就可以与符合AMD规范的模块一样使用了.
    当exports和init同时存在的时, exports将被忽略.

    示例源码:
    https://github.com/lizujia/requirejs_demo

  • 相关阅读:
    UILabel 详解
    didMoveToSuperView 引发的思考
    Source
    设计模式
    Code ReView
    UIApearance
    UINavigationBar
    initWithNibName与viewDidLoad的执行关系以及顺序
    bLock 回调 就是这么简单!
    程序语言小记
  • 原文地址:https://www.cnblogs.com/lzj0616/p/6208211.html
Copyright © 2020-2023  润新知