提高代码的复用性
:模块化可以将代码划分成可重用的部分,降低代码的冗余和重复,提高代码的复用性。
简化代码的维护和调试
:当一个软件系统变得越来越复杂时,进行模块化开发可以使得每个模块都相对独立,这样就可以方便地维护和调试每个模块,而不必考虑整个系统的复杂性。
提高代码的可读性
:模块化可以使得代码更加结构化,清晰明了,从而提高代码的可读性和可维护性。
提高开发效率
:模块化开发可以使得团队成员在不同模块上并行开发,从而提高开发效率。
降低项目的风险
:模块化开发可以使得开发人员更加关注模块之间的接口和依赖关系,从而降低项目的风险。
总之,模块化开发是一种有效的软件开发模式,可以提高软件开发的质量、效率和可维护性,特别是在大型软件系统的开发中,模块化更是必不可少的
。1
了解
)将每个功能以及它相关的一些状态数据,单独存放到不同的文件当中,我们去约定每一个文件就是一个独立的模块。
我们去使用这个模块,就是将这个模块引入到页面当中,然后直接调用模块中的成员(变量 / 函数)。
一个script标签就对应一个模块
,所有模块都在全局范围内工作缺点:
污染全局作用域
命名冲突问题
无法管理模块依赖关系
示例代码
:
// Student.js
var maths = 80;
var chinese = 90;
function score() {
return maths + chinese;
}
// Cook.js
var name = "煮饭";
function make_food() {
return new Promise(function (resolve, reject) {
setTimeout(() => {
resolve("煮好饭了")
}, 1000);
})
}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件划分模式title>
head>
<body>
<script src="Cook.js">script>
<script src="Student.js">script>
<script>
var studentScore = score();
console.log(studentScore );
make_food().then((message) => console.log(message));
script>
body>
html>
了解
)我们约定每个模块只暴露一个全局的对象,我们所有的模块成员都挂载到这个全局对象下面。
在第一阶段的基础上,通过将每个模块「包裹」为一个全局对象的形式实现,
有点类似于为模块内的成员添加了「命名空间」的感觉。
没有私有空间
模块成员仍然可以在外部被访问/修改
无法管理模块依赖关系
迪米特法则
:又叫作最少知识原则(The Least Knowledge Principle),一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话。英文简写为: LOD
。
// Student.js
var Student = {
maths: 80,
chinese: 90,
score: function () {
return this.maths + this.chinese;
},
skills: [Cook]
}
// Cook.js
var Cook = {
name: "煮饭",
make_food: function () {
return new Promise(function (resolve, reject) {
setTimeout(() => {
resolve("煮好饭了")
}, 1000);
})
}
}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>命名空间划分模式title>
head>
<body>
<script src="Cook.js">script>
<script src="Student.js">script>
<script>
// 我们可以用命名空间来访问属性,成员
var studentScore = Student.score();
console.log(studentScore);
Student.skills[0].make_food().then((message) => console.log(message));
script>
body>
html>
了解
)使用
立即执行函数
的方式,去为我们的模块提供私有空间。将模块中每个成员都放在一个函数提供的私有作用域当中,
对于需要暴露给外部的成员,我们可以通过return
的方式实现。确保了私有成员的安全。
有了私有成员的概念,私有成员只能在模块成员内通过闭包的形式访问。
利用立即执行函数
的参数传递模块依赖项
立即执行函数
: (function() {})()
// Student.js
; let Student = (function (modules) {
var maths = 80;
var chinese = 90;
var skills = [modules['cook']];
score = function () {
return maths + chinese;
}
return {
score,
skills
}
})({ cook: Cook }); // 模块的传入
// Cook.js
// 为什么加 ;
// 怕别的导入的库后面没有加 ";"
; let Cook = (function () {
var name = "煮饭";
make_food = function () {
return new Promise(function (resolve, reject) {
setTimeout(() => {
resolve("煮好饭了")
}, 1000);
})
}
return {
make_food: make_food
}
})();
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>立即执行函数title>
head>
<body>
<script src="Cook.js">script>
<script src="Student.js">script>
<script>
// 我们可以用命名空间来访问属性,成员
var studentScore = Student.score();
console.log(studentScore);
Student.skills[0].make_food().then((message) => console.log(message));
script>
body>
html>
一个文件就是一个模块
每个模块都有单独的作用域
通过module.exports
导出成员
通过require
函数载入模块缺点:
- CommonJS的出现本来是为了解决Node.js问题,约定的是以
同步模式加载模块
,在浏览器端
使用会导致效率低下
// Student.js
let cook = require("./Cook")
var maths = 80;
var chinese = 90;
var skills = [cook];
function score() {
return maths + chinese;
}
module.exports = {
score,
skills
}
var name = "煮饭";
function make_food() {
return new Promise(function (resolve, reject) {
setTimeout(() => {
resolve("煮好饭了")
}, 1000);
})
}
module.exports = { make_food }
/**
* 一个文件就是一个模块
* 每个模块都有单独的作用域
* 通过module.exports导出成员
* 通过require函数载入模块
* CommonJS约定的是以同步模式加载模块,在浏览器端使用会导致效率低下。
*/
let student = require('./Student')
var studentScore = student.score();
student.skills[0].make_food().then((message) => console.log(message));
console.log(studentScore)
Require的基本实现逻辑(重点看)
/**
* require的基本实现原理
* webpack打包原理
*/
(function () {
var modules = {
"./Cook.js": function (module, require) {
var name = "煮饭";
function make_food() {
return new Promise(function (resolve, reject) {
setTimeout(() => {
resolve("煮好饭了")
}, 1000);
})
}
module.exports = { make_food }
},
"./Student.js": function (module, require) {
let cook = require("./Cook.js")
var maths = 80;
var chinese = 90;
var skills = [cook];
function score() {
return maths + chinese;
}
module.exports = {
score,
skills
}
},
"./main.js": function (module, require) {
let student = require('./Student.js')
var studentScore = student.score();
student.skills[0].make_food().then((message) => console.log(message));
console.log(studentScore)
}
}
// 为什么多次require只执行一次 因为有缓存
// 为什么多次require只执行一次 因为有缓存
var caches = {};
// 基本实现逻辑
// 基本实现逻辑
function require(moduleId) {
if (caches[moduleId]) {
return caches[moduleId];
}
var module = { exports: {} }
//需要把模块运行一下
modules[moduleId](module, require);
caches[moduleId] = module.exports;
return module.exports;
}
require("./main.js")
})();
这个规范的出现主要是为了解决在浏览器里面,因为网络慢,加载JS卡顿的问题
作为一个规范,只需定义其语法API,而不关心其实现。AMD规范简单到只有一个API,即define函数:
define([module-name?], [array-of-dependencies?], [module-factory-or-object]);
module-name
: 模块标识,可以省略。array-of-dependencies
: 所依赖的模块,可以省略。module-factory-or-object
: 模块的实现,或者一个JavaScript对象。
AMD的基本实现逻辑(重点看)
// 缓存 为什么多个require只会执行一次
var modules = {};
// 代码实现页面可能有需要依赖的其他js
// 所以里面也用到了loadScript
function define(name, deps, factory) {
var pending = deps.length;
// 创建一个数组 来看看依赖是否全部加载完成
var resolvedDeps = new Array(pending);
deps.forEach(function (dep, index) {
if (modules[dep]) {
resolvedDeps[index] = modules[dep];
pending--;
} else {
loadScript(dep + ".js", function () {
resolvedDeps[index] = modules[dep];
pending--;
// 这里面是异步执行了的 所以在这里
// 在if外面都要做一个加载完成的判断
if (pending === 0) {
modules[name] = factory.apply(null, resolvedDeps);
}
})
}
if (pending === 0) {
modules[name] = factory.apply(null, resolvedDeps);
}
});
}
// 引用的时候也有可能需要依赖其他的js
// 所以在这个里面也用到了 loadScript
function require(deps, callback) {
var pending = deps.length;
var resolvedDeps = new Array(pending);
deps.forEach(function (dep, index) {
if (modules[dep]) {
resolvedDeps[index] = modules[dep];
pending--;
} else {
loadScript(dep + ".js", function () {
resolvedDeps[index] = modules[dep];
pending--;
// 这里面是异步执行了的 所以在这里
// 在if外面都要做一个加载完成的判断
if (pending === 0) {
callback.apply(null, resolvedDeps);
}
})
}
if (pending === 0) {
callback.apply(null, resolvedDeps);
}
})
}
// 动态加载script的话,并不会让document的渲染进行卡顿
function loadScript(url, callback) {
var script = document.createElement("script");
script.src = url;
// 加载的过程实际就是 执行 define 函数的过程
script.onload = callback || function () { };
document.head.appendChild(script);
}
require(["student"], function (student) {
var studentScore = student.score();
console.log(studentScore)
})
//student.js
// AMD的实现
define("student", [], function () {
var maths = 80;
var chinese = 90;
function score() {
return maths + chinese;
}
return { score }
});
实现逻辑与AMD基本类似,就不写了
ToDO
什么是模块化?为什么要进行模块化开发? ↩︎
细说前端模块化开发 ↩︎