为什么会有模块
我们最初只会有index.js的文件,后来随着业务的发展,这个代码发展到了1000多行,就很难读懂并且很难维护了了,因此我们就想到了分块,就是把相同业务逻辑的代码放在一起,这个就是模块.通常是会把一个文件看作一个模块的,每个模块文件都有其特定的功能,便于复用。通过使用模块,使我们能够在开发环境上更好地开发和维护我们的项目。我在写游戏项目的时候我把app.js写成element,js,enemy.js等,这个就是模块化思想.
我们把1000多行代码按功能分了不同的代码块,就是分别引用在了index.html里,例如如下:
<script src="common.js"></script>
<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>
这样实现模块化存在两大问题
- 命名冲突(不同的文件里可能会出现相同的变量,不能实现私有化)
- 管理模块依赖艰难(得分析模块之间的依赖,文件数多,就会很麻烦)
因此模块化规范诞生了,规范化的模块会使我们统一方法定义模块,不需要手动维护依赖。
模块化规范
目前我们有3类模块化规范
- CommonJs
- AMD异步模块定义
- CMD通用模块定义
CommonJS规范
- 文件即模块,一个文件即模块,每个文件拥有自己的作用域
- 使用 module.exports属性(或简写exports)来暴露对外的接口和属性
- 使用 require(moduleName) 来同步加载依赖模块
以下为一个例子:
math.js
/**
*创建计算圆形面积函数
*@param {Number} r 圆形面积
*@return {Number} 圆形面积
**/
functiuon area(r){
return Math.pi*r*r;
}
// 暴露对外接口
module.exports={
area:area
}
circle.js
/**
* 模块circle
*/
var math=require('./math')
var radius=10;
// 计算圆形面积
math.area(radius);
AMD & RequireJs
CommonJs是同步加载模块,适用在node服务端(文件存在本地硬盘),而不适合于浏览器端(通过网络加载,同步加载会阻塞页面).因此我们需要异步加载模块,AMD(Asynchronous Module Defenition)就诞生了.
- 使用 define(id?, dependencies?, factory) 函数来定义模块
- 使用 require(modules, callback) 函数来加载模块
id 是模块的标识,如果不写,如果不写默认就是我们加载的脚本文件的名字。
第二个参数是依赖的模块数组,默认是[‘require’,‘exports’,‘module’].
使用方法如下:
// 没有依赖其他模块的定义方式
difine('moduleA',function(require,exports,module){
exports.getNum=function(){
return 5;
}
});
// 依赖其他模块
define('moduleD',['moduleA'],function(moduleA){
// 通过模块A的方法初始化变量index
let index=moduleA.getNum();
// 通过return来暴露addIndex方法
return {
addIndex:function(){
index+1;
}
}
})
另一个方法是require(modules,callback) 第一个参数是加载的模块,第二个参数是要执行的函数.
require(['moduleA,moduleB'],function(moduleA,moduleB){
// 加载完模块A和模块B后的回调函数
})
Javascript没有原声的支持AMD规范,因此需要一个模块加载器,就是requireJS.requireJS会在运行时递归的分析依赖,查找模块路径,动态插入script,监听加载事件。
ES6的模块化
简单来说,ES6 模块的设计思想就是:一个 JS 文件就代表一个 JS 模块。在模块中你可以使用 import 和 export 关键字来导入或导出模块中的东西。
ES6 模块主要具备以下几个基本特点:
- 自动开启严格模式,即使你没有写 use strict
- 每个模块都有自己的上下文,每一个模块内声明的变量都是局部变量,不会污染全局作用域
- 模块中可以导入和导出各种类型的变量,如函数,对象,字符串,数字,布尔值,类等
- 每一个模块只加载一次,每一个 JS 只执行一次, 如果下次再去加载同目录下同文件,直接从内存中读取。 一个模块就是一个单例,或者说就是一个对象
export
// 个人所得税计算模块
// 在线参考站点:[个人所得税](http://www.gerensuodeshui.cn/)
// personal-income-tax.js
// 个税起征点
export const taxBasicNum = 3500;
// 税率等级
export const taxRatioLevel = [
{
num: 1500, // 小于1500
ratio: '3%',
subtract: 0, // 速算扣除数
},
{
num: 4500, // 大于1500,小于4500
ratio: '10%',
subtract: 105,
},
{
num: 9000, // 大于4500,小于9000
ratio: '20%',
subtract: 555,
},
{
num: 35000, // 大于9000,小于35000
ratio: '25%',
subtract: 1005,
},
{
num: 55000, // 大于35000,小于55000
ratio: '30%',
subtract: 2755,
},
{
num: 80000, // 大于55000,小于80000
ratio: '35%',
subtract: 5505,
},
{
num: 80000, // 大于80000
ratio: '45%',
subtract: 13505,
}];
// 所缴税收
// 应纳税所得额 = 应发工资 - 五险一金 - 个税起征点
// 所缴税收 = 应纳税所得额 * 税率 - 速算扣除数
export function calTax (num, insurance) {
let taxShouldNum = num - insurance - taxBasicNum;
let tax;
switch (true) {
case taxShouldNum < taxRatioLevel[0].num:
tax = taxShouldNum * taxRatioLevel[0].ratio - taxRatioLevel[0].subtract;
break;
case taxShouldNum < taxRatioLevel[1].num:
tax = taxShouldNum * taxRatioLevel[1].ratio - taxRatioLevel[1].subtract;
break;
case taxShouldNum < taxRatioLevel[2].num:
tax = taxShouldNum * taxRatioLevel[2].ratio - taxRatioLevel[2].subtract;
break;
case taxShouldNum < taxRatioLevel[3].num:
tax = taxShouldNum * taxRatioLevel[3].ratio - taxRatioLevel[3].subtract;
break;
case taxShouldNum < taxRatioLevel[4].num:
tax = taxShouldNum * taxRatioLevel[4].ratio - taxRatioLevel[4].subtract;
break;
case taxShouldNum < taxRatioLevel[5].num:
tax = taxShouldNum * taxRatioLevel[5].ratio - taxRatioLevel[5].subtract;
break;
case taxShouldNum > taxRatioLevel[6].num:
tax = taxShouldNum * taxRatioLevel[6].ratio - taxRatioLevel[6].subtract;
break;
default:
tax = 0;
}
return tax;
}
// 实发工资
export function calWages(num, insurance) {
let tax = calTax(num, insurance);
let wages = num - insurance - tax;
return wages;
}
import
// main.js
import {taxBasicNum, taxRatioLevel, calTax, calWages} from './personal-income-tax';
// 可以使用 taxBasicNum 输出一段话,说明个税的起征点是多少
console.log(`个税起征点为:taxBasicNum`);
// 还可以使用 taxRatioLevel 数据输出一个表格,对应各个等级的税率,这里就不演示了
// 计算20000元缴纳了五险一金3000后,应该缴纳多少税收及实际税后工资为多少
let tax = calTax(20000, 3000);
let wages = calWages(20000, 3000);
更详细的介绍:
index.js
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Document</title>
</head>
<body>
<!-- module -->
<script type="module" src="main.js"></script>
</body>
</html>
module.js
// 导出方法1
export var a = 1;
export var b = 2;
// 导出方法2
var c=3;
var d=4;
export { c,d };
export { c as myC,d as myD }
// 导出方法3
export default []
import arr,{ a,b,c,d,myC,myD } from './module.js';
console.log(a);
console.log(b);
// 第二种方式
console.log(c);
console.log(d);
// 第二种导入方式的另一种用法
console.log(myC);
console.log(myD);
// 第3种导入方法
console.log(arr);
// 第4种整体倒入 正常导入
import * as m from './module.js';
console.log(m);
// 异步导入
import ('./module.js').then(function(m){
console.log(m);
});
我个人觉得应该尽量使用ES6的export,import 而不是AMD 和CMD,因为requireJs会使性能变慢.