其他分享
首页 > 其他分享> > 模块化开发

模块化开发

作者:互联网

模块化开发

一、JavaScript模块化的必要性

​ 随着网站逐渐变成“互联网应用程序(WebApp)”,嵌入网页的JavaScript代码越来越庞大,越来越复杂。

​ 网页越来越像桌面程序,需要一个团队分工协作、进度管理、单元测试等等…开发者不得不使用软件工程的方法,管理网页的业务逻辑。

​ Javascript模块化编程,已经成为一个迫切的需求。理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。

​ 但是,Javascript不是一种模块化编程语言,它不支持""(class),更遑论"模块"(module)了。(正在制定中的ECMAScript标准第六版,将正式支持"类"和"模块",但还需要很长时间才能投入实用。)

​ Javascript社区做了很多努力,在现有的运行环境中,实现"模块"的效果。

二、Javascript模块化的写法

1.原始写法

模块就是实现特定功能的一组方法

只要吧不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块。

比如:tool.js

	function m1(){
        //...
    }
    function m2(){
        //...
    }

上面的函数m1()和m2(),组成一个模块。使用的时候,直接调用就行了。

这种做法的缺点很明显:"污染"了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接关系。

2.对象写法

为了解决上面的缺点,可以把模块写成一个对象,所有的模块成员都放到这个对象里面。

		var module1 = new Object({
            _count:0,
            m1:function(){
                //...
            },
            m2:function(){
                //...
            }
        });
        //上面的函数m1()和m2(),都封装在module1对象里。使用的时候,就是调用这个对象的属性。
        module1.m1();
        //【注】但是这样的写法会暴露所有模块成员,内部状态可以被外部改写。比如,外部代码可以直接改变内部计数器的值。

3.立即执行函数写法(闭包)

使用"立即执行函数"(Immediately-Invoked Function Expression,IIFE),可以达到不暴露私有成员的目的。

		var module1 = (function(){
            var _count = 0;
            var m1 = function(){
                //...
            };

            var m2 = function(){
                //...
            };
            return {
                m1:m1,
                m2:m2
            }
        })();
        //使用上面写法,外部代码无法读取内部_count变量
        console.log(module1._count);

4.放大模式

如果一个模块很大,必须分成几个部分,或者一个模块需要继承另一个模块,这时就有必要采用"放大模式"(augmentation)。

//接上面代码
		var module1 = (function(mod){
            mod.m3 = function(){
                //...
            }
            return mod;
        })(module1);
        //上述代码在module1模块添加了一个新方法m3(),然后返回新的moudule1模块。

5.宽放大模式

​ 在浏览器环境中,模块的各个部分通常都是从网上获取的,有时无法知道哪个部分会先加载。如果采用上一节的写法,第一个执行的部分有可能加载一个不存在空对象,这时就要采用"宽放大模式"。

		var module1 = (function(mod){
            var _count = 0;
            var m1 = function(){
                //...
            };

            var m2 = function(){
                //...
            };

            mod.out1 = m1;
            mod.out2 = m2; 

            return mod;
        })(module1 || {});

        var module1 = (function(mod){
            mod.m3 = function(){
                //...
            }
            return mod;
        })(module1 || {});

三、模块规范

目前,通行的Javascript模块规范共有两种:CommonJSAMD(CMD)。

2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程。

这标志"Javascript模块化编程"正式诞生。因为老实说,在浏览器环境下,没有模块也不是特别大的问题,毕竟网页程序的复杂性有限;但是在服务器端,一定要有模块,与操作系统和其他应用程序互动,否则根本没法编程。

node.js的模块系统,就是参照CommonJS规范实现的。在CommonJS中,有一个全局性方法require(),用于加载模块。假定有一个数学模块math.js,就可以像下面这样加载。

因为这个系列主要针对浏览器编程,不涉及node.js,所以对CommonJS就不多做介绍了。我们在这里只要知道,require()用于加载模块就行了。

CommonJS规范(服务器)编写代码

声明:

module.exports = {
	outA:showA,
	outB:showB
}

引入:(同步执行)

	var moduleA = require('moduleA.js');
	moduleA.outA();
	moduleA.outB();

AMD规范:(客户端/浏览器)

声明:

define(function(){
	//代码
	return{
		outA:showoA,
		outB:showB
	}
})

引入:(异步执行)

require("moduleA.js",function(moduleA){
	//这里的代码。模块引入之后执行。
	moduleA.putA();
	moduleA.outB();
})

alert("Hello World");

EMAC6(模块化规范)

声明:

export = {
	outA:showA,
	outB:showB
}

引入

import moduleA from "moduleA.js"
moduleA.outA()
moduleA.outB();

require.js用法

require.js的诞生就是为了解决这两个问题:

  1. 实现js文件的异步加载,避免网页失去响应
  2. 管理模块之间的依赖性,便于代码的编写和维护

require.js的加载

使用require.js的第一步就是去官方下载最新版本的require.js。下载后把它放在js的子目录下然后进行加载

<script src="js/require.js" async = 'true' defer data-main="js/main"></script>

==注:==async属性是表明这个文件是否需要异步加载,避免网页失去响应。IE不支持这个属性,支持defer,所以吧defer也写上

​ 加载完require.js以后,下一步就要加载我们自己的代码。假定我们自己的代码文件是main.js,也放在js目录下面。那么,只要像上面那么写就行了。

​ data-main属性的作用是,指定网页程序的主模块。在上例中,就是js目录下面的main.js,这个文件会第一个被require.js加载。由于require.js默认的文件后缀名是js,所以可以吧main.js简写成main。

主模块的写法

​ 上一节的main.js,我把它称为"主模块",意思是整个网页的入口代码。它有点像C语言的main()函数,所有代码都从这儿开始运行。

​ 下面就来看,怎么写main.js。

​ 如果我们的代码不依赖任何其他模块,那么可以直接写入javascript代码。

//main.js
alert("加载成功");

​ 但这样的话,就没必要使用require.js了。真正常见的情况是,主模块依赖于其他模块,这时就要使用AMD规范定义的的require()函数。

//main.js
require(["moduleA","moduleB","moduleC"],function(moduleA,moduleB,moduleC){
	//...
})

​ require()函数接受两个参数。第一个参数是一个数组,表示所依赖的模块,上例就是[‘moduleA’, ‘moduleB’, ‘moduleC’],即主模块依赖这三个模块;第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块。

​ require()异步加载moduleA,moduleB和moduleC,浏览器不会失去响应;它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。

模块化加载

require.config()就写在主模块(main.js)的头部。参数就是一个对象,这个对象的paths属性指定各个模块的加载路径。

require.config({
	paths:{
		aaa:"aaa'path"
	}
})

上面的代码给出了三个模块的文件名,路径默认与main.js在同一个目录(js子目录)。如果这些模块在其他目录,比如js/lib目录,则有两种写法。一种是逐一指定路径。

//可以用 baseUrl: + 路径 改变基目录
//如果某个模块在另一台主机上,也可以直接指定它的网址

AMD模块的写法

require.js加载的模块,采用AMD规范。也就是说,模块必须按照AMD的规定来写。具体来说,就是模块必须采用特定的define()函数来定义。如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。假定现在有一个add.js文件,它定义了一个add模块。那么,add.js就要这样写:

define(function(){
    function add(x,y){
        return x+y;
    }
    function show(){
        console.log("hello world");
    }
    function ccc(){
        console.log("ccc");
    }

    //对外暴露
    return {
        add:add,
        show:show,
        ccc:ccc
    }
});
require.config({
    paths:{
        add:"demo/add",
        mul:"demo/mul"
    }
})


require(["add","mul"],function(addObj,mulObj){
    var res = addObj.add(10 , 20);
    alert(res);
    addObj.show();

    alert(mulObj.mul(10,10));
})

如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。

案例分析

  1. 首先在index下导入require.js

  2. 配置main.js

    require.config({
        paths: {
            "jquery": "jquery-1.11.3",
            "jquery-cookie": "jquery.cookie",
            'parabola': "parabola",
            "index": "index",
            "banner": "banner"
        },
        shim: {
            //设置依赖关系  先引入jquery.js  然后在隐去jquery-cookie
            "jquery-cookie": ["jquery"],
            //声明当前模块不遵从AMD
            "parabola": {
    			exports: "_"
    		}
        }
    })
    
    
    require(['index', "banner"], function(index, banner){
        index.index();
        banner.banner();
    })
    
  3. 将js代码修改成遵守AMD规范(写在define里面,对外暴露)

标签:function,模块化,require,js,开发,模块,moduleA,加载
来源: https://blog.csdn.net/qq_45902611/article/details/116334186