通過MVC模式將Web視圖和邏輯代碼分離
MVC
模式(Model-View-Controller)
常被用在Web
程序中。如Struts
框架就是一個基于MVC
模式的Web
框架。所謂MVC
模式,就是將視圖(也就是客戶端代碼,包括html
、javascript
等)和模型(和數據庫及業務邏輯相關的Java
代碼)分開。并通過控制器將兩者聯系起來。這樣做的好處可以使客戶端開發人員和服務端開發人員的工作盡量分開,以使他們之間的干擾降到最低。 雖然象Struts這樣的框架可以很好地Model和View分離。但是對于客戶端的代碼,仍然存在著一定的視圖和邏輯混合的現象。如下面的代碼所示:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" >
function fun1(obj) { }
function fun2() { }
</script>
</head>
<body>
<input type="button" value="按鈕1 " onclick="fun1(this)"/>
<input type="button" value="按鈕2 " onclick="fun2()"/>
</body>
</html>
從上面的代碼可以看出,html代碼和javascript代碼都混在了同一個html文件中。在一般情況下,客戶端的界面是由美工設計的,而對于javascript代碼,美工一般是不參與實現的。這些代碼一般也應屬于業務邏輯代碼的一部分,雖然它們都在客戶端運行,但可能也會處理一定的業務邏輯,如驗證數據的正確性。尤其在AJAX應用中,在客戶端還會通過http協議從服務端獲取數據。這樣就和業務邏輯綁定得更緊了。因此,如果將用于描述界面的html和用于處理業務邏輯的javascript(渲染界面的javascript除外)混在一起,非常不利于團隊中負責這兩方面的人員進行協調。
最好的可能就是將這些javascript代碼從html代碼中分離。也許有很多人馬上就會想到,將這些javascript放到.js文件中,然后使用<script>標簽引用一下就ok了。代碼如下:
fun.js文件
function fun1(obj) { ... }
function fun2() {... }
index.html文件
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="fun.js">
</script>
</head>
<body>
<input type="button" value="按鈕1 " onclick="fun1(this)"/>
<input type="button" value="按鈕2 " onclick="fun2()"/>
</body>
</html>
雖然上面的代碼從某種程度上達到了視圖和邏輯分離的效果。但仍然有著一定的聯系。我們可以看到,兩個<input>標簽的onclick事件不還是引用著fun1和fun2函數嗎!其實美工人員是不關心這兩個函數到底是做什么的,甚至并不需要知道它們的存在。那么是否有更高的方法呢?答案當然是肯定的,就是通過動態的方式指定onclick事件,而這一切美工人員是完全沒有感覺的。
我們在文章的開頭提到了MVC模式。其實在客戶端也存在著一個MVC體系結構。我們可以將視圖(V)看成是描述界面的html、css和javascript代碼,而模型(M)可以看成是處理業務邏輯的javascript代碼,而控制器(C)就是將這兩類代碼連接起來的代碼(一般也是javascript代碼)。
在本文中給出了一個小例子來演示一下如何通過動態的方法將V
和M
分離。這個例子是通過<div>
實現的10
個小方塊,點擊其中的某一個,會將相應的數字追加到下方的文本框架中,并且加入了一些用javascript
實現的效果,如以一定間隔隨機更新方塊和數字的顏色,直接在文本框中輸入數字后,相應的文本框和數字的顏色也會隨機發生變化。界面效果如圖1
所示。
圖1
下面先來實現View。先看看如下的代碼:
numberKeys.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="my.css"/>
<script type="text/javascript" src="addevents.js">
</script>
</head>
<body>
<input id ="changeColor" type="button" value="開始變換顏色" />
<br/><br/>
<div id ="nubmerKeys" class="numberKeys" >
</div>
<br/><br/>
<br/><br/>
<input id="numbers" type="text" size="80"/>
</body>
</html>
從上面的代碼可以看出,除了一些html代碼外,沒有任何和業務邏輯有關的javascript代碼。但使用<script>引用了一個叫addevents.js的文件。在這個文件中將為這個程序添加相應的邏輯代碼,也就是說,這個文件相當于MVC中的M和C。
動作裝載事件是通過window的onload事件完成的,代碼如下:
window.onload = onLoad; // 為onload指定事件函數
function onLoad()
{
var text = document.getElementById("numbers");
if(text)
{
text.onkeyup = onKeyup; // 為文本框指定onkeyup事件
}
var button = document.getElementById("changeColor");
if(button)
{
button.onclick = stopAndStartTimer; // 為按鈕指定onclick事件
}
addButton(); // 用于在<div>中加入10個<div>標簽作為小方塊,并指定onclick事件
}
下面先來看一下addButton函數是如何實現的,代碼如下:
function addButton()
{
var div = getNumberKeysDiv(); // 獲得id為nubmerKeys的<div>標簽
try
{
if(div)
{
// 刪除<div>中的所有子元素
for(var i = div.childNodes.length - 1; i >= 0; i--)
{
try
{
div.removeChild(div.childNodes[i]);
}
catch(e)
{
}
}
// 為<div>標簽加10個子<div>標簽
for(var i = 0; i < 10; i++)
{
var button = document.createElement("div");
button.className = "button";
button.style.left = i * 61 + "px";
button.style.backgroundColor = getRandomColor();
button.style.border="solid 1px";
button.style.textAlign = "center";
button.style.lineHeight = "50px";
button.style.color = getRandomColor();
button.onclick = buttonOnClick;
div.appendChild(button);
button.innerHTML ="<b>" +i + "</b>";
}
}
}
catch(e)
{
}
}
addButton函數的基本實現原理是先將<div>中的所有元素刪除,再向其中加入10個<div>標簽。在addButton函數中有幾個關鍵的函數需要講解一下。
getNumberKeysDiv函數
這個函數用于獲得叫numberKeys的<div>標簽,實現代碼如下:
function getNumberKeysDiv()
{
var divs = document.getElementsByTagName("div");
if(divs)
{
for(var i = 0; i < divs.length; i++)
{
var div = divs[i];
if(div.className.toString().indexOf("numberKeys", 0) > -1)
{
return div;
}
}
}
}
這個函數并不是直接通過<div>的id找到這個<div>,而是通過<div>的class屬性,這樣可能更靈活,因為如果通過id找<div>,就必須要求美工必須將這個<div>命名為numberKeys,而如果通過查找包含numberKeys的class屬性的<div>會對美工的限制更少。因為,只有這個<div>才會使用css中的numberKeys。
getRandomColor方法
這個方法獲得了一個隨機的演示,返回了字符串類型,格式#FFFFFF。實現代碼如下:
function getRandomArbitary(min, max)
{
return Math.round(Math.random() * (max - min) + min);
}
function getRandomColor()
{
var red = getRandomArbitary(0, 255).toString(16);
var green = getRandomArbitary(0, 255).toString(16);
var blue = getRandomArbitary(0, 255).toString(16);
if(red.length == 1) red = "0" + red;
if(green.length == 1) green = "0" + green;
if(blue.length == 1) blue = "0" + blue;
return "#" + red.toString(16) + green.toString(16) + blue.toString(16);
}
getRandomColor通過getRandomArbitary函數獲得了三個十進制的隨機數(0-255),并將其轉換為16進制,并返回最終結果。
buttonOnClick函數
這個函數是<div>標簽的onclick事件函數,實現代碼如下:
function buttonOnClick()
{
var text = document.getElementById("numbers");
if(typeof this.innerText == "#ff0000")
text.value = text.value + this.textContent;
else
text.value = text.value + this.innerText;
this.style.backgroundColor = getRandomColor();
this.style.color = getRandomColor();
}
這個函數實現很簡單,它的功能是將相應<div>標簽中的數字追加到numbers文本框中。只是考慮了firefox和ie的不同。在firefox中,獲得<div>中的文本要使用textContext,而在ie中要使用innerText。最后再將當前點擊的<div>和數字的顏色再次變換。
到現在為止,還有兩個事件函數代碼沒有給出,這兩個事件函數是onKeyup和stopAndStartTimer。
onKeyup函數
當numbers文本框輸入一個字符后,發生這個事件,實現代碼如下:
function onKeyup()
{
var value = this.value;
if(value.length == 0) return;
var i = value.toString().substr(value.length - 1, 1);
if(isNaN(i) == false)
{
var div = getNumberKeysDiv();
if(div)
{
var button = div.childNodes[i];
button.style.backgroundColor = getRandomColor();
button.style.color = getRandomColor();
}
}
}
這個函數的實現代碼也很簡單。只是根據用戶在文本框中輸入的數字來找到相應的<div>標簽,并再次隨機變換<div>背景和數字的顏色。
stopAndStartTimer函數
這個函數是用來控制定時器的,如果啟動定時器,瀏覽器會每隔3秒重新使<div>隨機變化一次顏色。實現代碼如下:
var time;
function stopAndStartTimer()
{
if(this.value.toString().indexOf("停止",0) > -1)
{
this.value = "開始變換顏色";
clearInterval(time);
}
else
{
this.value = "停止變換顏色";
time = setInterval(onLoad, 3000);
}
}
從上面給出的代碼可以看出,在View層,除了<script>引用了一個javascript文件外,并未涉及任何和邏輯有關的代碼。而設計界面的美工也并不知道開發人員會為<div>及其他的按鈕和文本框添加什么動作。而美工要做的是調整和界面有關的東西,如顏色,位置,分割等。只要使用<script>引用了這個js文件,就可以很容易加入相應的動作,而要將這些動作去掉,刪除或注釋掉這個<script>標簽即可。
尤其要提一下<div>標簽,美工在設計界面時可以向這個<div>標簽添加任何子元素。而在加入addevents.js后,程序會自動將由美工向<div>標簽中的加入的內容都刪除,而加入由業務邏輯需要的元素。這樣美工用來設計界面的元素就不會影響開發人員需要加入的和業務邏輯有關的元素了。
根據上面的代碼不難看出,numberKeys.html屬于視圖層,所有的事件函數屬性模型層,而其他的javascript代碼都屬性控制器(Controller)。