An in-depth analysis of event mechanisms in JavaScript framework Backbone.js basics

Source: Internet
Author: User
Tags extend hash inheritance tagname


event model and its principle
Backbone.events is the core of the event implementation, which allows the object to have event capabilities

var Events = Backbone.events = {...}

Object listens to other objects through Listento, triggering events by trigger. Can detach from backbone MVC and use events on a custom object

var model = _.extend ({},backbone.events);
var view = _.extend ({},backbone.events);
View.listento (model, ' custom_event ', function () {alert (' Catch the Event ')});
Model.trigger (' custom_event ');

Execution results:

Backbone's model and view and other core classes, are inherited from the backbone.events. For example Backbone.model:

var Events = Backbone.events = {...}

var Model = Backbone.model = function (attributes, options) {
 ...
};

_.extend (Model.prototype, Events, {...})

In principle, this is how events work:

The object being listened to maintains an array of events _event, and when other objects call Listento, the event name and callback are maintained in the queue:

An event name can correspond to multiple callbacks, and for the listener, only the existence of the callback is known, and it is not known exactly which object is listening. When the listener invokes trigger (name), it traverses _event, selects an event with the same name, and executes all the callbacks below it.

Additional attention needs to be paid to the realization of Backbone's listento, in addition to enabling the listener to maintain the reference to the listener, but also to the listener also maintained the investigation. This is so that, at the right time, the listener can interrupt the interception unilaterally. Therefore, although it is a circular reference, the appropriate method for using backbone can be well maintained and there will be no problem in the later memory leaks section to see.

In addition, sometimes you just want the event to bind after it is bound and when the callback occurs. This is useful in some references to public modules. Listentoonce can do it

Synchronizing data with the server
backbone defaults to a set of restful-style server-side synchronization models that can reduce the workload of developers and make the model more robust (data consistency is maintained under various exceptions). However, to really play this effect, a matching service-side implementation is important. To illustrate the problem, assume that the service side has the following restful interface:

    • Get/resources get a list of resources
    • Post/resources Create a resource that returns all or part of a resource's fields
    • Get/resources/{id} Gets the resource details for an ID, returning all or part of the resource's fields
    • DELETE/RESOURCES/{ID} Delete a resource
    • PUT/RESOURCES/{ID} update all fields of a resource, return all or part of a resource
    • Patch/resources/{id} update Some fields of a resource, return all or part of a resource

Backbone will use the above HTTP methods where there are mainly the following:

    • Model.save () Logically, depending on whether the current model has ID to use post or put, if model has no ID, the representation is a new model, will use post to submit the model's fields all to the/resources If model has an ID, which means that it is already existing, it will use put to submit all the fields of the model to/resources/{id}. When the incoming options contain patch:true, save produces patch.
    • Model.destroy () will produce delete, the target URL is/resources/{id}, if the current model does not contain an ID, will not sync with the server, because at this time backbone think model on the server does not exist, do not need to delete
    • The Model.fetch () produces get, the target URL is/resources/{id}, and the resulting property is updated to model.
    • Collection.fetch () generates get, the target URL is/resources, and automatically instantiates the model for each object in the returned array
    • Collection.create () actually calls the Model.save

The options parameter exists in the parameter list of any of the above methods, and options allow you to modify some of the behavior of the backbone and AJAX requests, and the options available include:

    • Wait: You can specify whether to await the return result of the server and update model. Do not wait by default
    • URL: can override backbone URL format used by default
    • Attrs: You can specify what fields are saved to the server, and Options.patch can generate patch to partially update the model
    • Patch: Specifies the rest interface with partial updates
    • Data: will be passed directly to the AJAX data in jquery to overwrite all backbone's control of uploaded
    • Other: Any parameter in the options will be passed directly to jquery's Ajax as its options

Backbone through the model of the UrlRoot attribute or collection URL attribute to know the specific server interface address, in order to launch Ajax. In model URL default implementation, model in addition to will investigate UrlRoot, the second choice will be the model where collection URL, all sometimes only need to write in collection inside the URL can be.

Backbone determines whether or not to add an ID after the URL, depending on what type of operation to do with the server, the following code is the default URL implementation of model:

Url:function () {
 var base =
 _.result (this, ' UrlRoot ') | |
 _.result (this.collection, ' url ') | |
 Urlerror ();
 if (This.isnew ()) return base;
 Return Base.replace (/([^\/]) $/, ' $1/') + encodeURIComponent (this.id);
},

The regular/([^\/]) $/is a tricky process that solves the uncertainty of whether the URL ultimately contains '/'.

This regular match is the non/character at the end of the line, in this way, a target such as/resources will match s, and then replace with the group number to capture S, replacing S with s/, which automatically adds missing/, and when/resources/such a target does not match the result, There is no need to replace it.
The relationship between model and collection
In backbone, even if a class of model instances are indeed within a set, there is no compelling requirement to use collection classes. However, there are some additional benefits to using collections, including:

URL inheritance
after model belongs to collection, you can inherit the collection URL property. Collection follows the collection and array operations of underscore90%, making collection operations extremely convenient:

//underscore methods that we want to implement on the Collection.//90% of the core USEF Ulness of backbone collections is actually implemented//right Here:var methods = [' ForEach ', ' Each ', ' map ', ' collect ', ' Reduce ', ' foldl ', ' inject ', ' reduceright ', ' foldr ', ' find ', ' detect ', ' filter ', ' select ', ' Reject ', ' every ', ' all ', ' some ', ' any ', ' include ', ' contains ', ' Invoke ', ' Max ', ' min ', ' toArray ', ' size ', ' I ', ' head ', ' take ', ' initial ', ' rest ', ' t
Ail ', ' drop ', ' last ', ' without ', ' difference ', ' indexOf ', ' Shuffle ', ' lastindexof ', ' isempty ', ' chain ', ' sample ';
Backbone cleverly use the following code to attach these methods to the Collection://Mix in each underscore method as a proxy to ' collection#models '. _.each (Methods, function (method) {Collection.prototype[method] = function () {var args = Slice.call (arguments);  Array into a true array of args.unshift (This.models); The collection is really used to maintain the array of the collection as the first argument return _[method].apply (_, args);
Use apply to invoke the underscore method};

}); 

Automatically listen and forward the model events in the collection
collections can automatically listen for and forward the events of elements in the collection, and some of the event collections do the appropriate special processing, including:

When destroy detects an element's destroy event, the element is automatically removed from the collection and the Remove event is raised
Change:id detects that an element's ID attribute is modified to automatically update the internal reference relationship to model
Automatic Model Construction
with collection's fetch, you can load the server-side data collection, and at the same time, you can automatically create the relevant model instance and call the construction method

Element repeat judgment
Collection will determine whether the element is duplicated according to the unique key specified by the model's Idattribute, by default the unique key is the ID and can be overridden by the Idattribute. When the element repeats, you can choose whether to discard the repeating element or to merge the two elements, which are discarded by default

Model transformation
sometimes the data obtained from the rest interface does not fully meet the processing requirements of the interface, and the data can be preprocessed before the backbone object is instantiated by Model.parse or Collection.parse method. In general, Model.parse is used to process properties of a single returned object, and Collection.parse is used to process the returned collection, usually by filtering out unnecessary data. For example:

Select only type=1 book
var books = Backbone.Collection.extend ({
 parse:function (models,options) {return
 _. Filter (models, function (model) {return
  Model.type = = 1;}}}
)


Adds a URL property to the book object to render the
var book = Backbone.Model.extend ({
 parse:function (model,options) {return
 _. Extend (model,{URL: '/books/' + model.id});
 }
)

By collection's fetch, the automatically instantiated model, its parse will also be invoked.

Default value for model
model can set the default value by setting the Defaults property, which is useful. Because, whether it's a model or a collection, the Fetch data is asynchronous, and often the rendering of the view is likely to be done before the data arrives, and if there are no defaults, some views using the template engine may be wrong when rendering. For example, underscore's own view engine, which uses the with () {} syntax, complains because the object lacks attributes.

The El of the View
the backbone View object is very simple, and for developers, it is only concerned with an El attribute. The El attribute can be given in five different ways, with a high to low priority:

    • When you instantiate the view, pass the El
    • Declaring El in a class
    • When you instantiate the view, you pass in the TagName
    • Declaring tagname in a class
    • None of the above cases use the default ' div '

Exactly how you choose depends on the following points:

    • In general, if the module is a public module, the El is not provided in the class, but the external is passed in when instantiated, which keeps the public view independent and does not depend on the existing DOM element
    • TagName is generally useful for the view of a self-contained system, such as an Li in a row tr,ul in a table
    • Some DOM events must be in the presence of HTML to bind to success, such as Blur, which can only select existing HTML for this view

The view class also has several properties that can be exported, initialized by external, which are:

The List of view options to is merged as properties.
var viewoptions = [' Model ', ' collection ', ' El ', ' id ', ' attributes ', ' className ', ' tagName ', ' events '];

Memory leaks
event mechanisms can be a good source of code maintenance, but because of the complexity and confusion of the references between objects, event binding can easily lead to memory leaks. The following notation causes a memory leak:

var Task = Backbone.Model.extend ({}) var Taskview = Backbone.View.extend ({tagName: ' tr ', Template: _.template (' &LT;TD ><%= ID%></td><td><%= Summary%></td><td><%= description%></td>
 '), Initialize:function () {This.listento (This.model, ' Change ', this.render);
 }, Render:function () {this. $el. HTML (this.template (This.model.toJSON ()));
 return this; var taskcollection = Backbone.Collection.extend ({url: ' http://api.test.clippererm.com/api/testtasks ', model:task , comparator: ' Summary '}) var Taskcollectionview = Backbone.View.extend ({initialize:function () {This.listento (this.
 Collection, ' Add ', this.addone);
 This.listento (this.collection, ' Reset ', this.render);
 }, Addone:function (Task) {var view = new Taskview ({model:task});
 this. $el. Append (View.render (). $el);

 }, Render:function () {var _this = this;

 Simple and rough. Empty the DOM//when the render call is triggered by the sort event, the previously instantiated Taskview object leaks this. $el. empty (); This.collection.each (FunCtion (model) {_this.addone (model);
 }) return this;

 }

})

Use the following test code and combine the chrome heap memory snapshots to prove that:

var tasks = null;
var tasklist = null;

$ (function () {
 //body ...
 $ (' #start '). Click (function () {
 tasks = new Taskcollection ();
 tasklist = new Taskcollectionview ({
  collection:tasks,
  el: ' #tasklist '
 })

 Tasklist.render ();
 Tasks.fetch ();
 })

 $ (' #refresh '). Click (function () {
 tasks.fetch ({reset:true});
 })

 $ (' #sort '). Click (function () {
 //will listen for sort here, avoiding automatic sorting after first loading data, triggering sort events that confuse
 tasklist.listentoonce ( Tasks, ' sort ', tasklist.render);
 Tasks.sort ();
 })
}

Click Start, using the ' Take Heap Snapshot ' feature under Chrome ' profile ' to view the current heap memory situation, using child type filtering, you can see a total of 10 backbone object Instances (1+1+4+4):

Filtering with the child because our class inherits from the backbone type, and inheritance uses the method of rewriting the prototype, backbone the variable named child when inheriting, and finally, the child is returned
After clicking the sort, grab the snapshot again, you can see that the number of instances has changed to 14 because, during the render process, 4 new Taskview were created, and the previous 4 Taskview were not released (the reason is 4 because the number of bars in the record is 4)

Click again to sort, crawl the snapshot again, the number of instances increased by 4, into 18!

So why is it that the previous Taskview cannot be released after each sort? Because the Taskview instance listens for model, causing the model to have a reference to the newly created Taskview instance, the old Taskview cannot be deleted, and new ones are created, resulting in rising memory. And because references exist in the callback queue for the change event, the model notifies the old Taskview instance each time the change is triggered, causing a lot of useless code to be executed. So how to improve it?

Modify Taskcollectionview:

 var Taskcollectionview = Backbone.View.extend ({initialize:function () {this.listento (th
 Is.collection, ' Add ', this.addone);
 This.listento (this.collection, ' Reset ', this.render);
 Initializes a view array to track the created view This.views =[]}, Addone:function (Task) {var view = new Taskview ({model:task});
 this. $el. Append (View.render (). $el);
 Save the newly created view This.views.push (view);

 }, Render:function () {var _this = this;
 Iterate through the views array and call Backbone's remove _.each (this.views,function view) {View.remove () on each view. Off ();
 //Empty views array, when old view becomes no referenced unreachable object//garbage collector reclaims them this.views =[];

 this. $el. empty ();
 This.collection.each (function (model) {_this.addone (model);
 }) return this; }

})

Backbone's view has a remove method that, in addition to deleting the DOM object that the view is associated with, blocks event listening by listening to the objects that are recorded in the Listento method (mentioned in the principle of the event above), To make these listening objects delete their references. Use the stoplistening of the event base class within the remove to complete this action.
The above code uses an array of views to keep track of newly created Taskview objects and, in render, calls the remove of these view objects, and then empties the array so that the Taskview objects can be freed. Also, in addition to calling remove, off is called, and the View object may be disconnected by external listening.

Event-driven modules
Custom Events: Custom events are more suitable for multiplayer development because we know that if the function name is the same, then the following function will overwrite the previous one, and the event will not be overwritten in the case of binding.

<script type= "Text/javascript" >
 //Custom event
 var Mod = backbone. Model.extend ({
 defaults: {
  name: ' TRIGKIT4 ';
 },
 initialization:function () {//Initialize constructor
  This.on ( ' Change ', function () {//Bind changes event, execute this callback function alert (123) when data is changed;}
 );

 var model = new Mod;
 Model.set (' name ', ' backbone ');//Modify the default Name property value of backbone, where the data is changed, pop-up 123
</script>


Event Bindings
In addition, we can customize the changed data types to be bound:

Object.on (event, callback, [context])

Binds a callback function to an object that executes the callback function when the event is triggered:

<script type= "Text/javascript" >
 //Custom event
 var Mod = backbone. Model.extend ({
 defaults: {
  name: ' Trigkit4 ',
  age:21;
 },
 initialization:function () {//Initialize constructor C8/>this.on (' Change:age ', function () {//Bind change event, execute this callback function
  alert (123)

 when data changes); var model = new Mod;
 Model.set (' name ', ' backbone ');//Modify the default Name property value of backbone, where data is changed, pop-up 123
</script>
Listento
<script type= "Text/javascript" >
 $ (function () {
 var Mod = Backbone.Model.extend ({
  defaults: {
  Name: ' Trigkit4 '
  }
 });
 var V = Backbone.View.extend ({
  initialize:function () {
  this.listento (this.model, ' Change ', this.show); Listento more than on parameter
  },
  show:function (model) {
  $ (' body '). Append (' <div> ' + model.get (' name ') + ' </div> ');
  }
 );

 var m = new Mod;
 var v = new V ({model:m});//model specifies the model object m that was created, that is, the preceding route, the corresponding m.set of the hash value
 (' name ', ' Hello '); When the model is changed, the event is triggered and the page is updated 
 } );
</script>

Istento

<script type= "Text/javascript" >
 $ (function () {
  var Mod = Backbone.Model.extend ({
   defaults: {
    Name: ' Trigkit4 '
   }
  });
  var V = Backbone.View.extend ({
   initialize:function () {
    this.listento (this.model, ' Change ', this.show); Listento more than on parameter
   },
   show:function (model) {
    $ (' body '). Append (' <div> ' + model.get (' name ') + ' </div> ');
   }
  );

  var m = new Mod;
  var v = new V ({model:m});//model specifies the model object m that was created, that is, the preceding route, the corresponding m.set of the hash value
  (' name ', ' Hello '); When the model is changed, the event is triggered and the page is updated  
 } );
</script>

Model aggregator
backbone.collection
A collection is an ordered combination of models, where we can bind a "change" event to the collection to be notified when the model in the collection is changed, and the collection can listen for "add" and "Remove" events, update from the server, and use the method provided by Underscore.js

Routing and History Management

<script type= "Text/javascript" > var Workspace = Backbone.Router.extend ({routes: {"Help": "Hel P "," Search/:query ":" Search "," search/:query/p:page ":" Search "}, Help:function () {A
    Lert (123);
    }, Search:function (query,page) {alert (345);

  }
  });

  var w = new Workspace; Backbone.history.start ()//backbone find the corresponding callback function via hash value </script> event delegate <script type= "Text/javascript" > $ (Fu
          Nction () {var V = Backbone.View.extend ({el: $ (' body '),//group action on events events: { "Click input": "Hello", "MouseOver li": "World"}, Hello:function () {Alert (1234
        );
      }, World:function () {alert (123)}});
    var view = new V;
  });
    </script> <body> <imput type = "button" value = "hwx"/> <ul> <li>1234</li> <li>1234</li> <li>1234</li> <li>1234</li> <li>1234</li> </ul> </body>

 

Event Delegate Format: Event + space + who triggers: corresponding callback function

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.