In-depth parsing of the event mechanism in JavaScript framework Backbone. js, javascriptbackbone

Source: Internet
Author: User

In-depth parsing of the event mechanism in JavaScript framework Backbone. js, javascriptbackbone


Event model and its principles
Backbone. Events is the core of event implementation. It enables the object to have event capabilities.

var Events = Backbone.Events = { .. }

The object listens to other objects through listenTo and triggers events through trigger. You can use events on custom objects instead of Backbone MVC.

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 result:

Core classes such as the Model and View of Backbone are inherited from Backbone. Events. For example, Backbone. Model:

var Events = Backbone.Events = { .. }var Model = Backbone.Model = function(attributes, options) { ...};_.extend(Model.prototype, Events, { ... })

In principle, events work like this:

The listened object maintains an event array _ event. When other objects call listenTo, the event name and callback are maintained in the queue:

An event name can correspond to multiple Callbacks. For the listener, only the existence of the callback exists, but not the specific object that is listening for it. When the listener calls trigger (name), it traverses _ event, selects an event of the same name, and executes all the following callbacks.

It should be noted that the listenTo Implementation of the Backbone not only enables the listener to maintain the reference to the listener, but also enables the listener to maintain the listener. This is to allow the listener to unilaterally interrupt the listener when appropriate. Therefore, although it is a circular reference, the proper Backbone method can be well maintained without any problems. The memory leakage section will be shown later.

In addition, you only want to bind an event when the callback occurs. This is useful when referencing public modules. ListenToOnce can do this

Synchronize data with the server
By default, backbone implements a mechanism to synchronize models with RESTful servers. This mechanism not only reduces the workload of developers, in addition, the model can be more robust (Data Consistency can still be maintained under various exceptions ). However, a server implementation that matches this function is very important. To illustrate the problem, assume that the server has the following restful interfaces:

  • GET/resources
  • POST/resources create a resource and return all or part of the resource fields
  • GET/resources/{id} GET the resource details of an id and return all or part of the resource fields
  • DELETE/resources/{id} delete a Resource
  • PUT/resources/{id}: update all fields of a resource and return all or part of the fields of the resource.
  • PATCH/resources/{id} updates some fields of a resource and returns all or some fields of the resource.

Backbone uses the following HTTP methods:

  • Model. save () Logically, determine whether POST or PUT should be used based on whether the current model has an id. If the model does not have an id, it indicates a new model and POST will be used, submit all the fields of the model to/resources. If the model has an id, it indicates an existing model. PUT is used to submit all fields of the model to/resources/{id }. When the input options contains patch: true, save generates a PATCH.
  • Model. destroy () will generate DELETE, and the target url is/resources/{id}. If the current model does not contain the id, it will not be synchronized with the server, because at this time, backbone considers that the model does not exist on the server, no need to delete
  • Model. fetch () generates GET, the target url is/resources/{id}, and the obtained attribute is updated to the model.
  • Collection. fetch () generates GET, the target url is/resources, and the model is automatically instantiated for each object in the returned array.
  • Collection. create () will actually call Model. save

The options parameter exists in the parameter list of any of the preceding methods. You can use options to modify the behavior of backbone and ajax requests. options include:

  • Wait: You can specify whether to wait for the server to return results and then update the model. By default, do not wait
  • Url: overwrite the default url format used by backbone.
  • Attrs: Specifies the fields saved to the server. With options. patch, you can generate a PATCH to partially update the model.
  • Patch: Specify the REST interface that uses partial updates.
  • Data: The data is directly transmitted to jquery's ajax, which can overwrite all the actions of backbone to control the uploaded data.
  • Others: Any parameters in options will be passed directly to jquery's ajax as its options

Backbone learns the specific server interface address through the urlRoot attribute of the Model or the url attribute of the Collection to initiate ajax. In the default Model url implementation, the Model will not only check urlRoot, but also the url of the Collection where the Model is located. Sometimes you only need to write the url in the Collection.

Backbone determines whether to add the id behind the url based on the type of operation to be performed with the server. The following code is the default url Implementation of the 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 expression/([^ \/]) $/solves the uncertainty of whether the url contains.

This regular expression matches non-/characters at the end of the row. In this way, targets such as/resources will match s, and then replace uses the group number $1 to capture s, replace s with s/, so that the missing/is automatically added. When the/resources/target does not match the result, no replacement is required.
Relationship between Model and Collection
In backbone, even if a type of model instance is indeed in a set, the collection class is not mandatory. However, using collections has some additional benefits, including:

Url inheritance
After the Model belongs to the Collection, it can inherit the url attribute of the Collection. Collection follows the Collection and Array Operations of underscore90 %, making the Collection operations extremely convenient:

// Underscore methods that we want to implement on the Collection. // 90% of the core usefulness of Backbone Collections is actually implemented // right here: var methods = ['foreach', 'wheel', 'map', 'collect ', 'reduce', 'foldl ', 'inobject', 'certificate', 'foldr', 'Find', 'detect ', 'filter', 'select', 'reobject ', 'every', 'all', 'some', 'any', 'include ', 'contains', 'invoke', 'max', 'Min', 'toarray ', 'SIZE', 'First ', 'head', 'Take', 'initial', 'Rest', 'tail', 'drop', 'last', 'without ', 'difference ', 'indexof', 'shuffle', 'lastindexof ', 'isempty', 'chain', 'sample']; backbone cleverly uses the following code to append 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); // convert the parameter array into a real array args. unshift (this. models); // use the Collection to maintain the array of the Set, as the first parameter return _ [method]. apply (_, args); // use apply to call the underscore method };});

Automatically listen for and forward Model events in the Set
The collection can automatically listen for and forward the events of elements in the collection, and some event sets will perform corresponding special processing. These events include:

After the destroy event of the element is listened to, the destroy automatically removes the element from the set and triggers the remove event.
Change: After the id listening element's id attribute is changed, it automatically updates the reference relationship of the internal model.
Automatic Model Construction
The fetch of Collection can be used to load the data set on the server. At the same time, related Model instances can be automatically created and the constructor method can be called.

Repeated element judgment
Collection checks whether the elements are repeated based on the unique key specified by the Model idAttribute. By default, the unique key is id and can be overwritten by idAttribute. When the elements are repeated, you can choose whether to discard the repeated elements or merge the two elements. By default, the elements are discarded.

Model Conversion
Sometimes the data obtained from the REST interface cannot fully meet the interface processing requirements. You can use the Model. parse or Collection. parse method to pre-process the data before instantiating the Backbone object. In general, Model. parse is used to process the attributes of a single returned object, while Collection. parse is used to process the returned set. It is usually used to filter out unnecessary data. For example:

// Select only bookvar Books = Backbone with type = 1. collection. extend ({parse: function (models, options) {return _. filter (models, function (model) {return model. type = 1 ;}}}) // Add url attributes to the Book object to render var Book = Backbone. model. extend ({parse: function (model, options) {return _. extend (model, {url: '/books/' + model. id });}})

The Model is automatically instantiated through fetch of Collection, and its parse is also called.

Default model value
The Model can set the default value by setting the defaults attribute, which is useful. Because fetch data is asynchronous, whether it is a model or a set, and the rendering of views is often performed before the arrival of data. If there is no default value, some views that use the template engine may encounter errors during rendering. For example, the view engine that comes with underscore reports an error because the with () {} syntax is used because the object lacks attributes.

View el
The view object of Backbone is very simple. for developers, they only need to care about one el attribute. The el attribute can be given in five ways, with the priority from high to low:

  • When instantiating a View, pass el
  • Declare el in class
  • Input the tagName when instantiating the View
  • Declare tagName in the class
  • If none of the above are found, use the default 'div'

The selection depends on the following:

  • In general, if the module is a public module, but does not provide el in the class, it is used for external input during instantiation, so that the independence of the public View can be maintained, do not rely on existing DOM elements
  • TagName is generally useful for self-contained views. For example, a row of tr in a table and a li In ul
  • Some DOM events can be successfully bound only when html exists, such as blur. For such views, you can only select an existing html

The View class has several attributes that can be exported and initialized externally. They are:

// List of view options to be merged as properties.var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];

Memory leakage
The event mechanism can facilitate code maintenance. However, event binding may make the reference between objects complicated and disordered, and may cause memory leakage. The following code causes memory leakage:

Var Task = Backbone. model. extend ({}) var TaskView = Backbone. view. extend ({tagName: 'tr', template :_. template ('<td> <% = id %> </td> <% = summary %> </td> <% = description %> </ td> '), initialize: function () {this. listenTo (this. model, 'change', this. render) ;}, render: function () {this.cancel.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; // simply and roughly clear the DOM // when the sort event triggers a render call, the previously instantiated TaskView object will leak this. $ el. empty (); this. collection. each (function (model) {_ this. addOne (model) ;}) return this ;}})

Use the following test code, and use Chrome's heap memory snapshot to prove it:

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 () {// place the listening sort here to avoid the automatic sorting after the data is loaded for the first time, and trigger the sort event to confuse tasklist. listenToOnce (tasks, 'sort ', tasklist. render); tasks. sort ();})})

Click Start and use the 'take Heap snapshot' function under 'profile 'of Chrome to view the current Heap memory. The child type is used for filtering, we can see that there are 10 Backbone object instances (1 + 1 + 4 + 4 ):

The reason why we use child filtering is that our class inherits from the Backbone type, while the inheritance uses the method of rewriting the prototype. When Backbone inherits, the variable name used is child. Finally, child is returned.
After clicking sort, capture the snapshot again and you can see that the number of instances has changed to 14. This is because four new taskviews are created during the render process, the previous four taskviews were not released (4 because the number of records is 4)

Click "sort" again to capture snapshots again. The number of instances has increased by 4, which has changed to 18!

Why can't the previous TaskView be released after each sorting. Because all TaskView instances listen on the model, the model references the newly created TaskView instance. Therefore, the old TaskView cannot be deleted, and a new one is created, resulting in increasing memory. Because the reference exists in the callback queue of the change event, the model notifies the old TaskView instance every time the change event is triggered, resulting in a lot of useless code execution. So how can we improve it?

Modify TaskCollectionView:

Var TaskCollectionView = Backbone. view. extend ({initialize: function () {this. listenTo (this. collection, 'add', this. addOne); this. listenTo (this. collection, 'reset', this. render); // initialize 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; // traverses the views array and calls remove _ of Backbone for each view _. each (this. views, function (view) {view. remove (). off () ;}) // clear the views array. In this case, the old views become inaccessible objects that are not referenced. // The Garbage Collector recycles them. this. views = []; this. $ el. empty (); this. collection. each (function (model) {_ this. addOne (model) ;}) return this ;}})

The View of Backbone has a remove method. In addition to deleting the DOM object associated with the View, this method also blocks event listening, the listenTo method records the listened objects (mentioned in the event principle above) to delete the referenced objects. Use the stopListening of the event base class to complete this action within remove.
The code above uses an views array to track newly created TaskView objects, and calls the remove of these view objects in sequence during render, and then clears the array, in this way, these TaskView objects can be released. In addition to the remove function, the off function is also called to disconnect the view object from external listening.

Event-driven module
Custom events: Custom events are suitable for collaborative development by many people, because we know that if the function names are the same, the subsequent functions will overwrite the previous ones, events are not overwritten when they are bound.

<Script type = "text/javascript"> // custom event var Mod = backbone. model. extend ({defaults: {name: 'trigger4';}, initialization: function () {// initialize the constructor this. on ('change', function () {// bind the change event. When the data changes, execute this callback function alert (123 );});}}); var model = new Mod; model. set ('name', 'background'); // modify the default name attribute value to backbone. In this case, the data is changed. The value 123 is displayed. </script>


Event binding
In addition, we can also customize the changed data type to be bound:

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

Bind a callback function to an object and execute the callback function when the event is triggered:

<Script type = "text/javascript"> // custom event var Mod = backbone. model. extend ({defaults: {name: 'trigger4', age: 21;}, initialization: function () {// initialize the constructor this. on ('change: age', function () {// bind the change event. When the data changes, execute this callback function alert (123 );});}}); var model = new Mod; model. set ('name', 'background'); // modify the default name attribute value to backbone. In this case, the data is changed, the 123 </script> listenTo <script type = "text/javascript" >$ (function () {var Mod = Backbone. model. extend ({defaults: {name: 'trigger4'}); var V = Backbone. view. extend ({initialize: function () {this. listenTo (this. model, 'change', this. show); // listenTo has more parameters than on}, show: function (model) {$ ('body '). append ('<div>' + model. get ('name') + '</div>') ;}}; var m = new Mod; var v = new V ({model: m }); // model specifies the created model object m, that is, the previous route, and the corresponding m of the hash value. set ('name', 'Hello'); // when the model is modified, the event is triggered and the page is updated.}) </script>

IstenTo

<Script type = "text/javascript"> $ (function () {var Mod = Backbone. model. extend ({defaults: {name: 'trigger4'}); var V = Backbone. view. extend ({initialize: function () {this. listenTo (this. model, 'change', this. show); // listenTo has more parameters than on}, show: function (model) {$ ('body '). append ('<div>' + model. get ('name') + '</div>') ;}}; var m = new Mod; var v = new V ({model: m }); // model specifies the created model object m, that is, the previous route, and the corresponding m of the hash value. set ('name', 'Hello'); // when the model is modified, the event is triggered and the page is updated.}) </script>

Model collector
Backbone. Collection
A set is an ordered combination of models. We can bind a "change" event to the set to receive notifications when the model in the Set changes, the collection can also listen to "add" and "remove" events, update from the server, and use Underscore. methods provided by js

Route and History Management

<Script type = "text/javascript"> var Workspace = Backbone. router. extend ({routes: {"help": "help", "search/: query": "search", "search/: query/p: page ": "search"}, help: function () {alert (123) ;}, search: function (query, page) {alert (345 );}}); var w = new Workspace; Backbone. history. start (); // backbone finds the corresponding callback function using the hash value </script> event DeleGate <script type = "text/javascript" >$ (function () {var V = Backbone. view. extend ({el: $ ('body'), // perform group operations 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 the event: Corresponding callback function

Articles you may be interested in:
  • A Brief Introduction to the Model and View source code of Backbone. js
  • Prepare study notes in simple View in the Backbone. js framework
  • Describes the MVC Structure Design Concept of the Backbone. js framework of JavaScript.
  • Lightweight javascript framework Backbone User Guide
  • Some tips for using Backbone. js
  • Backbone. js 0.9.2 source code annotation Chinese translation version
  • Hello World program instance of Backbone. js
  • Details about the set in Backbone. js
  • Details about Javascript MVC Framework Backbone. js
  • Some suggestions for using JavaScript's Backbone. js framework

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.