On HTML5 single page Architecture (ii)--backbone + Requirejs + zepto + underscore

Source: Internet
Author: User

This article was reproduced from: http://www.cnblogs.com/kenkofox/p/4648472.html

A brief discussion on HTML5 single page Architecture (i)--requirejs + angular + Angular-route discusses a simple architecture of Angular+requirejs, This article continues to see how backbone with Requirejs.

In the same way, the project structure is good and bad is not to say how many great frameworks, but how to rationalize the use of the framework, so that project development smoother, code easier to manage. So with that in view, we will continue to explore backbone.

First, take a look at the entire project structure.

Similar to the previous article angular, Libs underscore and zepto. Three root directory files:

    • Index.html: the only HTML
    • Main.js:requirejs configuration, entry of the program
    • Router.js: Single-page routing configuration for the entire app or site

The first step, or the creation of a single page unique HTML

<! DOCTYPE html>

Backbone does not make a fuss about DOM attributes, we still write things in the native or familiar way. This defines a container div as the backbone view.

The Requirejs,data-main is then introduced to represent the main program entry.

Step Two, configure Main.js

(function (Win) {//config baseUrl var baseUrl = document.getElementById (' main '). getattribute (' Data-baseurl '); /* * File dependent */var config = {Baseurl:baseurl,//Dependent relative path paths: {//IF The dependency of a prefix is not as simple as baseurl stitching, it needs to be pointed out here Zepto: ' Libs/zepto.min ', jquery: ' Libs/zepto.min ', unde Rscore: ' Libs/underscore ', backbone: ' Libs/backbone ', text: ' Libs/text '//for Requirejs import H Tml type of dependency}, Shim: {//introduces a class library that does not use Requirejs module notation.                Backbone dependent underscore ' underscore ': {exports: ' _ '}, ' jquery ': {                Exports: ' $ '}, ' Zepto ': {exports: ' $ '}, ' backbone ': {    Deps: [' underscore ', ' jquery '], exports: ' Backbone '}};    Require.config (config); Backbone will add itself to the global variable require ([' backbone ', ' UnderscorE ', ' router '], function () {Backbone.history.start (); Start monitoring URL changes}) (window);

About Requirejs Grammar, or not much to say, we go to the official website to see it. This is the foundation.

Using backbone, you have to emphasize the Requirejs shim configuration. Backbone this bandit, want more things, to give him "shoes" (underscore), but also give him $ $ (jquery).

Because the terminal uses jquery is too large, so here do a little trick, with Zepto as jquery, cheated bandits. With a few Vietnamese dong, jokingly is the dollar, did not expect the old to believe.

There's a place to be aware that

No matter where you introduce backbone with Requirejs, there will be more backbone and $ two global variables, so the subsequent use of backbone will not need to be constrained by the requirejs of the AMD notation. It is also good to relax and breathe properly.

Once you have configured the dependencies, you can introduce router and invoke the key

Backbone.history.start

Start routing monitoring.

Step three, configure router, route table

define ([' backbone '], function () {var Router = Backbone.Router.extend ({routes: {' Module1 ': ' Module        1 ', ' Module2 (/:name) ': ' Module2 ', ' *actions ': ' DefaultAction '},//route initialization can do something            Initialize:function () {}, Module1:function () {var url = ' Module1/controller1.js ';                Here can not use the module depends on the wording, and instead of the URL to the wording, is to grunt Requirejs packaging time to break the dependency chain, separate multiple files require ([url], function (Controller) {            Controller ();        });            },//name with the routing configuration inside: Name consistent module2:function (name) {var url = ' Module2/controller2.js ';            require ([url], function (Controller) {controller (name);        });            }, Defaultaction:function () {console.log (' 404 ');        Location.hash = ' module2 ';    }    });    var router = new Router ();  Router.on (' Route ', function (route, params) {console.log (' hash change ', arguments); Over hereRoute is the method name for the route});    return router; Required here, giving way by Table execution});

Backbone.Router.extend This grammar, believe that do not have to say more, say more also say not clear, everybody Go official website is kingly: http://backbonejs.org

Backbone routes are similar to angular, but they are not the same for optional parameters. Angular use: param way, and backbone use (:p Aram), which way good, a matter of opinion.

This defines a default route, and two business routes.

The principle is simple, is to encounter Module1 hash (hash) to execute the corresponding function behind the string

It's an estimate that everyone knows about this thing. In the above code, the key difference is that the Requirejs is used to do the modularization, the route after the jump to do all the logic is defined in the other JS.

The key to this is that the URL is used here, and the independent variable is the way to configure the module's JS instead of

            Require ([' module1/controller1 '], function (Controller) {                controller ();            });

The purpose is grunt do requirejs packaging, can cut off both sides of the JS, do not merge in a large JS.

In addition, we can make good use of Router.on (' route ', function) this interface, in time to do the event to unbind and some cleanup work.

Fourth step, write a simple module

Controller1.js

define ([' module1/view1 '), function (View) {    var controller = function () {        var view = new View ();        View.render (' Kenko ');    };    return controller;});

View1.js

define ([' text!module1/tpl.html '], function (TPL) {    var View1 = Backbone.View.extend ({        el: ' #container ',        initialize:function () {        },        render:function (name) {this            . $el. HTML (_.template (TPL, {name:name} ));        }    });    return View1;});

Tpl.html

<div> here is    Module 1. My Name: <%=name%><br>    <a href= "#module2" >turn to module 2</a></div>

The format of the template is not the same as angular, the use of a more general way, jquery, underscore are this mode. Simply put, <%%> includes JS code, with = equals can directly output the value of the variable.

Here is the simplest mvc,m is just a value name,c is the controller, V is view1.

View1 's writing needs to follow the backbone syntax, otherwise there is no point in using backbone here. El points to the corresponding view DOM element, using the CSS selector, which can be used in view. $el get to this jquery style variable. Render is a custom function.

Here, run the program, you can see the effect of Module1.

Fifth step, then write the second module, the strict MVC

Model2.js

define ([], function () {    var Model2 = Backbone.Model.extend ({        ///model default data        defaults:function () {            return {                Name: "Noname"            };        },        //define some methods        fetch:function () {            var o = this;            Can do some HTTP request            SetTimeout (function () {                o.set ({name: ' Vivi '});                O.trigger (' nameevent ');     Trigger event to view} ();}    );    return Model2;});

The model syntax is also subject to backbone requirements, and defaults is the default property value. To read and write these properties, you need to pass the Model.get/set interface, or you can return the entire object with Tojson, or the anatomical use of model.attributes.xxx.

Fetch is a custom method that simulates an HTTP request, which is a very general practice, but this example does not use the backbone's rest interface.

After the data is returned, use the backbone built-in trigger trigger event to notify the listener, which is the view layer. Backbone and angular The biggest difference is that backbone do not pay attention to the component of the view layer, more concerned about the model and the event mechanism, and angular not focus on the event mechanism, the use of two-way binding to the data update the broken things hidden. Each has its own advantages, a matter of opinion.

View2.js

define ([' text!module2/tpl.html '], function (TPL) {    var View2 = Backbone.View.extend ({        el: ' #container ',        Events: {            ' click button ': ' Clickspan '     //use Agent to listen for interaction, the advantage is that the interface even re-rander, the event can also trigger, do not need to rebind. If you use Zepto hand-by-element binding, when the element refreshes, the event binding is invalid        },        initialize:function () {            this.model.on (' nameevent ', This.render, this);      Listener Event        },        render:function () {this            . $el. HTML (_.template (TPL, {name:this.model.get (' name ')});     Java-like DAO thought, everything through get set operation        },        clickspan:function (e) {            alert (' You clicked the button ');        }    );    return View2;});

Next, let's look at backbone a typical view of how to play. First look at the Initialize method, which is the initialization logic that is executed first when new View2 ().

We are here to listen to the nameevent message, which is the event that Model2 throws. When you receive this notification, update the interface. Logic is simple.

Here is a more useful events, interactive event broker mechanism.

We do not need to separate write Zepto on the DOM to bind the event separately, just configure a events mapping table here.

The click button is equivalent to the Zepto

$ (' button '). On (' click ', function)

The binding here is the Clickspan event.

The advantage of this event broker mechanism is that event snooping can be easily removed when routing is switched.

View.undelegateevents ()

Tpl.html

<div> here is    Module 2. My Name: <%=name%><br>    <button>click me!</button>    <a href= "#module1" >turn to Module 1</a></div>

Controller2.js

define ([' Module2/model2 ', ' module2/view2 '], function (Model, View) {    var controller = function (name) {        var Model = new Model ();        Name && model.set ({            name:name               //Set default property value        });        var view = new View ({Model:model});        View.render ();      Initializes the interface Model.fetch () using the default properties defined by the model        ;          Pull CGI and so on, get the data, then trigger the event, the interface receives the message to do the corresponding action    };    return controller;});

The controller is responsible for doing is to rub the data, put in the view. Let view first render with default data, then let the model pull up the latest data, and finally update the interface through the event mechanism.

Of course, this controller is not backbone specification, everyone can play.

Finally, back to the routing table, when the hash becomes module2, it executes:

        Module2:function (name) {            var url = ' Module2/controller2.js ';            require ([url], function (Controller) {                controller (name);            });        },

At this point, the simple Requirejs+backbone framework has been completed. In addition to the high degree of router coupling, each module logic code is independent, the app can easily be loaded on demand.

So the pursuit of the mechanism of the Sao years, to stop it? According to this scheme, in the actual development, many people will often fight at this juncture of router, where the configuration is not perfect.

Sixth step, optimize router, completely configure

The problem with the existing solution is that, in addition to writing the routing configuration, router also need to add the corresponding function, so that both redundant and easy to conflict, then can listen to the route event, do a unified routing processor? A handler function that handles all routed responses.

 define ([' backbone '), function () {var routesmap = {' Module1 ': ' module1/controller1.js ',//original should    is a method name, where trickery is changed to module path ' Module2 (/:name) ': ' Module2/controller2.js ', ' *actions ': ' DefaultAction '}; var Router = Backbone.Router.extend ({routes:routesmap, defaultaction:function () {Console.log            (' 404 ');        Location.hash = ' module2 ';    }    });    var router = new Router (); Use the on route to take over routing logic, where route is the value Router.on (' route ', function (route, params) {require ([route], function (Controller) {if (Router.currentcontroller && router.currentcontroller!== Controller) {Router.curren            Tcontroller.onroutechange && Router.currentController.onRouteChange ();            } Router.currentcontroller = controller;     Controller.apply (null, params);    Each module convention returns controller});    }); return router;}); 

The above code, the routing table out, the purpose is to be put into the index.html, you can do straight out of the server, maintain 0 cache, easy to achieve the control of the external network version.

In addition router, there is no function for each route, while the Key/value in the routing table changes to a real string-the module path.

Thanks to backbone's robustness, I began to think this will be an error, the result backbone did not find the corresponding function to stop execution, good, like one.

Instead of a corresponding function, the route event handler is replaced.

In the processor, the value of the configuration table is used, the corresponding module is pulled, and the corresponding controller is called. With this little trick, everyone is free to play, configured into various strings, multiple controllers are assembled in a Requirejs module and so on ...

In addition, there is a contract controller in the Onroutechange interface, used to receive the notification of routing switch, to do some destruction work.

Take a look at the new controller code:

define ([' Module2/model2 ', ' module2/view2 '], function (Model, View) {    var controller = function (name) {        var Model = new Model ();        Name && model.set ({            name:name               //Set default property value        });        var view = new View ({Model:model});        View.render ();      Initializes the interface Model.fetch () using the default properties defined by the model        ;          Pull CGI and so on, get the data, then trigger the event, the interface receives the message to do the corresponding action        Controller.onroutechange = function () {            console.log (' change ');  Can do some destruction work, such as view.undelegateevents ()            view.undelegateevents (    );}; return controller;});

At this point, the completion, multi-person development, need to modify the route, only need to modify a configuration, not write any logic here, using the SVN merge function, easy to complete collaborative development.

This article is code: Https://github.com/kenkozheng/HTML5_research/tree/master/BackboneRequireJS

On HTML5 single page Architecture (ii)--backbone + Requirejs + zepto + underscore

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.