js设计模式在web前端开发中的实践——网站登录

2021-01-08 10:15发布

本文,笔者将以一个登录模块的开发流程,向各位说明穿插在其中的知识点 —— 【单例模式】和【发布-订阅模式】的使用。


OK,步入正题。

假如你是一个大型网站的前端开发人员,在经过激烈地讨论以后,决定让你负责登录模块的开发。
先说,这个网站很大,所以有很多模块,比如:header头部、nav导航、消息列表、购物车… 这些模块有一个共同的特点,就是必须先用ajax异步请求获取用户的登录信息 —— 这是正常的,比如header中就要显示用户的名字和头像。
所以你很重要。

你着手做了。你的构思是:仿Web QQ那样。比如当点击了左边导航栏里的“请登录”的头像时,就会弹出一个遮罩层 + 一个登录浮窗。很明显这个浮窗在页面中总是唯一的。
第一种解决方案是在页面加载完成以后便创建好这个div浮窗,他一开始是处于隐藏状态的,当用户点击时,才开始显示 —— 我想,用js控制的display切换会有帮助。
但你很快就会发现一个问题:也许用户进入这个页面只是随便逛逛、看看天气、or打打游戏,根本不想进行登录操作。这时,这个“总是一开始就被创建好的登录浮窗”就有可能成为白白浪费一些DOM节点的“元凶”。
你很快又有了想法,决定让用户点击对应区域时再去加载。你可能会写类似如下代码:

<body>
	<button id="loginBtn">登录</button>
</body>

//js
<script>
	var createLoginLayer=function(){
		var div=document.createElement('div');
		div.innerHTML="我是登录浮窗";
		div.style.display="none";
		document.body.appendChild(div);
		return div;
	}
	document.querySelector('#loginBtn').onclick=function(){
		var loginBtn=createLoginLayer();
		loginBtn.style.display="block";
	}
</script>

你突然发现,这样做虽然避免了每次页面打开时的DOM浪费,却产生看一个更大的问题:每次click时都会创建一个新的div!虽然我们可以在点击“关闭”时去销毁掉这个ElementNode,但这样频繁地创建和删除结点,在一个页面上,明显是不合理,也是不必要的。
于是你自然地想到了前几天看到的 惰性单例设计模式 ,可以用一个变量去判断是否已创建过相同的div。 于是,上面代码中的js部分就变成了这样:

var createLoginLayer=(function(){
	var div;
	return function(){
		if(!div){
			div=document.createElement('div');
			div.innerHTML="我是登录浮窗";
			div.style.display="none";
			document.body.appendChild(div);
		}
		return div;
	}
})();
document.querySelector('#loginBtn').onclick=function(){
	var loginBtn=createLoginLayer();
	loginBtn.style.display="block";
}

OK,虽然它还有些许缺点(复用性差),但对于几个功能不冲突的页面来说还算“绰绰有余”,你心满意足地用了ajax/fetch/axios/vue-resource完成了登陆功能。

但现在麻烦又来了。正如前面所说,你所做工作关联性极大,但你不知道除了header、nav、消息列表、购物车之外,将来还有哪些模块需要用到这些用户信息。但如果他们和用户信息之间产生了强耦合性,就比如你按如下方式写了功能代码:

login.succ(function(data){
	header.setAvatar(data.avatar);
	nav.setAvatar(data.avatar);
	message.refresh();   //刷新消息列表
	cart.refresh();   //刷新购物车列表
});

现在虽然只有登录模块是你负责编写的,但你还要了解header里设置头像的方法叫setAvatar、购物车中刷新页面的方法叫refresh,,,这可不是个好消息 —— 这种耦合会使程序变得“僵硬”。

试想某一天,这个项目里又增加了一个“收货地址”管理模块。它不是你负责完成的,此时你正在外地度过你一年仅几天的可怜假期,但同事却给你打了一个电话:“Hello,登录后还要麻烦刷新下地址列表!” 于是你不得不打开电脑,找到了不知道几个月前写的那段login.succ的代码片段,在此函数最后加了一句:

address.refresh();   //刷新地址列表1

天哪!这是多可怜的一件事!

这时你终于想起重构这些代码:你又那么“恰好”的想起了翻看过的 发布-订阅设计模式 。你欣喜若狂。

我们就会越来越疲于应付一些突如其来的业务要求,正比如大量重复相同且没什么意义的代码。要么跳槽了事,要么必须学会重构。
使用发布—订阅模式重写之后,对用户信息感兴趣的业务模块将自行订阅登录成功的消息事件。 当登录成功时,登录模块只需要发布登录成功的消息,而业务方接受到消息之后,就会开始进行各自的业务处理,登录模块并不关心业务方究竟要做什么,也不想去了解它们的内部细节。

改善过后代码如下:

$.ajax('xxx?login',function(data){
	login.trigger('loginSucc',data);   //发布登录成功的消息
});
//各模块去监听消息
var header=(function(){
	login.listen('loginSucc',function(data){
		header.setAvatar(data.avatar);
	});
	return{
		setAvatar:function(data){
			console.log('设置header头像');
		}
	}
})();
//...

假如有一天,项目中增加了一个刷新收货地址列表的行为,那么只要在“收货地址”模块里加上监听消息的方法,即可。而这些你就完全不必操心了:

var address=(function(){
	login.listen('loginSucc',function(data){
		address.refresh(data);
	});
	return{
		refresh:function(avatar){
			console.log('正在刷新...');
		}
	}
})();


作者:行舟客

链接:https://yunxiaomeng.blog.csdn.net/article/details/103541714

来源:CSDN
著作权归作者所有,转载请联系作者获得授权,切勿私自转载。