1. 程式人生 > >犀利的backbone 然而我不會用

犀利的backbone 然而我不會用

backbone 介紹

程式碼例子都摘自這裡點我

有些人早已知道這個框架(然而你們卻不會用!),有些人不知道這個框架 我在這裡都說一下

backbone 是一個很輕量級的框架(其實我覺得這句話真是扯),是的相比較於Angularjs Ember來說 確實很輕量級 但是 backbone 這個框架並不能單獨執行,他需要兩個幫手 第一個Jquery 第二個 underscore 這兩個JS庫 呵呵噠!

想了解 Angularjs 點這裡

backbone 能幹嘛呢?額 可以做單頁應用 SPA 聽說過吧 它是一個MVC框架 2010年出來的 距今已經有6歲了 算是MV*框架中 元老級別的了。

backbone 基礎

這一小節 我們將講解一下backbone 的 models, views,collections,events,還有 routers
首先 我們來搭建一個開發環境

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script src="jquery.min.js"></script>
<script src="underscore-min.js"
>
</script> <script src="backbone-min.js"></script> <script> // Your code goes here </script> </body> </html>

指令碼什麼的 自己放到相應的資料夾中 就這樣開發環境就搭建完成了

MODEL

backbone 中的model 就是處理資料的 記住這一條就可以了
下面我們建立一個model

var Todo = Backbone.Model.extend({});

//開始建立自己的model
// 新的model 沒有啥資料:
var todo1 = new Todo(); // 輸出log: {} console.log(JSON.stringify(todo1)); // 我們來寫一點資料: var todo2 = new Todo({ title: 'Check the attributes of both model instances in the console.', completed: true }); // logs: {"title":"Check the attributes of both model instances in the console.","completed":true} console.log(JSON.stringify(todo2));

model 的初始化

在backbone 的model中有一個initialize()方法
也就是在model 每次初始化的時候會執行這裡面的程式碼

var Todo = Backbone.Model.extend({
  initialize: function(){
      console.log('This model has been initialized.');
  }
});
var myTodo = new Todo();
// Logs: This model has been initialized.

設定model 的預設值

你可以 給你的 model 設定一個預設值,在你沒有給model 賦值的時候 model中都會存放這些預設值

但是要注意的是 如果你包含一個物件作為預設值,他會被所有的例項共享,如果你不想這樣可以定義一個函式取代 嘿嘿嘿

var Todo = Backbone.Model.extend({
  // Default todo attribute values
  defaults: {
    title: '',
    completed: false
  }
});

// Now we can create our concrete instance of the model
// with default values as follows:
var todo1 = new Todo();

// Following logs: {"title":"","completed":false}
console.log(JSON.stringify(todo1));

// Or we could instantiate it with some of the attributes (e.g., with custom title):
var todo2 = new Todo({
  title: 'Check attributes of the logged models in the console.'
});

// Following logs: {"title":"Check attributes of the logged models in the console.","completed":false}
console.log(JSON.stringify(todo2));

// Or override all of the default attributes:
var todo3 = new Todo({
  title: 'This todo is done, so take no action on this one.',
  completed: true
});

// Following logs: {"title":"This todo is done, so take no action on this one.","completed":true} 
console.log(JSON.stringify(todo3));

Getters&Setters

顧名思義 取資料 設定資料

Model.get();
從當前model中獲取當前的屬性

var Todo = Backbone.Model.extend({
  // Default todo attribute values
  defaults: {
    title: '',
    completed: false
  }
});

var todo1 = new Todo();
console.log(todo1.get('title')); // empty string
console.log(todo1.get('completed')); // false

var todo2 = new Todo({
  title: "Retrieved with model's get() method.",
  completed: true
});
console.log(todo2.get('title')); // Retrieved with model's get() method.
console.log(todo2.get('completed')); // true

如果你想獲得model中所有的 資料屬性 有一個toJSON()方法

var Todo = Backbone.Model.extend({
  // Default todo attribute values
  defaults: {
    title: '',
    completed: false
  }
});

var todo1 = new Todo();
var todo1Attributes = todo1.toJSON();
// Following logs: {"title":"","completed":false} 
console.log(todo1Attributes);

var todo2 = new Todo({
  title: "Try these examples and check results in console.",
  completed: true
});

// logs: {"title":"Try these examples and check results in console.","completed":true}
console.log(todo2.toJSON());

Model.set()
向model設定一個或多個hash屬性(attributes)。

var Todo = Backbone.Model.extend({
  // Default todo attribute values
  defaults: {
    title: '',
    completed: false
  }
});

// Setting the value of attributes via instantiation
var myTodo = new Todo({
  title: "Set through instantiation."
});
console.log('Todo title: ' + myTodo.get('title')); // Todo title: Set through instantiation.
console.log('Completed: ' + myTodo.get('completed')); // Completed: false

// Set single attribute value at a time through Model.set():
myTodo.set("title", "Title attribute set through Model.set().");
console.log('Todo title: ' + myTodo.get('title')); // Todo title: Title attribute set through Model.set().
console.log('Completed: ' + myTodo.get('completed')); // Completed: false

// Set map of attributes through Model.set():
myTodo.set({
  title: "Both attributes set through Model.set().",
  completed: true
});
console.log('Todo title: ' + myTodo.get('title')); // Todo title: Both attributes set through Model.set().
console.log('Completed: ' + myTodo.get('completed')); // Completed: true

如果任何一個屬性改變了model的狀態,在不傳入 {silent: true} 選項引數的情況下,會觸發 “change” 事件,更改特定屬性的事件也會觸發。 可以繫結事件到某個屬性,例如:change:title,及 change:content。

var Person = new Backbone.Model();
Person.on("change:name", function() { console.log('Name changed'); });
Person.set({name: 'Andrew'});
// log entry: Name changed

Person.set({name: 'Jeremy'}, {silent: true});
// no log entry

console.log(Person.hasChanged("name"));
// true: change was recorded
console.log(Person.hasChanged(null));
// true: something (anything) has changed

如果你想在model 發生變化的時候偵聽這個變化你也可以這麼做把偵聽的方法放到initialize中去

var Todo = Backbone.Model.extend({
  // Default todo attribute values
  defaults: {
    title: '',
    completed: false
  },
  initialize: function(){
    console.log('This model has been initialized.');
    this.on('change', function(){
        console.log('- Values for this model have changed.');
    });
  }
});

var myTodo = new Todo();

myTodo.set('title', 'The listener is triggered whenever an attribute value changes.');
console.log('Title has changed: ' + myTodo.get('title'));


myTodo.set('completed', true);
console.log('Completed has changed: ' + myTodo.get('completed'));

myTodo.set({
  title: 'Changing more than one attribute at the same time only triggers the listener once.',
  completed: true
});

// Above logs:
// This model has been initialized.
// - Values for this model have changed.
// Title has changed: The listener is triggered whenever an attribute value changes.
// - Values for this model have changed.
// Completed has changed: true
// - Values for this model have changed.

model validation

預設情況下validate在save之前呼叫, 但如果傳遞了 {validate:true},也可以在set之前呼叫。

var Person = new Backbone.Model({name: 'Jeremy'});

// Validate the model name
Person.validate = function(attrs) {
  if (!attrs.name) {
    return 'I need your name';
  }
};

// Change the name
Person.set({name: 'Samuel'});
console.log(Person.get('name'));
// 'Samuel'

// Remove the name attribute, force validation
Person.unset('name', {validate: true});
// false

這裡順帶說說unset()方法
從內部屬性散列表中刪除指定屬性(attribute)。 如果未設定 silent 選項,會觸發 “change” 事件。

看完上面的程式碼 恩大家有點疑惑 驗證完以後 怎麼通知我呢
其實有一個invalid事件 當你的驗證不通過的時候這個事件會被觸發
.save()方法 就不會繼續執行

var Todo = Backbone.Model.extend({
  defaults: {
    completed: false
  },

  validate: function(attributes){
    if(attributes.title === undefined){
        return "Remember to set a title for your todo.";
    }
  },

  initialize: function(){
    console.log('This model has been initialized.');
    this.on("invalid", function(model, error){
        console.log(error);
    });
  }
});

var myTodo = new Todo();
myTodo.set('completed', true, {validate: true}); // logs: Remember to set a title for your todo.
console.log('completed: ' + myTodo.get('completed')); // completed: false

還有一種簡易的方法 可以知道驗證返回的錯誤

var emptyTodo = new Todo(null, {validate: true});
console.log(emptyTodo.validationError);

Model 中還有一些其他的方法和屬性在這裡就不一一介紹了 大家可以去看看API 好像一半的方法還是underscore裡面的 呵呵噠

VIEW

backbone中的view和其他框架一樣 通過一套模板框架來實現資料的繫結和互動這裡我們使用的是 underscore microtemplates

view 中有一個render()方法可以 監聽繫結到model 中的 change()事件 這樣當modal 改變的時候 view也會改變 就不用重新整理整個頁面了

建立一個VIEW

建立一個新VIEW其實和建立一個model是差不多的
我們用 Backbone.View來建立

var TodoView = Backbone.View.extend({

  tagName:  'li',

  // Cache the template function for a single item.
  todoTpl: _.template( "An example template" ),

  events: {
    'dblclick label': 'edit',
    'keypress .edit': 'updateOnEnter',
    'blur .edit':   'close'
  },

  initialize: function (options) {
    // In Backbone 1.1.0, if you want to access passed options in
    // your view, you will need to save them as follows:
    this.options = options || {};
  },

  // Re-render the title of the todo item.
  render: function() {
    this.$el.html( this.todoTpl( this.model.attributes ) );
    this.input = this.$('.edit');
    return this;
  },

  edit: function() {
    // executed when todo label is double clicked
  },

  close: function() {
    // executed when todo loses focus
  },

  updateOnEnter: function( e ) {
    // executed on each keypress when in todo edit mode,
    // but we'll wait for enter to get in action
  }
});

var todoView = new TodoView();

// log reference to a DOM element that corresponds to the view instance
console.log(todoView.el); // logs <li></li>

首先我們來說一說試圖中的el 你們看程式碼的最後一行

所有的檢視都擁有一個 DOM 元素(el 屬性),即使該元素仍未插入頁面中去。 檢視可以在任何時候渲染,然後一次性插入 DOM 中去,這樣能儘量減少 reflows 和 repaints 從而獲得高效能的 UI 渲染。 this.el 可以從檢視的 tagName, className, id 和 attributes 建立,如果都未指定,el 會是一個空 div。

上面的程式碼 tagName 設定的是 li 所以最後建立一個 li 元素 下面我們再來建立一個

var TodosView = Backbone.View.extend({
  tagName: 'ul', // required, but defaults to 'div' if not set
  className: 'container', // optional, you can assign multiple classes to 
                          // this property like so: 'container homepage'
  id: 'todos' // optional
});

var todosView = new TodosView();
console.log(todosView.el); // logs <ul id="todos" class="container"></ul>

我們建立了一個 ul 元素 class 是 container id 是 todos

但是上面的程式碼其實並沒有插入到DOM中 也就是 我們在瀏覽器中是看不到的哦

如果某個元素是存在的 可以用el 向用jquery 那樣匹配它意思就是 這樣

el: '#footer'

有時候 我們需要 jquery 的 一些內建的 方法 比如什麼 append()啊 之類的backbone 給我們提供了一個 elview.el 等同於 (view.el)view.(selector) 等同於 $(view.el).find(selector)

setElement

如果你想應用一個Backbone檢視到不同的DOM元素, 使用setElement, 這也將創造快取$el引用,檢視的委託事件從舊元素移動到新元素上。

// We create two DOM elements representing buttons
// which could easily be containers or something else
var button1 = $('<button></button>');
var button2 = $('<button></button>');

// Define a new view
var View = Backbone.View.extend({
      events: {
        click: function(e) {
          console.log(view.el === e.target);
        }
      }
    });

// Create a new instance of the view, applying it
// to button1
var view = new View({el: button1});

// Apply the view to button2 using setElement
view.setElement(button2);

button1.trigger('click'); 
button2.trigger('click'); // returns true

瞭解 render()

render()實際上是一個配置方法 通過這些配置來渲染你的模板

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title></title>
  <meta name="description" content="">
</head>
<body>
  <div id="todo">
  </div>
  <script type="text/template" id="item-template">
    <div>
      <input id="todo_complete" type="checkbox" <%= completed ? 'checked="checked"' : '' %>>
      <%= title %>
    </div>
  </script>
  <script src="underscore-min.js"></script>
  <script src="backbone-min.js"></script>
  <script src="jquery-min.js"></script>
  <script src="example.js"></script>
</body>
</html>

.template是 underscore 編譯渲染模板的方法在上面的例子中 我們傳入ID為 item-template 給 .template()方法

render 預設實現是沒有操作的。 過載本函式可以實現從模型資料渲染檢視模板,並可用新的 HTML 更新 this.el。 推薦的做法是在 render 函式的末尾 return this 以開啟鏈式呼叫。

其實這麼說大家應該還是不明白 ,其實我也不明白 但是還是繼續等會兒我們再來說一下這個問題

VIEW事件

大家都知道onclick 這些操作事件吧 在backbone中是這樣的

// A sample view
var TodoView = Backbone.View.extend({
  tagName:  'li',

  // with an events hash containing DOM events
  // specific to an item:
  events: {
    'click .toggle': 'toggleCompleted',
    'dblclick label': 'edit',
    'keypress .edit': 'updateOnEnter',
    'click .destroy': 'clear',
    'blur .edit': 'close'
  },

格式就是這樣 ‘eventName selector’: ‘callbackFunction’
簡單明瞭,然後 綁定了事件有了回撥 事件執行完成以後我們要更新檢視啊 所有可以這樣

var TodoView = Backbone.View.extend({
  initialize: function() {
    this.model.bind('change', _.bind(this.render, this));
    this.model.on('change:a', this.renderA, this);
  }
});

上面兩種方法都可以 不過第一個是老版本的方法 還要藉助underscore中的方法

Collection

你可以理解為 model 的集合

var Todo = Backbone.Model.extend({
  defaults: {
    title: '',
    completed: false
  }
});

var TodosCollection = Backbone.Collection.extend({
  model: Todo
});

var myTodo = new Todo({title:'Read the whole book', id: 2});

// pass array of models on collection instantiation
var todos = new TodosCollection([myTodo]);
console.log("Collection size: " + todos.length); // Collection size: 1

我們可以使用add(), remove()方法對集合中的模型 進行新增和刪除

var Todo = Backbone.Model.extend({
  defaults: {
    title: '',
    completed: false
  }
});

var TodosCollection = Backbone.Collection.extend({
  model: Todo
});

var a = new Todo({ title: 'Go to Jamaica.'}),
    b = new Todo({ title: 'Go to China.'}),
    c = new Todo({ title: 'Go to Disneyland.'});

var todos = new TodosCollection([a,b]);
console.log("Collection size: " + todos.length);
// Logs: Collection size: 2

todos.add(c);
console.log("Collection size: " + todos.length);
// Logs: Collection size: 3

todos.remove([a,b]);
console.log("Collection size: " + todos.length);
// Logs: Collection size: 1

todos.remove(c);
console.log("Collection size: " + todos.length);
// Logs: Collection size: 0

如果您要新增 集合中已經存在的模型 到集合,他們會被忽略, 除非你傳遞{merge: true}, 在這種情況下,它們的屬性將被合併到相應的模型中.

var items = new Backbone.Collection;
items.add([{ id : 1, name: "Dog" , age: 3}, { id : 2, name: "cat" , age: 2}]);
items.add([{ id : 1, name: "Bear" }], {merge: true });
items.add([{ id : 2, name: "lion" }]); // merge: false

console.log(JSON.stringify(items.toJSON()));
// [{"id":1,"name":"Bear","age":3},{"id":2,"name":"cat","age":2}]

我們可以使用clllection.get()方法獲取集合中的model 只要傳個ID就可以了 這個ID可以是你自己定義的ID 也可以是 backbone自動生成的ID

var myTodo = new Todo({title:'Read the whole book', id: 2});

// pass array of models on collection instantiation
var todos = new TodosCollection([myTodo]);

var todo2 = todos.get(2);

// Models, as objects, are passed by reference
console.log(todo2 === myTodo); // true

這裡使用的CID

// extends the previous example

var todoCid = todos.get(todo2.cid);

// As mentioned in previous example, 
// models are passed by reference
console.log(todoCid === myTodo); // true

當我們修改了collection中的model時 我們也可以偵聽這些狀態方式和view中的一樣

var TodosCollection = new Backbone.Collection();

TodosCollection.on("add", function(todo) {
  console.log("I should " + todo.get("title") + ". Have I done it before? "  + (todo.get("completed") ? 'Yeah!': 'No.' ));
});

TodosCollection.add([
  { title: 'go to Jamaica', completed: false },
  { title: 'go to China', completed: false },
  { title: 'go to Disneyland', completed: true }
]);

// The above logs:
// I should go to Jamaica. Have I done it before? No.
// I should go to China. Have I done it before? No.
// I should go to Disneyland. Have I done it before? Yeah!
var TodosCollection = new Backbone.Collection();

// log a message if a model in the collection changes
TodosCollection.on("change:title", function(model) {
    console.log("Changed my mind! I should " + model.get('title'));
});

TodosCollection.add([
  { title: 'go to Jamaica.', completed: false, id: 3 },
]);

var myTodo = TodosCollection.get(3);

myTodo.set('title', 'go fishing');
// Logs: Changed my mind! I should go fishing

我們也可以使用 set() reset()方法 設定或者重置 collection中的值

var TodosCollection = new Backbone.Collection();

TodosCollection.add([
    { id: 1, title: 'go to Jamaica.', completed: false },
    { id: 2, title: 'go to China.', completed: false },
    { id: 3, title: 'go to Disneyland.', completed: true }
]);

// we can listen for add/change/remove events
TodosCollection.on("add", function(model) {
  console.log("Added " + model.get('title'));
});

TodosCollection.on("remove", function(model) {
  console.log("Removed " + model.get('title'));
});

TodosCollection.on("change:completed", function(model) {
  console.log("Completed " + model.get('title'));
});

TodosCollection.set([
    { id: 1, title: 'go to Jamaica.', completed: true },
    { id: 2, title: 'go to China.', completed: false },
    { id: 4, title: 'go to Disney World.', completed: false }
]);

// Above logs:
// Completed go to Jamaica.
// Removed go to Disneyland.
// Added go to Disney World.
var TodosCollection = new Backbone.Collection();

// we can listen for reset events
TodosCollection.on("reset", function() {
  console.log("Collection reset.");
});

TodosCollection.add([
  { title: 'go to Jamaica.', completed: false },
  { title: 'go to China.', completed: false },
  { title: 'go to Disneyland.', completed: true }
]);

console.log('Collection size: ' + TodosCollection.length); // Collection size: 3

TodosCollection.reset([
  { title: 'go to Cuba.', completed: false }
]);
// Above logs 'Collection reset.'

console.log('Collection size: ' + TodosCollection.length); // Collection size: 1

由於collection 是個集合自然也集成了許多underscore對集合的操作方法
例如map each等等 這裡不介紹了大家可以看underscore的文件
點這裡看文件

Backbone與伺服器的互動

第一個 Collection.fetch()
從伺服器拉取集合的預設模型設定 ,成功接收資料後會setting(設定)集合。 options 支援 success 和 error 回撥函式,兩個回撥函式接收 (collection, response, options)作為引數。當模型資料從伺服器返回時, 它使用 set來(智慧的)合併所獲取到的模型, 除非你傳遞了 {reset: true}, 在這種情況下,集合將(有效地)重置。 可以委託Backbone.sync 在幕後自定義永續性策略 並返回一個jqXHR。 fetch請求的伺服器處理器應該返回模型JSON陣列。

var Todo = Backbone.Model.extend({
  defaults: {
    title: '',
    completed: false
  }
});

var TodosCollection = Backbone.Collection.extend({
  model: Todo,
  url: '/todos'
});

var todos = new TodosCollection();
todos.fetch(); // sends HTTP GET to /todos

第二個向伺服器傳送資料 collections.create() 與 save()方法不同的是

它方便的在集合中建立一個模型的新例項。 相當於使用屬性雜湊(鍵值物件)例項化一個模型, 然後將該模型儲存到伺服器, 建立成功後將模型新增到集合中。 返回這個新模型。 如果客戶端驗證失敗,該模型將不會被儲存, 與驗證錯誤。 為了能正常執行,需要在集合中設定 model 屬性。 create 方法接收鍵值物件或者已經存在尚未儲存的模型物件作為引數。
建立一個模型將立即觸發集合上的”add”事件, 一個”request”的事件作為新的模型被髮送到伺服器, 還有一個 “sync” ”事件,一旦伺服器響應成功建立模型。 如果你想在集合中新增這個模型前等待伺服器響應,請傳遞{wait: true}。

var Todo = Backbone.Model.extend({
  defaults: {
    title: '',
    completed: false
  }
});

var TodosCollection = Backbone.Collection.extend({
  model: Todo,
  url: '/todos'
});

var todos = new TodosCollection();
todos.fetch();

var todo2 = todos.get(2);
todo2.set('title', 'go fishing');
todo2.save(); // sends HTTP PUT to /todos/2

todos.create({title: 'Try out code samples'}); // sends HTTP POST to /todos and adds to collection

我們也可以通過 collection.destory()從伺服器刪除資料

var Todo = Backbone.Model.extend({
  defaults: {
    title: '',
    completed: false
  }
});

var TodosCollection = Backbone.Collection.extend({
  model: Todo,
  url: '/todos'
});

var todos = new TodosCollection();
todos.fetch();

var todo2 = todos.get(2);
todo2.destroy(); // sends HTTP DELETE to /todos/2 and removes from collection