欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

模块化开发

程序员文章站 2022-06-30 22:52:34
...

模块化开发

1.模块化开发规范

模块化是一个语言膨胀的必经之路,它能够帮助开发者拆分和组织代码,在模块化规范形成之前,JS开发者使用Module设计模式来解决JS全局作用域的污染问题。Module模式最初被定义为一种在传统软件工程中为类提供私有和公有封装的方法。在JavaScript中,Module模式使用匿名函数自调用 (闭包)来封装,通过自定义暴露行为来区分私有成员和公有成员。
The Module Pattern,模块模式,也译为模组模式,是一种通用的对代码进行模块化组织与定义的方式。这里所说的模块(Modules),是指实现某特定功能的一组方法和代码。许多现代语言都定义了代码的模块化组织方式,比如 Golang 和 Java,它们都使用 package 与 import 来管理与使用模块,而目前版本的 JavaScript 并未提供一种原生的、语言级别的模块化组织模式,而是将模块化的方法交由开发者来实现。因此,出现了很多种 JavaScript 模块化的实现方式,比如,CommonJS Modules、AMD 等。

2.为什么要模块化

在模块化没有出现之前,我们JavaScript脚本大概是这样的:

    <script src="module1.js"></script>
    <script src="module2.js"></script>
    <script src="module3.js"></script>
    <script src="module4.js"></script>
    ....

但我们引入多个js文件时,会存在一些问题:

  • 把所有的代码放到全局环境会引起冲突
  • 各个脚本必须按照严格的依赖顺序放置
  • 在大型的项目中,可能会引入很多的js脚本,script就会变得很多,并且难以维护。

为了解决这些问题,涌现了各种模块化的方案。

3.命名规范私有属性

JavaScript对象的所有属性公有的,且没有显示的方法指定某个属性不能被外界某个对象访问。但是有时候我们希望对象中某些属性是私有的。为了解决这个问题,我们制定了一个命名规范,如果不希望某些属性被外界访问,你可以在属性名之前加下划线(_)。

//通过命名前面加上 _ 下划线的方式 来私有化
var pereson = {
    uname: 'zhangsan',
    _age: 40
}

4.自定义模块化方案

这种方式是创建对象的一种方法,用于创建具有私有属性的对象。基本思路是使用一个立即执行的函数表达式,返回一个对象。该函数表达式可以包含任意数量的局部变量,这些变量从函数的外部是无法访问到的。因为返回的对象是在自执行函数内部声明的,所以对象中定义的方法可以访问自执行函数内的局部变量,这些方法被具有特权的方法。

var person = (function() {
    //私有属性
    var age = 18;
    function getAge() {
        return age;
    }
    function growAge() {
        return age++;
    }
    return {
        name: 'zhangsan',
        getStuAge: getAge,
        growStuAge: growAge
    }
})();
console.log(person.name); //zhangsan
console.log(person.getStuAge()); //18
console.log(person.growStuAge()); //18

5.构造函数的私有成员

模块模式在定义单个对象的私有属性上十分有效,但对于那些同样需要私有属性的自定义类型又如何呢?我们可以在构造函数种使用类似的模式来创建每个实例的私有属性。

 // 构造函数私有化属性
var Person = (function() {
    var age = 18;

    function Person1(name) {
        this.name = name;
    }
    Person1.prototype.getAge = function() {
        return age;
    };
    return Person1;
})();
var p1 = new Person('zhangsan');
console.log(p1); //Person1 {name: "zhangsan"}
console.log(p1.getAge()); //18
HTML 5 <script > 属性
1.async 定义和用法
async 属性规定一旦脚本可用,则会异步执行。
注释:async 属性仅适用于外部脚本(只有在使用 src 属性时)。
注释:有多种执行外部脚本的方法:
- 如果 async="async":脚本相对于页面的其余部分异步地执行(当页面继续进行解析时,脚本将被执行)
- 如果不使用 async 且 defer="defer":脚本将在页面完成解析时执行
- 如果既不使用 async 也不使用 defer:在浏览器继续解析页面之前,立即读取并执行脚本

2.defer 定义和用法
defer 属性规定是否对脚本执行进行延迟,直到页面加载为止。
有的 javascript 脚本 document.write 方法来创建当前的文档内容,其他脚本就不一定是了。
如果您的脚本不会改变文档的内容,可将 defer 属性加入到 <script> 标签中,以便加快处理文档的速度。因为浏览器知道它将能够安全地读取文档的剩余部分而不用执行脚本,它将推迟对脚本的解释,直到文档已经显示给用户为止。

模块化开发

6.ES6模块化规范

导出内容

export function bubble(arr) {
    // 1.排序从小到大
    // 2.进行冒泡排序
    // 控制的是比较的趟数
    for (var i = 0; i < arr.length - 1; i++) {
        // 控制的是每趟比较的次数
        for (var j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
            }
        }
        console.log('第' + (i + 1) + '次' + '结果' + arr);
    }
    return arr;
}
function noRepeat(arr1) {
    for (var i = 0; i < arr1.length; i++) {
        for (var j = i + 1; j < arr1.length; j++) {
            if (arr1[i] == arr1[j]) {
                arr1.splice(j, 1);
                j--;
            }
        }
    }
    return arr1;
}

导入内容

import { bubble } from './es6/01-module1.js'; 
        var arr = [1,7, 4,5,12,10]; 
        var relArr = bubble(arr); 
        console.log(relArr);

一次导出多个

//var obj= {
//     myNoRepeat:noRepeat,
//     myBubble:bubble
// };

// export {obj as exObj};

//默认导出方式
export default{
    myNoRepeat:noRepeat,
    myBubble:bubble
};

一次导入多个

  <script type="module">
	import {exObj} from './es6/01-module1-es6.js';
	console.log(exObj);
	var arr = [1,3,4,4,5,5,6,1];
	var relArr = exObj.myNoRepeat(arr);
	var bubArr = exObj.myNoRepeat(arr);
	console.log(relArr);
	console.log(bubArr);
</script>

import mod1 from './es6/01-module1.js';//mod1 是自己命名的

8. 其他模块化方案

AMD规范

AMD即Asynchronous Module Definition,异步模块定义。它是在浏览器端实现模块化开发的规范。由于该规范不是JavaScript原始支持的,使用AMD规范进行开发的时候需要引入第三方的库函数,也就是鼎鼎大名的RequireJS

RequireJS主要解决两个问题:

  1. 多个js文件可能有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器。

  2. js加载的时候浏览器会停止页面渲染,加载的文件越多,页面失去响应的时间越长。

  3. 定义模块

RequireJS提供了一个define函数,用来定模块。

define(id, [dependencies], factory);

  • id:可选参数,用来定义模块的标识,如果没有提供参数的话,默认为文件的名字。
  • dependencies:当前模块依赖的其他模块,数组。
  • factory:工厂方法,初始化模块需要执行的函数或对象。如果为函数,它只被执行一次。如果是对象,此对象会作为模块的输出值。
  1. 在页面中加载模块
    加载模块需要使用require()函数。
    require([dependencies], function(){});
  • dependencies:该参数是一个数组,表示所依赖的模块。
  • function:这是一个回调函数,当所依赖的模块都加载成功之后,将调用该回调方法。依赖的模块会以参数的形式传入该函数,从而在回调函数内部就可以使用这些模块。

require()函数在加载依赖的模块时,是异步加载的,这样浏览器就不会失去响应,回调函数只有在所依赖的全部模块都被被成功加载之后才执行,因此,解决了依赖性的问题。

CMD规范

CMD即Common Module Definition通用模块定义。它解决的问题和AMD规范是一样的,只不过在模块定义方式和模块加载时机上不同。CMD也需要额外的引入第三方的库文件,SeaJS。
因为CMD推崇一个文件一个模块,所以经常使用文件名作为模块的ID;CMD推崇就近原则,所以一般不再define的参数中写依赖,在factory函数中写。

  • require:我们定义的模块可能会依赖其他模块,这个时候就可使用require()引入依赖。
  • exports:等价于module.exports,只是为了方便我们使用
  • module.exports:用于存放模块需要暴露的属性

AMD和CMD规范的区别

AMD在加载模块完成后会立即执行该模块,所有的模块都加载完成后执行require方法中的回调函数,执行主逻辑,这样的效果就是依赖模块的执行顺序和书写顺序不一定一致,看网速,谁先下载下来,谁先执行,但是我们的主逻辑一定是在所有的依赖模块都被加载完成后才执行。
CMD加载完某个模块的时候并不执行,只是把它们下载下来而已,在所有的模块下载完成后,当使用require请求某个模块的时候,才执行对应的模块。

相关标签: js