JavaScript 模块化入门教程

模块化介绍

模块化的概念

模块化的本质是将一个复杂的程序依据一定的规则 (规范) 封装成几个块 (文件),并进行组合在一起,块 (文件) 的内部数据和操作实现是私有的,只是对外部暴露一些接口 (方法) 与外部其它模块通信。

模块化的进化史

一、将不同的功能封装成不同的全局函数(全局函数模式)

  • 缺点:Global 被污染,很容易引起命名冲突

二、简单对象封装(命名模式)

  • 优点:减少 Global 上的变量数目
  • 缺点:本质是对象,一点都不安全

三、匿名函数自调用【闭包】(IIFE 模式)

  • 优点:函数是 JavaScript 唯一的 Local Scope
  • 缺点:如果当前这个模块依赖另一个模块怎么办?

四、引入依赖(IIFE 模式增强),这是现代模块化实现的基石

为什么要模块化

前端开发的现状

前端开发的现状往往是在页面中引入加载大量的 JavaScript 文件(如下图),这会导致页面请求过多、依赖模糊、代码难以维护等问题。值得一提的是,这些问题可以通过现代模块化编码和项目构建来解决。

模块化的需求

  • Web sites are turning into Web apps(网站正转变为网络应用程序)
  • Code complexity grows as the site gets bigger(代码复杂度随着站点变大而变复杂)
  • Assembly gets harder(组装变得更难)
  • Developer wants discrete JS files/modules(开发者想分离 JS 文件 / 模块)
  • Deployment wants optimized code in just one or a few HTTP calls(网站部署者想通过使用一个或者很少 HTTP 请求来优化代码)

模块化的优点

  • 避免命名冲突(减少命名空间污染)
  • 更好的分离,按需加载
  • 更高的复用性
  • 更高的可维护性

模块化规范

JavaScript 的模块化规范分为四大类,分别是 CommonJS、ES 6、AMD、CMD。

CommonJS

概述

每个 JavaScript 文件都可以当一个模块,并具有以下特性:

  • 在服务器端:模块的加载是在运行时同步加载的
  • 在浏览器端:模块需要提前编译打包处理

实现

  • 服务端的实现

  • 浏览器端的实现

    • Browserify,它也称为 CommonJS 的浏览器端的打包工具

Node.js 与 Browserify 的区别

Node.js 是在运行时动态加载模块(同步),而 Browserify 是在转译(编译)时就会加载并打包 (合并) require 的模块。

基础语法

  • 暴露模块

    • module.exports = value
    • exports.xxx = value
  • 引入模块

    • require(xxx)
      • 内置模块: xxx 为模块名
      • 第三方模块: xxx 为模块名
      • 自定义模块: xxx 为模块文件的路径

使用案例

  • 创建项目结构
1
2
3
4
5
6
7
05
├── app.js
├── modules
│ ├── module1.js
│ ├── module2.js
│ └── module3.js
└── package.json
  • Node 安装第三方模块
1
npm install uniq --save
  • 模块化编码 - module1.js
1
2
3
4
5
6
7
8
// module.exports = value 暴露一个对象

module.exports = {
msg : 'module1 msg',
foo() {
console.log('module1 foo()');
}
}
  • 模块化编码 - module2.js
1
2
3
4
5
// module.exports = value 暴露一个函数

module.exports = function () {
console.log('module2()');
}
  • 模块化编码 - module3.js
1
2
3
4
5
6
7
8
9
// exports.xxx = value 暴露多个目标

exports.foo = function () {
console.log('module3 foo()');
}

exports.bar = function () {
console.log('module3 bar()')
}
  • 模块化编码 - app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 引入内置模块
const fs = require('fs');

// 引入第三方模块
const uniq = require('uniq');

// 引入自定义模块
const module1 = require('./modules/module1');
const module2 = require('./modules/module2');
const module3 = require('./modules/module3');

// 使用模块
fs.readFile('app.js', function(error, data) {
console.log(data.toString());
})

let arr = [1, 1, 2, 2, 3, 5];
console.log(uniq(arr));

module1.foo();
module2();
module3.foo();
module3.bar();
  • 通过 Node 运行 app.js
1
node app.js