js設計模式在web前端開發中的實踐——網站登錄

本文,筆者將以一個登錄模塊的開發流程,向各位說明穿插在其中的知識點 —— 【單例模式】和【發佈-訂閱模式】的使用。


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();   //刷新地址列表

天哪!這是多可憐的一件事!

這時你終於想起重構這些代碼:你又那麼“恰好”的想起了翻看過的 發佈-訂閱設計模式 。你欣喜若狂。

我們就會越來越疲於應付一些突如其來的業務要求,正比如大量重複相同且沒什麼意義的代碼。要麼跳槽了事,要麼必須學會重構。
使用發佈—訂閱模式重寫之後,對用戶信息感興趣的業務模塊將自行訂閱登錄成功的消息事件。 當登錄成功時,登錄模塊只需要發佈登錄成功的消息,而業務方接受到消息之後,就會開始進行各自的業務處理,登錄模塊並不關心業務方究竟要做什麼,也不想去了解它們的內部細節。

改善過後代碼如下:

$.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('正在刷新...');
		}
	}
})();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章