篇首語:這將是一系列文章,按心情更新,從做坊開始,簡述在前端開發中遇到的一系列問題,以及爲了解決相似問題而衍生出的設計模式,看看這些模式是如何解決咱們遇到的難題以及相應的好處和不足。html
在一個傳統的做坊模式下的應用,大概是這樣的。前端
需求:一個TodoList。設計模式
代碼大體是以下,具體結果能夠參照我建立的JsFiddle http://jsfiddle.net/xykhzwq8/服務器
var ndContent = $("#content"); var ndList = $("#todoList"); // 增長一個item $("#add").click(function(){ var content = ndContent.val(); $(['<li class="item">', '<span class="item-content">', content, '</span>', '<button class="del">x</button>', '</li>' ].join('') ).appendTo(ndList); }); // 刪除一個item ndList.on('click', '.del', function(){ $(this).parents('.item').remove(); });
這是極簡單的,並且也看不到做坊模式的很差的地方。相反,它看上去很易懂,很直接,咱們都很喜歡。app
可是因爲它實在太簡單,知足不了用戶的需求,用戶但願能夠:dom
因而咱們開始修改Dom結構,大體以下:ide
<li class="item"> <input type="checkbox" class="check"/> <span> {content} </span> <button class="del">x</button> </li>
以及一個顯示總數量,已完成數量, 未完成數量的統計框。測試
<div id="stat"> <span>已完成: <i class="finished">{finished}</i></span> <span>未完成: <i class="unfinished">{unfinished}</i></span> <span>總量: <i class="total">{total}</i></span> </div>
新增了一個checkbox
用於讓用戶勾選,同時JS代碼擴展以下:
http://jsfiddle.net/b7s2z2jw/2/網站
ndList.on("click", ".check", function(){ updateStat(); }); function updateStat(){ var finished = 0; var totalCount = 0; var unfinished = 0 ndList.find('.check').each(function(index, checkbox) { totalCount++; if (checkbox.checked) finished++; }); unfinished = totalCount - finished; $(".finished").html(finished); $(".unfinished").html(unfinished); $(".total").html(totalCount); }
依然很是容易實現。但注意了,咱們增長了一個updateStat
的方法,而且須要在四個地方顯調用它:this
除了點擊勾選
是新增的需求外,其他三項均是原有的代碼和邏輯。這裏展現了做坊模式的一個問題是:
當有新業務(本例是統計數量)進入時,須要修改已有的業務(啓動,增長,刪除)的代碼
以上是咱們遇到的第一個問題。注意,在這個例子中或許還看不出太大的不便,當一個應用有着更多的交互和數據時:
許多用戶但願:
因而咱們遇到了不一樣UI塊間交互的問題,一個簡陋的解決方式是這樣的:
首先升級一下item節點的內容,增長一個edit
的按鈕:
<li class="item"> <input type="checkbox" class="check"/> <span class="item-content"> tv </span> <button class="del">x</button> <button class="edit">edit</button> </li>
接下來處理交互,代碼大體爲:
http://jsfiddle.net/dht27Lqe/2/
ndList.on('click', '.edit', function(){ var item = $(this).parents('.item').find('.item-content'); var content = item.html(); ndMain.hide(); $("#edit-dialog .content").val(content); $("#edit-dialog").show(); $("#edit-dialog .ok").one('click', function(){ var content = $("#edit-dialog .content").val(); item.html(content); $("#edit-dialog").hide(); ndMain.show(); }); }); $("#edit-dialog .cancel").click(function(){ ndMain.show(); $("#edit-dialog").hide(); });
上述代碼有點醜陋,可是能工做,即使是在做坊模式下,咱們也能夠將edit-dialog
封裝代組件以減小對其的dom操做,如:
ndList.on('click', '.edit', function(){ var item = $(this).parents('.item').find('.item-content'); var content = item.html(); $dialog.open({ content: content, onOk: function (content) { item.html(content); } }); });
可是在上面兩個代碼片斷中,咱們都能看到一段無比醜陋的代碼:
var item = $(this).parents('.item').find('.item-content');
它的做用是,根據當前點擊的edit
按鈕,獲取其對應item
的內容。這段代碼強耦合了HTML的片斷,其帶來的災難是另每一個前端在開發業務時都極爲頭疼:
當UI變化時,如PM但願改變item的外觀,不少時候會無可避免地改變一個item
的HTML,致使你:
這即是咱們遇到的第二個問題:
業務邏輯與HTML結構強耦合,修改其中之一,必須當心翼翼地調試看上去與其徹底不相干的另外一個。(不少狀況下你甚至找不到另外一個在哪...
分離展現和交互彷佛成了笑話,由於它帶來更大的不肯定性和不穩定的依賴耦合。
公司業務增加了,TodoList將全面升級,針對不一樣用戶推出了以下不一樣的功能:
全選
按鈕,將全部事務設置爲已讀對於全選按鈕,其HTML爲:
<input type='checkbox' class='checkall' />
這段HTML僅在用戶是高級用戶時,纔會從服務器渲染下來。因而在JS中須要:
代碼以下:
var ndCheckAll = $('.checkall'); // 若是這個節點存在 if (ndCheckAll[0]) { ndCheckAll.click(function(){ // 將全部的checkbox設爲true ndList.find('item .check').attr("checked",true); // 更新底部的統計狀態 updateStat(); }); } // 修改點擊每一個item的checkbox的代碼 ndList.on('.check', 'click', function(){ // 保留原來的代碼 ..... // 新增的代碼 // 1. 若是點擊後,列表狀態不是全選,那麼將.checkedAll設爲false // 2. 若是點擊後,列表狀態爲全選,那麼將.checkedAll設爲true // 3. 還須要判斷.checkedAll這個節點是否存在 });
上面的代碼存在的問題是:
updateStat
,它本不該該關注這個的。全選
業務代碼中調用updateStat
是什麼意思?每新增一個需求,都要對已有的狀況有徹底掌控,這負擔是極重的。這種狀況存在很廣泛,以電商網站下單爲例,一個訂單根據其不一樣屬性在下單時可能須要展現:
這些都將產生不一樣形態的組合和依賴,改變任何一段的信息,都將可能改變其它地方的展現。
做坊模式存在的問題:
在實際的做坊工程中,因爲人們習慣於操做DOM,還可能遇到以下坑爹的問題: