Knockout : 实现复杂的web聊天窗体
公司以前一个同事写的这个聊天的窗体,由于是采用了html拼接的方式,外加处理的时候没有合理的划分职责,导致页面js代码量非常庞大(1500行左右)。现在这哥们离职了,苦的是我们剩下的人,不多说,我先去擦把泪。
公司基本没周都要求给web聊天加各种各样的功能,然后各种不能忍,权衡之后只能选择重构代码,可怜的娃娃啊。。。。
本次完全采用Knockout重构,基础的东西就不多说了,学习Knockout请移步大叔的blog(多谢大叔无私分享) http://www.cnblogs.com/TomXu/archive/2011/11/21/2257154.html
上web聊天界面截图:
view代码:
<!doctype html> <html> <head> <meta charset="utf-8"> <title>后台聊天</title> <link rel="stylesheet" href="css/jquery.qqFace-min.css" /> <link rel="stylesheet" href="css/index0923.css" /> <style type="text/css"> /* style="display:none;" */ </style> </head> <body> <div class="chatDiv" id="chatDiv" style="border:1px solid #ededed;"> <!-- 单聊 --> <div id="singleChat"> <!-- 聊天联系人列表 --> <div class="chatLp"> <!--div class="title"><a href="###" class="clearAll">清空</a></div--> <div class="title"> <a href="###" class="sendAll l">群发</a> <a href="javascript:void(0);" class="delete r" data-bind="click:function(){viewModel.clearArray();}"></a> </div> <ul class="chatList" data-bind="template:{ name: 'demo',foreach:viewModel.viewModelArray}"> <!--<li><i class="ico icoOne"></i><i class="come"></i><span class="txt">八卦台湾</span></li> <li class="select"><i class="ico icoOne"></i><span class="txt">八卦台湾</span></li> <li><img class="ico" src="images/img.png"/><span class="txt">八卦台湾</span></li>--> </ul> </div> <div class="chatRp"> <div class="chatName" data-bind="template:{ name: 'title',foreach:viewModel.selectViewModel}"> </div> <!-- 对话内容 --> <div class="content"> <div id="chatMain" class="chatMain" style="overflow-y: scroll;" tabindex="5001" data-bind="template:{ name: 'content',foreach:viewModel.selectModel}"> </div> <div class="speakInp"> <span class="spaeakBtn biaoq"></span> <span class="spaeakBtn edit"></span> <textarea class="textarea-txt" style="overflow: hidden;" data-bind="value:textContent"></textarea> <a class="sub_btn" href="javascript:void(0);" data-bind="click:function(){viewModel.msgPush();}">发送</a> </div> </div> </div> <div class="clearB"></div> </div> </div> <div> <button data-bind="click:add">Add</button> </div> <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> <script type="text/javascript" src="js/jquery.qqFace-min.js"></script> <script type="text/javascript" src="js/jquery.nicescroll.min.js"></script> <script src="js/jquery.tmpl.js"></script> <script src="js/knockout-2.0.0.debug.js"></script> <script type="text/x-jquery-tmpl" id="demo"> <li data-bind="click:function() {viewModel.select($data)},css:{select:$data.data()==viewModel.selectModel()}, event: { mouseover:function(){ viewModel.showCloseIco($data,true);}, mouseout: function(){ viewModel.showCloseIco($data,false);} }"> <img class="ico" data-bind="css:{icoOne:$data.data().length>0}" src="${ico}"></img> <span class="txt">${name}</span> <i class="icodelete" data-bind="click:function () {viewModel.remove($data)},style:{display:$data.isShow()== true?'inline':'none'}"></i> </li> </script> <script type="text/x-jquery-tmpl" id="content"> <div class="anotherIetm" data-bind="css:{selfIetm:$data.msgtype }"> <div class="icoP"><img src="${ico}" class="ico"></div> <div class="contentP"> <span class="name">${name}</span> <div class="speakCon"> <span class="jiao"></span> <div class="mainCont">${msg}</div> <div class="clearB"></div> </div> </div> <div class="clearB"></div> </div> </script> <script type="text/x-jquery-tmpl" id="title"> <span class="left">${name}</span> <span class="right"> <span>您的身份:[${serverId}]</span> </span> <div class="clearB"></div> </script> <script src="ChatModel.js"></script> <script src="ChatViewModel.js"></script> </body> </html>
Model代码:
/// <reference path="js/knockout-2.0.0.debug.js" /> function LeftObject(ico, name, serverid) { this.ico = ico; this.name = name; this.isShow = ko.observable(); this.data = ko.observableArray(); this.serverId = serverid; } function MsgContent(msgtype, msg, name, ico) { this.msgtype = msgtype; this.msg = msg; this.name = name; this.ico = ico; }
ViewModel代码:
/// <reference path="js/knockout-2.0.0.debug.js" /> //======================左边用户列表操作=========================== var num = 0; var viewModel = { add: function () { num = num + 1; viewModel.viewModelArray.push(new LeftObject("http://www.baidu.com/img/bdlogo.png", "Test" + num,"客服-应用主")); if (viewModel.selectModel().length == 0) { viewModel.selectModel(viewModel.viewModelArray()[viewModel.viewModelArray().length - 1].data()); viewModel.selectViewModel(viewModel.viewModelArray()[viewModel.viewModelArray().length - 1]); } }, push: function () { if (viewModel.selectModel()) { viewModel.selectModel.push({ content: num, isSend: (num % 2 == 1) }) } }, viewModelArray: ko.observableArray() }; viewModel.remove = function (e) { viewModel.viewModelArray.remove(e); if (viewModel.selectModel() == e.data()) { viewModel.selectModel([]); } viewModel.selectModel(viewModel.viewModelArray()[0].data()); viewModel.selectViewModel(viewModel.viewModelArray()[0]); } viewModel.select = function (e) { viewModel.selectModel(e.data()); viewModel.selectViewModel(e); } viewModel.selectModel = ko.observableArray(); viewModel.selectViewModel = ko.observable(); viewModel.showCloseIco = function (e,isShow) { e.isShow(isShow); //alert(e.isShow()); } viewModel.clearArray = function () { viewModel.viewModelArray([]); } //===========================分割线 下面是聊天窗口操作函数=========================================== viewModel.msgAccept = function (data) { viewModel.selectModel.push(new MsgContent(true, data, "自己", "images/ico.png")); } viewModel.msgSend = function (data) { viewModel.selectModel.push(new MsgContent(false, data, "别人", "images/img.png")); } viewModel.textContent = ko.observable(); //模拟发送 viewModel.msgPush = function (data) { if (viewModel.viewModelArray().length == 0) { return; } var d = parseInt( Math.random()*10 % 2); data = viewModel.textContent(); viewModel.textContent(''); if (d == 0) { viewModel.msgSend(data); } else { viewModel.msgAccept(data); } } //===================================================================== ko.applyBindings(viewModel);