常见的几种前端模块化
js代码常用的模块化例子:
1.一个功能封装为一个函数:
<pre>function fun1(){ ... };
function fun2(){ ... };</pre>
这种直接调用就可以使用相关功能了,缺点是会污染全局变量,功能之前也没有关联性.
2.对象的写法:
<pre>var myModule ={
var name = 'tom',
var age = 12,
setName:function (name){
this.name = name;
},
getName:function(name){
return this.name
}...
}</pre>
这种写法只需要在需要用到的地方先引用文件,在obj.fun1() 调用就可以.这样避免了变量污染,只要保证模块名唯一即可,同时同一模块内的成员也有了关系看似不错的解决方案,但是也有缺陷,外部可以随意修改内部成员obj.age=22.
3.函数立即调用的写法:
<pre>var myModule = (function(){
var name = 'tom';
var age = 12;
setNmae : function(name){
this.name = name;
}
getNmae : function(name){
return this.name;
}
return{
setNmae:setNmae,
getNmae :getNmae
}
}()); </pre>这样在模块外部无法修改我们没有暴露出来的变量、函数
4.后台模块化之node.js和common.js
node.js使用的模块化规范是common.js,在common.js中,一个文件代表一个功能或模块相应的功能需要暴露出去,在别的文件里才可以引用.例如:
<pre>//模块文件myModule.js
function fun1(){ ... };
function fun2(){ ... };
module.exports={
fun1:fun1,
fun2:fun2
}
//加载模块
var myModule = require('./myModule'); //node中引入模块文件名自带.js,所以可以不用全写.
myModule .fun1();</pre>
5.浏览器端的AMD:
由于require是同步的,而浏览器要引入文件时使用的标签是异步的,所以,在浏览器端是无法使用node的模块方法的.
语法:
requireJS定义了一个函数 define,它是全局变量,用来定义模块
define(id?, dependencies?, factory);
● id:可选参数,用来定义模块的标识,如果没有提供该参数,脚本文件名(去掉拓展名)
● dependencies:是一个当前模块依赖的模块名称数组
● factory:工厂方法,模块初始化要执行的函数或对象。如果为函数,它应该只被执行一次。如果是对象,此对象应该为模块的输出值
在页面上使用require函数加载模块
require([dependencies], function(){});
require()函数接受两个参数
a. 第一个参数是一个数组,表示所依赖的模块
b. 第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块
require()函数在加载依赖的函数的时候是异步加载的,这样浏览器不会失去响应,它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。
AMD是require.js规范的一种方法,例如:
<pre>//定义模块myModule.js
define(['dependency'],function(){
var name = 'tom';
function fun1 (){
...
};
function fun2 (){
...
};
return {
fun1:fun1
}
});
//加载模块
require(['myModule'],function(my){
my.fun1();
})</pre>
6.浏览器端的CMD:
CMD靠Sea.js实现:
语法:
define(id?, deps?, factory)
因为CMD推崇
● 一个文件一个模块,所以经常就用文件名作为模块id
● CMD推崇依赖就近,所以一般不在define的参数中写依赖,在factory中写
factory有三个参数
function(require, exports, module)
require
require 是 factory 函数的第一个参数
require(id)
require 是一个方法,接受 模块标识 作为唯一参数,用来获取其他模块提供的接口
exports
exports 是一个对象,用来向外提供模块接口
module
module 是一个对象,上面存储了与当前模块相关联的一些属性和方法
例如:
<pre>// 定义模块 myModule.js
define(function(require, exports, module) {
var $ = require('jquery.js')
$('div').addClass('active');
function fun1(){ ... }
var fun1 = exports.fun1 ;
});
// 加载模块
seajs.use(['myModule.js'], function(my){
my.fun1();
});</pre>
AMD和CMD的区别:
最明显的区别就是在模块定义时对依赖的处理不同
● AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块
● CMD推崇就近依赖,只有在用到某个模块的时候再去require
同样都是异步加载模块,AMD在加载模块完成后就会执行改模块,所有模块都加载执行完后会进入require的回调函数,执行主逻辑,这样的效果就是依赖模块的执行顺序和书写顺序不一定一致,看网络速度,哪个先下载下来,哪个先执行,但是主逻辑一定在所有依赖加载完成后才执行
CMD加载完某个依赖模块后并不执行,只是下载而已,在所有依赖模块加载完成后进入主逻辑,遇到require语句的时候才执行对应的模块,这样模块的执行顺序和书写顺序是完全一致的
这也是很多人说AMD用户体验好,因为没有延迟,依赖模块提前执行了,CMD性能好,因为只有用户需要的时候才执行的原因.
7.ES6里的模块化:
es6模块和common.js的差异:
● CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
解释:CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
● CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
解释:因为 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
语法:
a. export .... //定义模块
b. import {' dependency '} form './文件名';
例如:
<pre>//定义模块myModule.js
export let conter = 3;
export function inConter(){
return conter++;
}
//加载模块
import {conter,inConter} form './myModule';
console.log(conter); //3
inConter();
console.log(conter); //4</pre>