2019-12-04

# 1. 第8部分. 模块化 

## 1.1. 什么是模块化

模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题进行系统性的分解以之处理。

模块化是一种处理复杂系统分解为代码结构更合理,可维护性更高的可管理的模块的方式。可以想象一个巨大的系统代码,被整合优化分割成逻辑性很强的模块时,对于软件是一种何等意义的存在。

## 1.2. 为什么要模块化? 

无模块时刻面临着:

- 全局变量的灾难

- 函数命名冲突

- 依赖关系不好管理


## 1.3. 模块化解决方案

### 1.3.1. 闭包

每个js文件都是IIFE包裹的,各个js文件分别在不同的词法作用域中,相互隔离,最后通过闭包的方式来暴露变量。每个闭包都是独立的文件,每个文件仍然通过script下载,标签顺序就是模块的依赖关系。

### 1.3.2. 面向对象

在闭包的基础上,所有返回值都是对象,对象其实就是一些方法和属性的集合

优点:

1. 规范化输出,更加统一的便于相互依赖和引用

2. 使用类的方式开发,辩护后面的依赖进行扩展

### 1.3.3. UI

  雅虎出品的一个工具,包含模块化管理、js压缩、混淆、请求合并等性能优化的工具。

  通过YUI全局对象去管理不同模块,所有模块都只是对象上的不同属性,相当于是不同程序运行在操作系统上。核心是闭包。

  ------

### 1.3.4. CommonJS

commonjs作为Node模块化规范

特点:

  - 每个文件都是一个Module实例,原生Module对象

  - 文件内通过require对象引入指定模块

  - 所有文件加载均是同步完成

  - 通过module关键字暴露内容:

  >  exports与module.exports 为了方便Node为每个模块提供一个exports变量,指向module.exports, 等同于var exports = module.exprots。所以exports不能直接指向一个变量。

  - 每个模块加载一次后就会被缓存

  - 模块编译本质上是沙箱编译

  - node的API,只能在服务端上运行 

优点:

  - 强大的查找模块功能,开发十分方便

  - 标准化的输入输出,非常统一

  - 每个文件引入自己的依赖,最终形成文件依赖树

  - 模块缓存机制,提高编译效率

  - 利用node实现文件同步读取

  - 依赖注入变量的 **沙箱** 编译实现模块化

```

var a = require('');

module.exports = {}

```

沙箱编译之后

```

(function(exports, require, module,__filename,__dirname){})();

```

### 1.3.5. AMD规范和RequireJS(Asynchronous Module Definition)异步模块

  AMD规范:采用异步方式加载模块,模块加载不影响它后面的语句进行,所有依赖这个模块的语句,都定义在一个回调函数中,等到所有依赖加载完成后,这个回调函数才会运行。

RequireJs 是模块化工具框架,是AMD规范的具体实现

特点

  - 配置文件:有一个main文件,配置不同模块的路径,以及shim不满足AMD规范的js文件

  - 依赖前置,动态创建script引入依赖,在script标签的onload事件监听文件加载完毕,一个模块的回调函数必须等所有依赖都加载完毕之后,才可以执行,类似Promise.all


加载

```

    require([module], callback)

```

定义

```

    define(id?, dependencies?, factory);

```

```

    // main.js

    requirejs.config({

        shim:{},

        paths:{

            a:'/a.js',

            b:'/b.js',

            c:'/c.js',

            index:'/index.js',

        }

    })

    require(['index'], function(index){

        index();

    })

```

优点

  - 动态并行加载js。依赖前置,无需再考虑js加载顺序问题

  - 核心还是注入变量的沙箱编译,解决模块化问题

  - 规范化输入输出

  - 对于不满足AMD规范的文件可以很好的兼容

### 1.3.6. CMD规范和SaeJs

**特点** 

- define定义模块,require加载模块,exporta暴露变量

- 不同于AMD的依赖前置,CMD按需加载

- 推崇api功能单一,一个模块干一件事

**核心特点**

- 需要配置模块对应的url

- 入口文件执行只有,根据文件内的依赖关系整理出依赖树,然后通过script标签加载依赖

- 依赖加载完毕之后,执行根factory

- 在factory中遇到require,则去执行对应模块的factory,实现就近依赖

- 类似commonJS,对所有模块进行缓存模块url就是id

- 类似commonJs可以使用相对路径加载模块

- exports和return都可以暴露变量

### 1.3.7. ES6 Module

核心思想是尽量静态化,使得编译时就能确定依赖关系,以及输入和输出的变量。

**es6和commonJS的区别**

- ES6中的模块化在CommonJS的基础上,增加了关键字import  from export as default

- commonjs模块输出的是一个值的拷贝,es6模块输出的是值的引用

- commonjs模块是运行时加载,es6模块是编译时输出接口

- comminjs 模块输出的是值的拷贝,一旦输出一个值,模块内部的变化就影响不带这个值

- es6模块运行机制是js引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用,等到脚本真正执行的时候,再根据这个只读引用,到被加载的那个模块里面去取值。

**关键字**

- **import** 命令用于输入其他模块提供的功能;具有提升功能,本质是import命令是编译阶段执行的,在代码运行之前;静态执行,不能有变量,因为变量是运行时执行的;重复多次只加载一次

- **export** 用于规定模块的对外接口,可以输出变量、函数、类;规定的是对外接口,必须与模块内部的变量一一对应,不能只输出一个值;是动态绑定关系,通过该接口可以取到模块内部实时的值。可以出现在顶层任意位置,不能被包含

- **as** 关键字重命名

- **from** 指定路径,可以是相对,也可以是绝对,也可以是模块名。模块名不带路径,必须通过配置

- **default**

```

function foo(){}

export default foo;

```

```

import customName from ''

```

由于使用import命令的时候,需要知道所加载的变量名或函数名,为了给用户提供方便,可以不阅读文档加载模块。利用export default为模块指定默认输出。

利用default输出的接口都变为匿名接口,引入的时候可以直接重命名为任意名称使用;

只能定义一次,import不需要大括号。

本质上就是输出一个叫做default的变量或方法,然后系统允许你添加任意名字;

只是输出一个名为default的变量或方法,后面不能跟变量声明语句,可以跟class

**export 和 import 混合使用**

    export {foo, bar} from 'my_module' 并对外转发提供接口

**import()**

动态加载

执行到这一句的时候,同步加载模块信息

没有建立静态连接关系,类似于node的require方法

import()模块加载成功之后,这个模块会作为一个对象,当作then方法的参数。因此可以使用对象解构赋值的语法,获取输出接口。模块有default输出接口,可以用参数直接获得

import()也可以用在async函数中

场景

- 按需加载

- 条件加载

- 动态模块路径

**引入**

script加入 type = 'module' 

相当于script加上了defer,渲染完成之后加载,按照顺序加载

加上async之后,就不按照顺序执行了,按照加载完成就执行该模块      

**模块转码**                                                                **es6 module transpiler** 转码器,将es6模块转为commonjs模块或者AMD模块

npm install -g es6-module-transpiler

compile-modules convert file.js file1.js

compile-modules convert -o out.js file1.js

**systemjs** 垫片库

在浏览器内加载es6模块、amd模块、commonjs模块,将其转为es5

System.import('./app.js').then()

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容