模块化编程

拿到设计时,我们首先要把项目划分为一个个模块,或者组件

划分原则:

  • 按照功能和需求划分:普通业务模块/ 组件
  • 按照复用性划分:通用业务模块
  • 通用功能模块组件【UI组件库】

1.1 模块化演化进程

未来的项目一定是 模块化/组件化 开发!!

模块化编程进化历史

  • 单例设计模式

  • AMD「require.js」

  • CommonJS(node.js)

  • CMD「sea.js」

  • ES6Module

1.2单例设计模式

也就是原生js写法,用<script src>,调用文件,把各个功能写在不同的模块下,最后合并到页面中。

问题

  • 会出现全局变量污染,也就是模块间会有相互冲突。
  • 模块间相互引用问题,用window方法多了后也会冲突

解决问题

  • 模块间相互冲突的问题:

    • 「闭包」 ;
  • 模块间相互引用的问题:

    • 使用windows.XXX = XXX 暴露方法到全局

    • 解决windows多了后冲突:直接导出整个对象,就像react组件一样,对象中有所有对应的方法。

      详细解释:让每个模块有一个独有的模块名xxxModule,也叫命名空间(回想起来c不就是吗)。

      模块闭包时,直接return对象,别人要用时,直接模块名.方法()调用就行。

如此单例设计模式就完成了。

注意:包导入的顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//单例格式
let xxxModule = (function () {
let time = new Date();
const query = function query() {
// ...
};
const handle = function handle() {
// ...
};

// 把供其它板块调用的方法,暴露到全局对象上
//「局限:暴露的内容比较多,则还会引发全局变量冲突」
// window.query = query;

return {
query,
handle
};
})();

没看例子:p132 37:17~52:30

不足之处:需要手动分析模块之间的依赖关系,按顺序依次导入相关模块;所有模块都是基于script一个个导入的,这样页面HTTP请求变多!

1.3 AMD「require.js」

https://requirejs.org

优势:在保证模块之间独立和可以相互访问的基础上,HTML中无需再导入各个模块了「不存在顺序问题」,也不需要自己去分析相互间的依赖关系!
不足:依赖模块的导入是“前置导入”,只有把依赖模块动态导入完毕,才会触发回调函数执行「阻碍代码执行速度」;代码书写的顺序也不是很方便;可能存在重复导入;

image-20231112133515879

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//main.js
require.config({
//全局配置
baseUrl: 'js/lib',
});

require(['moduleB', 'moduleA'], function (moduleB, moduleA) {
console.log(moduleB.average(10, 20, 30, 40, 50));
});

//moduleA.js
define(function () {
return {
sum(...args) {
let len = args.length,
firstItem = args[0];
if (len === 0) return 0;
if (len === 1) return firstItem;
return args.reduce((total, item) => {
return total + item;
});
}
};
});

//moduleB.js
define(['moduleA'], function (moudleA) {
return {
// 求平均数(去掉最大最小值)
average(...args) {
let len = args.length,
firstItem = args[0];
if (len === 0) return 0;
if (len === 1) return firstItem;
args.sort((a, b) => a - b);
args.pop();
args.shift();
return (moudleA.sum(...args) / args.length).toFixed(2);
}
};
});

没看例子:p132 58:17~1:20:05

1.4 CommonJS模块化规范

node.js出现了,不用导入

每创建一个js文件就创建了一个模块,里面的代码就是独立的。用module.exports导出对象。

唯一的问题:浏览器端不支持CommonJS规范
淘宝“玉伯”仿照CommonJS规范,研发了一款插件 sea.js ,旨在把CommonJS规范搬到浏览器端运行「这种模块思想被称之为CMD」

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* ---A.js--- */
const sum = function sum(...params) {
return params.reduce((result, item) => {
return result + item;
});
};
// 暴露一些API
module.exports = {
sum
};

/* ---B.js--- */
let name = '哈哈';
let A = require('./A');
const average = function average(...params) {
return (A.sum(...params) / params.length).toFixed(2);
};
module.exports = {
average
};

/* ---main.js--- */
let A = require('./A');
console.log(A.sum(10, 20, 30, 40));
let B = require('./B');
console.log(B.average(10, 20, 30, 40));

缓存机制

还是没有实战—27:35

1.5 ES6Module模块规范

https://es6.ruanyifeng.com/#docs/module

可以直接运行在浏览器端;不支持IE;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
使用在html文件上时,导入文件前要加入type = module,预览要使用http协议(LiveServer),不能使用File协议。
模块的导入导出:
整体来讲,每个模块导出的是一个Module对象,我们可以基于 export/export default 为对象中加入导出的内容
@导出方法1 export default xxx;
+ xxx可以是任意类型值
+ 给Module对象新增一个 “default” 属性,属性值就是导出的值
+ 在一个模块中,export default只允许出现一次
@导出方法2 export 创建值的表达式(声明变量 = 值);
let num = 10;
export num; //这样写是错误的
export let num = 10; //这样写是正确的
---
let num = 10;
export {
num, //这样写是可以的
x:100 //这样写是错误的
};
---
+ export可以出现多次,导出多个内容
+ 声明的变量叫啥名字,最后给Module对象加入的是同名的属性名,属性值就是变量的值

1.5.2导入方法

我们会基于 import 方法导入模块「导入模块导出的内容」

  • import N from ‘模块地址(在浏览器端不能忽略.js后缀名)’;

N获取的不是整个导出的Module对象,仅仅获取的是Module中default这一项的值
不能在import期间,直接对N解构赋值,因为涉及解构赋值就会变为另外一种情况

  • 解构赋值:import {x,y} from ‘模块地址’;

直接对导出的Module对象中(排除default属性),导出的内容进行解构赋值

  • import * as M from ‘模块地址’;先导入所有导出的内容,赋值给M变量

还是没有细看