亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

BACKBONE.JS 簡單入門范例

2019-11-19 15:09:05
字體:
來源:轉載
供稿:網友

11年剛開始用前端MVC框架時寫過一篇文章,當時Knockout和Backbone都在用,但之后的項目全是在用Backbone,主要因為它簡單、靈活,無論是富JS應用還是企業網站都用得上。相比React針對View和單向數據流的設計,Backbone更能體現MVC的思路,所以針對它寫一篇入門范例,說明如下:

1. 結構上分4節,介紹Model/View/Collection,實現從遠程獲取數據顯示到表格且修改刪除;
2. 名為“范例”,所以代碼為主,每節的第1段代碼都是完整代碼,復制粘貼就能用,每段代碼都是基于前一段代碼來寫的,因此每段代碼的新內容不會超過20行(大括號計算在內);
3. 每行代碼沒有注釋,但重要內容之后有寫具體的說明;
4. 開發環境是Chrome,使用github的API,這樣用Chrome即使在本地路徑(形如file://的路徑)也能獲取數據。

0. Introduction

幾乎所有的框架都是在做兩件事:一是幫你把代碼寫在正確的地方;二是幫你做一些臟活累活。Backbone實現一種清晰的MVC代碼結構,解決了數據模型和視圖映射的問題。雖然所有JS相關的項目都可以用,但Backbone最適合的還是這樣一種場景:需要用JS生成大量的頁面內容(HTML為主),用戶跟頁面元素有很多的交互行為。

Backbone對象有5個重要的函數,Model/Collection/View/Router/History。Router和History是針對Web應用程序的優化,建議先熟悉pushState的相關知識。入門階段可以只了解Model/Collection/View。將Model視為核心,Collection是Model的集合,View是為了實現Model的改動在前端的反映。

1. Model

Model是所有JS應用的核心,很多Backbone教程喜歡從View開始講,其實View的內容不多,而且理解了View意義不大,理解Model更重要。以下代碼實現從github的API獲取一條gist信息,顯示到頁面上:

<!DOCTYPE html><html><head><script type="text/javascript" src="https://code.jquery.com/jquery-1.11.1.js"></script><script type="text/javascript" src="http://underscorejs.org/underscore-min.js"></script><script type="text/javascript" src="http://backbonejs.org/backbone-min.js"></script><link  rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="stylesheet"></head><body> <table id="js-id-gists" class="table">  <thead><th>description</th><th>URL</th><th>created_at</th></thead>  <tbody></tbody> </table> <script type="text/javascript"> var Gist = Backbone.Model.extend({  url: 'https://api.github.com/gists/public',  parse: function (response) {   return (response[0]);  } }),  gist = new Gist(); gist.on('change', function (model) {  var tbody = document.getElementById('js-id-gists').children[1],   tr = document.getElementById(model.get('id'));  if (!tr) {   tr = document.createElement('tr');   tr.setAttribute('id', model.get('id'));  }  tr.innerHTML = '<td>' + model.get('description') + '</td><td>' + model.get('url') + '</td><td>' + model.get('created_at') + '</td>';  tbody.appendChild(tr); }); gist.fetch(); </script></body></html>

LINE4~8: 加載要用到的JS庫。ajax請求和部分View的功能需要jQuery支持(或者重寫ajax/View的功能);Backbone的代碼是基于Underscore寫的(或者用Lo-Dash代替);加載bootstrap.css只是因為默認樣式太難看…

LINE16~22: 創建一個Model并實例化。url是數據源(API接口)的地址,parse用來處理返回的數據。實際返回的是一個Array,這里取第一個Object。

LINE24~33: 綁定change事件。還沒有使用View,所以要自己處理HTML。這10行代碼主要是get的用法(model.get),其他的功能之后會用View來實現。

LINE34: 執行fetch。從遠程獲取數據,獲到數據后會觸發change事件。可以重寫sync方法

打開Chrome的Console,輸入gist,可以看到Model獲得的屬性:

Model提供數據和與數據相關的邏輯。上圖輸出的屬性是數據,代碼中的fetch/parse/get/set都是對數據進行操作,其他的還有escape/unset/clear/destory,從函數名字就大致可以明白它的用途。還有很常用的validate函數,在set/save操作時用來做數據驗證,驗證失敗會觸發invalid事件:

/* 替換之前代碼的JS部分(LINE16~34) */ var Gist = Backbone.Model.extend({  url: 'https://api.github.com/gists/public',  parse: function (response) {   return (response[0]);  },  defaults: {   website: 'dmyz'  },  validate: function (attrs) {   if (attrs.website == 'dmyz') {    return 'Website Error';   }  } }),  gist = new Gist(); gist.on('invalid', function (model, error) {  alert(error); }); gist.on('change', function (model) {  var tbody = document.getElementById('js-id-gists').children[1],   tr = document.getElementById(model.get('id'));  if (!tr) {   tr = document.createElement('tr');   tr.setAttribute('id', model.get('id'));  }  tr.innerHTML = '<td>'+ model.get('description') +'</td><td>'+ model.get('url') +'</td><td>'+ model.get('created_at') +'</td>';  tbody.appendChild(tr); }); gist.save();

跟之前的代碼比較,有4處改動:

LINE7~9: 增加了defaults。如果屬性中沒有website(注意不是website值為空),會設置website值為dmyz。
LINE10~14: 增加validate函數。當website值為dmyz時,觸發invalid事件。
LINE18~20: 綁定invalid事件,alert返回的錯誤。
LINE31: 不做fetch,直接save操作。

因為沒有fetch,所以頁面上不會顯示數據。執行save操作,會調用validate函數,驗證失敗會觸發invalid事件,alert出錯誤提示。同時save操作也會向Model的URL發起一個PUT請求,github這個API沒有處理PUT,所以會返回404錯誤。

在Console中輸入gist.set(‘description', ‘demo'),可以看到頁面元素也會有相應的變化。執行gist.set(‘description', gist.previous(‘description'))恢復之前的值。這就是Model和View的映射,現在還是自己實現的,下一節會用Backbone的View來實現。

2. View

用Backbone的View來改寫之前代碼LINE24~33部分:

<!DOCTYPE html><html><head><script type="text/javascript" src="https://code.jquery.com/jquery-1.11.1.js"></script><script type="text/javascript" src="http://underscorejs.org/underscore-min.js"></script><script type="text/javascript" src="http://backbonejs.org/backbone-min.js"></script><link  rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="stylesheet"></head><body> <table id="js-id-gists" class="table">  <thead><th>description</th><th>URL</th><th>created_at</th><th></th></thead>  <tbody></tbody> </table> <script type="text/javascript"> var Gist = Backbone.Model.extend({  url: 'https://api.github.com/gists/public',  parse: function (response) {   return response[0];  } }),  gist = new Gist(); var GistRow = Backbone.View.extend({  el: 'tbody',  MODEL: gist,  events: {   'click a': 'replaceURL'  },  replaceURL: function () {   this.MODEL.set('url', 'http://dmyz.org');  },  initialize: function () {   this.listenTo(this.MODEL, 'change', this.render);  },  render: function () {   var model = this.MODEL,    tr = document.createElement('tr');   tr.innerHTML = '<td>' + model.get('description') + '</td><td>' + model.get('url') + '</td><td>' + model.get('created_at') + '</td><td><a href="javascript:void(0)" rel="external nofollow" rel="external nofollow" rel="external nofollow" >®</a></td>';   this.el.innerHTML = tr.outerHTML;   return this;  } }); var tr = new GistRow(); gist.fetch(); </script></body></html>

LINE25: 所有的View都是基于DOM的,指定el會選擇頁面的元素,指定tagName會創建相應的DOM,如果都沒有指定會是一個空的div。
LINE27~32: 綁定click事件到a標簽,replaceURL函數會修改(set)url屬性的值。
LINE33~35: View的初始化函數(initialize),監聽change事件,當Model數據更新時觸發render函數。
LINE36~42: render函數。主要是LINE41~42這兩行,把生成的HTML代碼寫到this.el,返回this。
LINE44: 實例化GistRow,初始化函數(initialize)會被執行。

點擊行末的a標簽,頁面顯示的這條記錄的URL會被修改成http://dmyz.org。

這個View名為GistRow,選擇的卻是tbody標簽,這顯然是不合理的。接下來更改JS代碼,顯示API返回的30條數據:

/* 替換之前代碼的JS部分(LINE16~45) */ var Gist = Backbone.Model.extend(),  Gists = Backbone.Model.extend({   url: 'https://api.github.com/gists/public',   parse: function (response) {    return response;   }  }),  gists = new Gists(); var GistRow = Backbone.View.extend({  tagName: 'tr',  render: function (object) {   var model = new Gist(object);   this.el.innerHTML = '<td>' + model.get('description') + '</td><td>'+ model.get('url') + '</td><td>' + model.get('created_at') + '</td><td></td>'   return this;  } }); var GistsView = Backbone.View.extend({  el: 'tbody',  model: gists,  initialize: function () {   this.listenTo(this.model, 'change', this.render);  },  render: function () {   var html = '';   _.forEach(this.model.attributes, function (object) {    var tr = new GistRow();    html += tr.render(object).el.outerHTML;   });   this.el.innerHTML = html;   return this;  } }); var gistsView = new GistsView(); gists.fetch();

LINE2~9: 創建了兩個Model(Gist和Gists),parse現在返回完整Array而不只是第一條。
LINE11~18: 創建一個tr。render方法會傳一個Object來實例化一個Gist的Model,再從這個Model里get需要的值。
LINE26~34: 遍歷Model中的所有屬性?,F在使用的是Model而不是Collection,所以遍歷出的是Object。forEach是Underscore的函數。

Backbone的View更多的是組織代碼的作用,它實際干的活很少。View的model屬性在本節第一段代碼用的是大寫,表明只是一個名字,并不是說給View傳一個Model它會替你完成什么,控制邏輯還是要自己寫。還有View中經常會用到的template函數,也是要自己定義的,具體結合哪種模板引擎來用就看自己的需求了。

這段代碼中的Gists比較難操作其中的每一個值,它其實應該是Gist的集合,這就是Backbone的Collection做的事了。

3. Collection

Collection是Model的集合,在這個Collection中的Model如果觸發了某個事件,可以在Collection中接收到并做處理。第2節的代碼用Collection實現:

<!DOCTYPE html><html><head><script type="text/javascript" src="https://code.jquery.com/jquery-1.11.1.js"></script><script type="text/javascript" src="http://underscorejs.org/underscore-min.js"></script><script type="text/javascript" src="http://backbonejs.org/backbone-min.js"></script><link  rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="stylesheet"></head><body> <table id="js-id-gists" class="table">  <thead><th>description</th><th>URL</th><th>created_at</th><th></th></thead>  <tbody></tbody> </table> <script type="text/javascript"> var Gist = Backbone.Model.extend(),  Gists = Backbone.Collection.extend({   model: Gist,   url: 'https://api.github.com/gists/public',   parse: function (response) {    return response;   }  }),  gists = new Gists(); var GistRow = Backbone.View.extend({  tagName: 'tr',  render: function (model) {   this.el.innerHTML = '<td>' + model.get('description') + '</td><td>'+ model.get('url') + '</td><td>' + model.get('created_at') + '</td><td></td>'   return this;  } }); var GistsView = Backbone.View.extend({  el: 'tbody',  collection: gists,  initialize: function () {   this.listenTo(this.collection, 'reset', this.render);  },  render: function () {   var html = '';   _.forEach(this.collection.models, function (model) {    var tr = new GistRow();    html += tr.render(model).el.outerHTML;   });   this.el.innerHTML = html;   return this;  } }); var gistsView = new GistsView(); gists.fetch({reset: true}); </script></body></html>

LINE17~23: 基本跟第2節的第2段代碼一樣。把Model改成Collection,指定Collection的Model,這樣Collectio獲得返回值會自動封裝成Model的Array。
LINE38: Collection和Model不同,獲取到數據也不會觸發事件,所以綁定一個reset事件,在之后的fetch操作中傳遞{reset: true}。
LINE42~45: 從Collection從遍歷Model,傳給GistRow這個View,生成HTML。

Collection是Backbone里功能最多的函數(雖然其中很多是Underscore的),而且只要理解了Model和View的關系,使用Collection不會有任何障礙。給Collection綁定各種事件來實現豐富的交互功能了,以下這段JS代碼會加入刪除/編輯的操作,可以在JSBIN上查看源代碼和執行結果。只是增加了事件,沒有什么新內容,所以就不做說明了,附上JSBIN的演示地址:http://jsbin.com/jevisopo/1

/* 替換之前代碼的JS部分(LINE16~51) */ var Gist = Backbone.Model.extend(),  Gists = Backbone.Collection.extend({   model: Gist,   url: 'https://api.github.com/gists/public',   parse: function (response) {    return response;   }  }),  gists = new Gists(); var GistRow = Backbone.View.extend({  tagName: 'tr',  render: function (model) {   this.el.id = model.cid;   this.el.innerHTML = '<td>' + model.get('description') + '</td><td>'+ model.get('url') + '</td><td>' + model.get('created_at') + '</td><td><a href="javascript:void(0)" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="js-remove">X</a> <a href="javascript:void(0)" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="js-edit">E</a> </td>'   return this;  } }); var GistsView = Backbone.View.extend({  el: 'tbody',  collection: gists,  events: {   'click a.js-remove': function (e) {    var cid = e.currentTarget.parentElement.parentElement.id;    gists.get(cid).destroy();    gists.remove(cid);   },   'click a.js-edit': 'editRow',   'blur td[contenteditable]': 'saveRow'  },  editRow: function (e) {   var tr = e.currentTarget.parentElement.parentElement,    i = 0;   while (i < 3) {    tr.children[i].setAttribute('contenteditable', true);    i++;   }  },  saveRow: function (e) {   var tr = e.currentTarget.parentElement,    model = gists.get(tr.id);   model.set({    'description' : tr.children[0].innerText,    'url': tr.children[1].innerText,    'created_at': tr.children[2].innerText   });   model.save();  },  initialize: function () {   var self = this;   _.forEach(['reset', 'remove', 'range'], function (e) {    self.listenTo(self.collection, e, self.render);   });  },  render: function () {   var html = '';   _.forEach(this.collection.models, function (model) {    var tr = new GistRow();    html += tr.render(model).el.outerHTML;   });   this.el.innerHTML = html;   return this;  } }); var gistsView = new GistsView(); gists.fetch({reset: true});

Afterword

雖然是入門范例,但因為篇幅有限,有些基本語言特征和Backbone的功能不可能面面俱到,如果還看不懂肯定是我漏掉了需要解釋的點,請(在Google之后)評論或是郵件告知。

Backbone不是jQuery插件,引入以后整個DOM立即實現增刪改查了,也做不到KnockoutJS/AnglarJS那樣,在DOM上做數據綁定就自動完成邏輯。它是將一些前端工作處理得更好更規范,如果學習前端MVC的目的是想輕松完成工作,Backbone可能不是最佳選擇。如果有一個項目,100多行HTML和1000多行JS,JS主要都在操作頁面DOM(如果討厭+號連接HTML還可以搭配React/JSX來寫),那就可以考慮用Backbone來重寫了,它比其他龐大的MVC框架要容易掌握得多,作為入門學習也是非常不錯的。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
97人洗澡人人免费公开视频碰碰碰| 日韩精品免费在线播放| 性亚洲最疯狂xxxx高清| 久久人体大胆视频| 国产成人精彩在线视频九色| 欧美激情免费视频| 日韩免费av在线| 国产精品国产福利国产秒拍| 日韩av片免费在线观看| 亚洲视频网站在线观看| 亚洲第一精品夜夜躁人人爽| 欧美精品久久久久| 欧美亚洲国产日本| 亚洲综合在线小说| 精品国产成人av| 黄色精品一区二区| 国产精品日韩在线观看| 久久影视电视剧凤归四时歌| 91麻豆国产精品| 欧美成人网在线| 热久久免费国产视频| 一区国产精品视频| 国产精品爽黄69天堂a| 久久久精品视频在线观看| 一本色道久久88亚洲综合88| 狠狠躁夜夜躁人人躁婷婷91| 亚洲成人激情在线| 国产欧美一区二区三区久久| 日韩风俗一区 二区| 欧美精品成人在线| 国产成人涩涩涩视频在线观看| 欧美第一黄网免费网站| 国产视频久久久| 欧美中文字幕视频在线观看| 亚洲bt天天射| 中文字幕亚洲天堂| 久久精品欧美视频| 91po在线观看91精品国产性色| 国产美女扒开尿口久久久| 亚洲图片欧洲图片av| 在线精品91av| 亚洲3p在线观看| 欧美激情精品久久久久久免费印度| 精品一区精品二区| 另类色图亚洲色图| 成人福利在线视频| 国产精品视频公开费视频| 日韩国产精品视频| 久久精品国产2020观看福利| 亚洲无av在线中文字幕| 全亚洲最色的网站在线观看| 久久综合久久美利坚合众国| 久久99精品久久久久久青青91| 欧美大片免费看| 国内精品久久久久影院优| 97久久超碰福利国产精品…| 亚洲精品理论电影| 亚洲理论电影网| 久久久黄色av| 日韩电影中文字幕在线| 国产a∨精品一区二区三区不卡| 成人中文字幕+乱码+中文字幕| 精品偷拍一区二区三区在线看| 国产美女精品免费电影| 国内精久久久久久久久久人| 国产精品九九九| 欧美日韩综合视频| 日韩国产高清污视频在线观看| 欧美成人一区在线| 97婷婷大伊香蕉精品视频| 久久久久久久成人| 欧美美最猛性xxxxxx| 亚洲a级在线播放观看| 亚洲福利在线播放| 久久久精品免费视频| 亚洲女人天堂av| 亚洲成人动漫在线播放| 亚洲xxxx视频| 日韩欧美在线观看| 激情亚洲一区二区三区四区| 国产精品嫩草影院一区二区| 久久久久中文字幕2018| 日韩av电影国产| 国产精品久久久久久久电影| 国产精品久久久久久影视| 欧洲亚洲免费视频| 亚洲精品美女久久久久| 欧美高清视频一区二区| 日韩欧美在线视频| 国产精品久久久久久久久久久久| 九九热精品视频国产| www.xxxx精品| 久久艳片www.17c.com| 亚洲第一精品电影| 热99精品只有里视频精品| 一区二区三区国产在线观看| 国产精品日韩一区| 国产欧美精品va在线观看| 国产精品www网站| 中文字幕在线观看亚洲| 欧亚精品在线观看| 精品亚洲夜色av98在线观看| 亚洲码在线观看| 亚洲国产中文字幕在线观看| 国产精品国产福利国产秒拍| 性欧美在线看片a免费观看| 国产主播精品在线| 国产自产女人91一区在线观看| 日韩精品极品在线观看| 国产91成人video| 91精品国产99久久久久久| 久久久亚洲欧洲日产国码aⅴ| 欧美孕妇与黑人孕交| 色播久久人人爽人人爽人人片视av| 精品国产区一区二区三区在线观看| 国产精品日日摸夜夜添夜夜av| 中文字幕国产精品久久| 欧美二区乱c黑人| 日韩精品在线电影| 日韩一级黄色av| 欧美午夜精品久久久久久浪潮| 久久99亚洲热视| 亚洲人午夜精品免费| 国产精品观看在线亚洲人成网| 日本道色综合久久影院| 亚洲欧洲偷拍精品| 少妇久久久久久| 亚洲免费av电影| 国产精品www| 国产一区二区精品丝袜| 国产在线98福利播放视频| 黑人巨大精品欧美一区二区三区| 国产成人97精品免费看片| 亚洲丝袜一区在线| 亚洲欧美精品伊人久久| 亚洲精品欧美极品| 91精品国产乱码久久久久久久久| 亚洲精品v欧美精品v日韩精品| 日韩精品久久久久| 国产精品高清网站| 性欧美暴力猛交69hd| 久久999免费视频| 国产一区二区三区三区在线观看| 亚洲人午夜精品免费| 欧美成aaa人片在线观看蜜臀| 欧美激情精品久久久久久大尺度| 啊v视频在线一区二区三区| 国产91精品在线播放| 亚洲精品一区二区久| 久久精品久久久久久国产 免费| 亚洲欧美三级在线| 亚洲国产私拍精品国模在线观看| 欧美成人一区二区三区电影| 日韩视频第一页| 国产亚洲精品激情久久| 国产精品偷伦一区二区| 欧美另类高清videos| 欧美午夜激情在线| 欧美小视频在线| 国产精品扒开腿做爽爽爽的视频| 亚洲午夜激情免费视频| 午夜精品福利电影| 2019精品视频| 一区二区三区黄色|