同步模块模式——SMD(Synchronous Module Definition)
模块化
:将复杂的系统分解成高内聚、低耦合的模块,使系统开发变得可控、可维护、可拓展,提高模块的复用率请求发出后,无论模块是否存在,立即执行后续的逻辑,实现模块开发中对模块的立即引用
创建一个导航模块
<style>
#nav {
list-style: none;
margin: 0;
padding: 0;
}
#nav li {
float: left;
width: 80px;
height: 32px;
line-height: 32px;
text-align: center;
background-color: #eee;
cursor: pointer;
color: #0051ff;
margin: 0 2px;
}
#nav li span {
margin-left: 3px;
color: red;
font-size: 12px;
}
#panel {
display: none;
position: absolute;
top: 40px;
width: 200px;
height: 150px;
line-height: 150px;
text-align: center;
background-color: #ccc;
}
</style>
<body>
<ul id="nav"></ul>
<div id="panel">面板</div>
</body>
/**
* 创建导航逻辑(由A工程师创建)
* @param {HTMLElement} dom 上下文DOM
* @param {Array} data 数据
*/
function createNav(dom, data) {
/* ============== A工程师加入 创建了导航模块并添加了导航的内容 ============== */
{
for (let i = 0; i < data.length; i++) {
dom.append(`<li>${
data[i].label}</li>`)
}
}
/* ============== B工程师加入 在A工程师的代码里边为导航添加引导 ============== */
{
let li = $('li', dom);
for (let i = 0; i < data.length; i++) {
if (data[i].isGuide) {
$(li[i]).append('<span>[hot]</span>');
}
}
}
/* ============== C工程师加入 在A工程师的代码里边为导航添加事件 ============== */
{
let li = $('li', dom);
li.on('mouseover', function () {
$('#panel').css({
left: this.offsetLeft + 'px', display: 'block' });
}).on('mouseout', function () {
$('#panel').css({
display: 'none' });
});
}
};
// 模拟请求,获取导航引导数据
setTimeout(function () {
let data = [
{
label: '首页', isGuide: false },
{
label: '推荐', isGuide: true },
{
label: '我的', isGuide: false }
];
// 创建的导航模块
createNav($('#nav'), data);
}, 2000);
缺点
:- 由于B、C工程师是在A工程师写的代码的内部添加的自己的逻辑,当后续需求要求A工程师修改逻辑时,就很有可能影响B、C工程师的逻辑,导致A工程时修改时要照顾B、C写的逻辑。
解决方案
:使用模块化开发(包括模块管理器和模块调用器)
模块管理器
// 模块管理器
const F = {
};
/**
* 定义模块管理器
* @param {string} str 模块路由
* @param {Function} fn 模块方法
* @returns F模块管理器对象
*/
F.define = function (str, fn) {
// 过滤掉第一个元素为F的字符串数组
let keys = str.replace(/^F\./, '').split('.');
// 屏蔽对define与module模块方法的重写
if (keys[0] === 'define' || keys[0] === 'module') return this;
// 遍历字符串数组,赋值到当前对象内
let obj = this;
for (let i = 0; i < keys.length; i++) {
// 防止后续模块覆盖以前定义的模块
if (typeof obj[keys[i]] === 'undefined') {
obj[keys[i]] = {
};
if (i === keys.length - 1) {
obj[keys[i]] = fn && fn();
}
}
obj = obj[keys[i]];
}
// 当前对象
return this;
};
// 创建一个字符串模块
F.define('F.string.fn', function () {
return {
// 定义一个去除字符串空格的方法
trim(str) {
return str.replace(/\s+/g, '');
}
};
});
F.string.fn.trim(' P r o s p e r L e e '); // 'ProsperLee'
F.define('create.dom', function () {
let dom = null;
return function (el) {
dom = document.createElement(el);
dom.innerHTML = `创建的DOM元素-${
el}`;
document.body.appendChild(dom);
return dom;
}
});
F.create.dom('li'); // <li>创建的DOM元素-li</li>
F.define('a', function () {
return function () {
} });
F.define('a.b', function () {
return function () {
} });
F.define('a.b.c', function () {
return function () {
} });
F.define('a.b.c', function () {
return {
a: {
}, b: {
}, c: {
}, d: {
}, e: {
} } });
模块调用器
/**
* 模块调用方法
* @param {...any} args 需要最后的参数为function
*/
F.module = function (...args) {
// 依赖模块列表
let modules = [];
// 判断最后一个参数是否为函数
let fn = typeof args[args.length - 1] === 'function' ? args.pop() : null;
// 将参数转换为数组
let arr = (args.length === 1 && Array.isArray(args[0])) ? args[0] : args;
// 遍历参数
let i = 0;
while (i < arr.length) {
// 判断参数是否是字符串(通过define创建的)
if (typeof arr[i] === 'string') {
let keys = arr[i].replace(/^F\./, '').split('.');
// 获取当前F对象
let mod = this;
for (let j = 0; j < keys.length; j++) {
// 替换当前的mod,查询到最里层对象
mod = mod[keys[j]];
}
modules.push(mod);
} else {
modules.push(arr[i])
}
i++;
}
fn && fn.apply(this, modules);
};
F.module(['create.dom', 'F.string.fn.trim', document], function (dom, trim, doc) {
console.log(this); // F
dom('li').innerText = trim(' P r o s p e r L e e '); // <li>ProsperLee</li>
console.log(doc); // #document
});
解决创建导航模块问题
// 模块管理器
const F = {
};
/**
* 定义模块管理器
* @param {string} str 模块路由
* @param {Function} fn 模块方法
* @returns F模块管理器对象
*/
F.define = function (str, fn) {
// 过滤掉第一个元素为F的字符串数组
let keys = str.replace(/^F\./, '').split('.');
// 屏蔽对define与module模块方法的重写
if (keys[0] === 'define' || keys[0] === 'module') return this;
// 遍历字符串数组,赋值到当前对象内
let obj = this;
for (let i = 0; i < keys.length; i++) {
// 防止后续模块覆盖以前定义的模块
if (typeof obj[keys[i]] === 'undefined') {
obj[keys[i]] = {
};
if (i === keys.length - 1) {
obj[keys[i]] = fn && fn();
}
}
obj = obj[keys[i]];
}
// 当前对象
return this;
};
/**
* 模块调用方法
* @param {...any} args 需要最后的参数为function
*/
F.module = function (...args) {
// 依赖模块列表
let modules = [];
// 判断最后一个参数是否为函数
let fn = typeof args[args.length - 1] === 'function' ? args.pop() : null;
// 将参数转换为数组
let arr = (args.length === 1 && Array.isArray(args[0])) ? args[0] : args;
// 遍历参数
let i = 0;
while (i < arr.length) {
// 判断参数是否是字符串(通过define创建的)
if (typeof arr[i] === 'string') {
let keys = arr[i].replace(/^F\./, '').split('.');
// 获取当前F对象
let mod = this;
for (let j = 0; j < keys.length; j++) {
// 替换当前的mod,查询到最里层对象
mod = mod[keys[j]];
}
modules.push(mod);
} else {
modules.push(arr[i])
}
i++;
}
fn && fn.apply(this, modules);
};
// 创建导航模块
F.define('nav.create', function () {
return function (dom, data) {
for (let i = 0; i < data.length; i++) {
dom.append(`<li>${
data[i].label}</li>`)
}
}
});
// 添加导航引导模块
F.define('nav.create.addGuide', function () {
return function (dom, data) {
let li = $('li', dom);
for (let i = 0; i < data.length; i++) {
if (data[i].isGuide) {
$(li[i]).append('<span>[hot]</span>');
}
}
}
});
// 添加导航事件模块
F.define('nav.create.hover', function () {
return function (dom) {
let li = $('li', dom);
li.on('mouseover', function () {
$('#panel').css({
left: this.offsetLeft + 'px', display: 'block' });
}).on('mouseout', function () {
$('#panel').css({
display: 'none' });
});
}
});
// 导航数据请求模块
F.define('nav.request', function () {
return function (cb) {
setTimeout(function () {
let data = [
{
label: '首页', isGuide: false },
{
label: '推荐', isGuide: true },
{
label: '我的', isGuide: false }
];
cb($('#nav'), data);
}, 2000);
}
});
console.log(F);
F.module(['nav.request', 'nav.create', 'nav.create.addGuide', 'nav.create.hover'], function (request, create, addGuide, hover) {
request(function (dom, data) {
create(dom, data);
addGuide(dom, data);
hover(dom);
});
});
特点
模块化开发即是以分而治之的思想,实现对复杂系统的分解,使系统随着其功能的增加而变得可控、可拓展、可维护。
模块化开发是一种对系统的分解,但使用时又像是以组合模式对模块的组合。
因此这也使得系统中的问题一般出现在局部,使得开发人员处理相应模块即可,而不用顾虑整个系统。
因此相对于整个复杂的系统,对于局部模块的改造、优化甚至替换所需成本要小得多。
组合的灵活性也使得我们可以实现更复杂、多样化的功能。