篇首语:这将是一系列文章,按心情更新,从做坊开始,简述在前端开发中遇到的一系列问题,以及为了解决相似问题而衍生出的设计模式,看看这些模式是如何解决咱们遇到的难题以及相应的好处和不足。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,还可能遇到以下坑爹的问题: