前面我們分三章來說明了使用django template與jquery的差別,通過jquery如何來實現前后端的分離,同時再9章節使用vue.js 我們淺嘗輒止的介紹了JQuery到vue的切換,由於監控界面沒有數據提交,無法很好的體現處我說的vue的優勢,所以筆者增加本章節來進一步的對比兩者異同點(沒有對比就沒有傷害),讀者可以通過代碼去體會vue的優勢。vue作為精簡版本的MVVM,完成雙向綁定后我們編程的時候可以更專注於model本身,而不像JQuery還需要知道DOM元素的標簽去讀寫值。兩者都是前端腳本框架,實際項目中,有些時候我們會混合使用兩者的。
1.1. 重構模板tasks.html
說干就干,我們通過重構tasks.html模板切換到vue模式下來理解和對比兩者的差異吧,MVVM要點之一:MVVM 將數據雙向綁定(data-binding)作為核心思想,View 和 Model 之間沒有聯系,它們通過 ViewModel 這個橋梁進行交互。
<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title>任務列表</title> <link href="https://cdn.bootcss.com/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> </head> <body> <div id="content-main" class="container"> <div><a href='0/change/'>新增</a></div> {% csrf_token %} <table id="id_task_table"> <tr> <th>ID</th> <th>任務號</th> <th>源地址</th> <th>目標地址</th> <th>條碼</th> <th>狀態</th> <th>優先級</th> <th>開始時間</th> <th>結束時間</th> <th>作業數量</th> <th>操作</th> </tr> <tr v-for="task in taskList"> <!--①--> <td>[[task.TaskId]]</td> <td><a :href="task.TaskId + '/view/'">[[task.TaskNum]]</a></td> <td>[[task.Source]]</td> <td>[[task.Target]]</td> <td>[[task.Barcode]]</td> <td>[[task.State]]</td> <td>[[task.Priority]]</td> <td>[[task.BeginDate]]</td> <td>[[task.EndDate]]</td> <td>[[task.SystemDate]]</td> <td>[[task.JobCount]]</td> <td>[[task.JobCount]]</td> <td><a :id="task.TaskId+'-decompose'" :href="task.TaskId+'/decompose/'">分解</a> <a :id="task.TaskId+'-start'" :href="task.TaskId+'/start/'">下達</a> <a :id="task.TaskId+'-change'" :href="task.TaskId+'/change/'">修改</a> <a :id="task.TaskId+'-delete'" href='#' @click="taskDel(task.TaskId)">刪除</a></td> </tr> </table> </div> <script> var vm = new Vue({ el: '#content-main', data: { taskList: [], }, delimiters: ['[[', ']]'], mounted() { this.getData() }, methods: { getData: function () { _this = this //本例使用ajax實現vue的異步請求 $.ajax({ url: "/task/taskGetList/", success: function (result) { d = result _this.taskList = d.rows //② //for (const task of d.rows) { // var row = ""; // row += "<tr>"; // row += "<td>" + task.TaskId + "</td>"; // row += "<td><a href='" + task.TaskId + "/view/'>" + task.TaskNum + "</a></td>";//① // row += "<td>" + task.Source + "</td>"; // row += "<td>" + task.Target + "</td>"; // row += "<td>" + task.Barcode + "</td>"; // row += "<td>" + task.State + "</td>"; // row += "<td>" + task.Priority + "</td>"; // row += "<td>" + (task.BeginDate ? task.BeginDate : '-') + "</td>"; // row += "<td>" + (task.EndDate ? task.EndDate : '-') + "</td>"; // row += "<td>" + task.SystemDate + "</td>"; // row += "<td>" + task.JobCount + "</td>"; // row += "<td><a id='" + task.TaskId + "-decompose' href='" + task.TaskId + "/decompose/'>分解</a> <a id='" + task.TaskId + "-start' href='" + task.TaskId + "/start/'>下達</a>" // + (task.State == '未處理' ? " <a id='" + task.TaskId + "-change' href='" + task.TaskId + "/change/'>修改</a>" : "") // + " <a id='" + task.TaskId + "-delete' href='#' onclick=taskDel(" + task.TaskId + ")>刪除</a></td>"; //② // row += "</tr>"; // $("#id_task_table tbody").append(row); //} } }); }, taskDel: function (pk) { //異步從后台獲得值 url_str = "/task/taskDel/" data = { pk: pk, csrfmiddlewaretoken: $('[name="csrfmiddlewaretoken"]').val() } $.ajax({ type: 'POST', url: url_str, data: data, success: function (result) { //alert('HEHE') window.location.replace("/task/"); } }); }, }, }); </script> </body> </html>
標注①:原來jquery寫DOM標簽的賦值操作改成了vue的循環模板綁定;標注②:ajax異步請求獲得的數據集直接賦值給vue model層即可,vue MVVM會感知數據變化而把數據同步渲染到view層。
上代碼如上菜,相對於一大堆原理圖,閱讀代碼、比較代碼如直接下筷!自行品味vue是否可口可樂!於筆者這個改變確實爽點很多!
1.1.1. 運行結果
代碼改動不是很大的情況下,我們任務列表不知不覺中就遷移到MVVM模式下了,剛開始使用vue的時候沒注意,后來vue是國人寫的,那個眼睛瞪大了很久,只要寫代碼也能基本衣食無憂,我們還是能出牛人的嘛。哎,那幫踢球的不早就衣食無憂了!?
1.2. 重構模板taskChange.html
上面小節我們見識了Vue的model層變化后數據自動同步到view的方向,雙向綁定的另一個面就是: View 的變化會自動同步到 Model,當用戶操作 View,ViewModel 感知到變化,然后自動更新 Model層數據,我們提交數據到后台直接提交model層即可。下面我們來改造taskChange.html支持vue吧。
<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title></title> <link href="https://cdn.bootcss.com/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="content-main" class="container"> <h1>任務詳情</h1> <div class="row"><div><b>對象標識:</b></div><div id="task_id">{{pk}}</div></div> <form method="post" id="edit_form"> <div class="row"><div><b>任務號:</b></div><input v-bind:disabled="pk>0" v-model="model.TaskNum" /></div> <!--①--> <div class="row"><div><b>源地址:</b></div><input v-model="model.Source"/></div> <div class="row"><div><b>目標地址:</b></div><input v-model="model.Target" /></div> <div class="row"><div><b>條碼:</b></div><input v-bind:disabled="pk>0" v-model="model.Barcode"/><input type="button" value="提交" @click="saveData()"></div> {% csrf_token %} </form> <div class="row"><div><b>任務狀態:</b></div><div id="state">[[model.State]]</div></div> <div class="row"><div><b>優先級:</b></div><div id="priority">[[model.Priority]]</div></div> <div class="row"><div><b>開始時間:</b></div><div id="begin_date">[[model.BeginDate]]</div></div> <div class="row"><div><b>結束時間:</b></div><div id="end_date">[[model.EndDate]]</div></div> <div class="row"><div><b>作業數量:</b></div><div id="job_count">[[model.JobCount]]</div></div> </div> <script> var vm = new Vue({ el: '#content-main', data: { model: {}, pk: parseInt($('#task_id').text()), }, delimiters: ['[[', ']]'], mounted() { this.getData() }, methods: { getData: function () { _this = this url_str = "/task/taskGet/" + this.pk + '/' $.ajax({ url: url_str, success: function (result) { _this.model = result.model // ③ 注意不在jquery寫元素值 //if (task.pk) { 修改 // $('#id_task_num').removeAttr('disabled') // $('#id_barcode').removeAttr('disabled') //} else { // 新增 // $('#id_task_num').attr('disabled',true) // $('#id_barcode').attr('disabled',true) //} //$('#id_source').val(task.Source) //$('#id_target').val(task.Target) //$('#id_task_num').val(task.TaskNum) //$('#id_barcode').val(task.Barcode) //$('#state').text(task.State) //$('#priority').text(task.Priority) //$('#begin_date').text(task.BeginDate) //$('#end_date').text(task.EndDate) //$('#job_count').text(task.JobCount) } }); }, taskDel: function (pk) { url_str = "/task/taskDel/" data = { pk: pk, csrfmiddlewaretoken: $('[name="csrfmiddlewaretoken"]').val() } $.ajax({ type: 'POST', url: url_str, data: data, success: function (result) { window.location.replace("/task/"); } }); }, saveData:function() { //異步提交數據到后台 url_str = "/task/taskSave/" + this.pk + '/' //data = { // source: $('#id_source').val(), // target: $('#id_target').val(), // csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val() //} //if ($('#task_id').text() <= 0) // data.taskNum = $('#id_task_num').val() // data.barcode = $('#id_barcode').val() // ④ 不再判斷處理直接通過model獲取提交后台值 data = { source: this.model.Source, target: this.model.Target, taskNum: this.model.TaskNum, barcode: this.model.Barcode, csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val() } $.ajax({ type: 'POST', url: url_str, data: data, success: function (result) { window.location.replace("/task/"); } }); }, }, }); </script> </body> </html>
標注①:原來jquery寫DOM標簽的賦值操作改成了vue v-model綁定,v-bind:disabled等等;標注②:獲取通過django模板傳過來的對象標識; 標注③:ajax異步請求獲得的數據集直接賦值給vue model層,MVVM會感知數據變化而把數據同步渲染到view層。;標注④:提交數據到后台直接操作model值,后面會進一步優化提交方法。
數據提交到后台同樣也不在關注DOM上的元素和標簽,直接操作model層即可,項目的實戰中,為了簡化交互賦值操作,我們常常采用獲取的model修改屬性值后直接提交回去的方式,這樣就進一步簡化了代碼。
1.3. 簡化提交代碼
1.3.1. 前端代碼
saveData:function() {
//異步提交數據到后台
url_str = "/task/taskSave/" + this.pk + '/'
// ④ 不再判斷處理直接通過model獲取提交后台值
//data = {
// source: this.model.Source, target: this.model.Target, taskNum: this.model.TaskNum,
// barcode: this.model.Barcode, csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
//}
data = this.model
data.csrfmiddlewaretoken=$("[name='csrfmiddlewaretoken']").val()
$.ajax({
type: 'POST', url: url_str, data: data, success: function (result) {
window.location.replace("/task/");
}
});
},
運行修改任務點擊提交,調試窗口會提示后台服務器錯誤!
1.3.2. 后端代碼
錯誤的原因主要是因為,model屬性大小寫的問題,我們進一步重構后台代碼來支持這一提交模式的變更。
def taskSave(request,pk): if int(pk) > 0: data={"Source":request.POST['Source'],"Target":request.POST['Target']} model = Task.objects.filter(pk=pk).update(**data) else: data={"Source":request.POST['Source'],"Target":request.POST['Target'],"Barcode":request.POST['Barcode'],\ "TaskNum":request.POST['TaskNum'], "State":1,"Priority":1,} model=Task.objects.create(**data) data={'total':1,'success':True} return JsonResponse(data)
簡單的調整后端獲取參數大小寫,前端技術棧切換到vue了,采用vue我們大大的簡化了提交數據的操作,由於MVVM模式實現UI端的業務邏輯分層,model與view就聚焦專注相應的功能領域,大大的提高了開發和維護效率。高內聚、低耦合這個開發的底層邏輯應該是我們日常開發過程需要構建的核心的思維模型!
1.4. 小結
本章節vue vs jquery給我們總體的的印象也是好像改動不大嘛,咋個就切換技術棧了,當然沒有完全說jquery就不用了,實際項目考慮的方面會更多,團隊技術慣性,團隊開發、協作和維護。很多時候混用的情況,但無論如何,代碼寫的簡潔又能搞定功能,應該是工程師的內功之一吧。簡單又好用,這點是vue成功的地方,也是該向vue的作者學習地方。