事务就很简短,》斟酌了angular+requirejs的三个差不离框架结构新万博manbetx官网

正文转发自:http://www.cnblogs.com/kenkofox/p/4650310.html

本文转发自:http://www.cnblogs.com/kenkofox/p/4648472.html

不过,这一篇,笔者想进一步追究一下那多个框架的利弊,其余,再进一步,抛开那三个框架,回到本真,自个儿搞个简易的路由一样能够兑现单页面。

上一篇《浅谈HTML5单页面架构(一)——requirejs + angular +
angular-route
》研商了angular+requirejs的3个粗略架构,这一篇接二连三来探视backbone怎么着跟requirejs结合。

以此对于刚(Yu-Gang)做前端开发的新校友来说就最为可是了,如若一来到岗位就一大堆angular、backbone、requirejs,看资料都看一两周。其实咱们最熟习的东西依旧万分英镑$,用比索能消除的题材,就无须难为到angular、backbone三叔了。

一样地,项目架构好与坏不是说用了略微牛逼的框架,而是怎么合理运用框架,让项目支付更通畅,代码更便于管理。那么带着这一个指标,我们来继续商量backbone。

 

 

优先表达,由于本人的业务范围窄,不必然能把angular和backbone的效应都用3次,所以以下的分析大概一面之识,欢迎大家座谈。

先是,来看看整个项目布局。

angular优点:

新万博manbetx官网 1

  • 无敌的数额双向绑定
  • View界面层组件化
  • 嵌入的强硬服务(例如表单校验)
  • 路由简单

跟上一篇angular类似,libs里多了underscore和zepto。多个根目录文件:

angular缺点:

  • index.html:唯一的html
  • main.js:requirejs的布署,程序的入口
  • router.js:整个app或网站的单页面路由布署
  • 引入的js较大,对运动端的话有点吃不消
  • 语法复杂,学习花费高

 

backbone优点:

率先步,依旧建立单页面唯一的HTML

  • 引入的js较小
  • 清晰MVC分层
  • Model层事件机制
  • 路由不难而且有利于扩大

新万博manbetx官网 2

backbone缺点:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>Backbone & Requirejs</title>
</head>
<body>
<div id="container"></div>
<script data-baseurl="./" data-main="main.js" src="libs/require.js" id="main"></script>
</body>
</html>
  • MVC有点愚拙,有时候觉得麻烦
  • 从不双向绑定,界面修改只可以靠本人
  • view切换时,没有丰裕便利的轩然大波通报(要团结监听route)

新万博manbetx官网 3

骨子里,这八个框架都不行美丽,不过,在其实工作中,不必然百试百灵,因为有局地运动端的单页面web,业务就很简短,只是路由分别切换来多少个子模块,各样子模块基本都以拉一回数据,显示给用户,很少用户交互从而修改数据,改变视图的效益。

backbone没有在dom属性上做作品,大家仍旧按原生的可能说熟知的方法写东西。这里定义了叁个container
div作为backbone的视图。

对于那种气象,使用angular未免有点杀鸡用牛刀的感到,而backbone尽管小巧了累累,可是模型的效劳也是浪费的。

下一场引入requirejs,data-main表示主程序入口。

因此,在那边,笔者想追究一下,能或无法抛开那五个框架,只索取大家着力所需,建立多个更简单的架构呢?

 

经历看来,一些类库是必不可少的:

 

  • requirejs:模块划分
  • zepto:移动端的jquery
  • underscore:便捷的基本功艺术,包蕴模版template、each、map等等
  • 路由库:那里先使用director.js,不过这个人并不曾backbone和angular的路由好用,小说最后再来讨论那些难点

第二步,配置main.js

 

新万博manbetx官网 4

投机做一套最简便的架构,思想格外简单:

(function (win) {
    //配置baseUrl
    var baseUrl = document.getElementById('main').getAttribute('data-baseurl');

    /*
     * 文件依赖
     */
    var config = {
        baseUrl: baseUrl,           //依赖相对路径
        paths: {                    //如果某个前缀的依赖不是按照baseUrl拼接这么简单,就需要在这里指出
            zepto: 'libs/zepto.min',
            jquery: 'libs/zepto.min',
            underscore: 'libs/underscore',
            backbone: 'libs/backbone',
            text: 'libs/text'             //用于requirejs导入html类型的依赖
        },
        shim: {                     //引入没有使用requirejs模块写法的类库。backbone依赖underscore
            'underscore': {
                exports: '_'
            },
            'jquery': {
                exports: '$'
            },
            'zepto': {
                exports: '$'
            },
            'backbone': {
                deps: ['underscore', 'jquery'],
                exports: 'Backbone'
            }
        }
    };

    require.config(config);

    //Backbone会把自己加到全局变量中
    require(['backbone', 'underscore', 'router'], function(){
        Backbone.history.start();   //开始监控url变化
    });

})(window);
  1. 运维程序
  2. 监听路由
  3. 路由变化,映射到相应的拍卖逻辑,加载对应的模块
  4. 模块加载成功,修改dom,也正是视图
  5. 页面跳转时,移除上3个模块,加载下一个模块,也便是回来第③点

新万博manbetx官网 5

 

有关requirejs的语法,照旧不多说,大家自个儿去官网看呢。这几个是基础。

简短的思绪,让架构相当简洁,新团队成员来到能够轻松上手,而angular和backbone的框架结构,少说得二 、3天才能融入贰个已有品种中去。

运用backbone,不得不强调requirejs的shim配置。backbone这几个土匪,要的东西多了,要给她“鞋”(underscore),还要给他美元$(jquery)。

接下去,大家切实看看咋办。

鉴于终端应用jquery就太庞大了,所以那里做了个小把戏,用zepto充当jquery,骗了胡子一把。用几张越南社会主义共和国盾,戏称是欧元,没悟出土老冒也信了。

 

有个地点须要注意的是,

第一步,还是index.html

甭管在哪个地方用requirejs引入backbone后,就会多了Backbone和$那多少个全局变量,所以持续再采纳backbone就不须要约束于requirejs的AMD写法了。适当放松透透气也是好的。

新万博manbetx官网 6

配置好凭借关系后,就能够引入router,并调用关键的

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Underscore & Director & Requirejs</title>
</head>

<body>
<div id="container"></div>
<script data-baseurl="./" data-main="main.js" src="libs/require.js" id="main"></script>
</body>
</html>
Backbone.history.start

新万博manbetx官网 7

初始路由督察。

以此附近两篇没什么差异。requirejs引入main.js作为程序入口

 

 

第三步,配置router,路由表

其次步,main.js配置requirejs的倚重关系,并运营webapp

新万博manbetx官网 8

新万博manbetx官网 9

define(['backbone'], function () {

    var Router = Backbone.Router.extend({

        routes: {
            'module1': 'module1',
            'module2(/:name)': 'module2',
            '*actions': 'defaultAction'
        },

        //路由初始化可以做一些事
        initialize: function () {
        },

        module1: function() {
            var url = 'module1/controller1.js';
            //这里不能用模块依赖的写法,而改为url的写法,是为了grunt requirejs打包的时候断开依赖链,分开多个文件
            require([url], function (controller) {
                controller();
            });
        },

        //name跟路由配置里边的:name一致
        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);  //这里route是路由对应的方法名
    });

    return router;    //这里必须的,让路由表执行
});
(function (win) {
    //配置baseUrl
    var baseUrl = document.getElementById('main').getAttribute('data-baseurl');

    /*
     * 文件依赖
     */
    var config = {
        baseUrl: baseUrl,           //依赖相对路径
        paths: {                    //如果某个前缀的依赖不是按照baseUrl拼接这么简单,就需要在这里指出
            director: 'libs/director',
            zepto: 'libs/zepto.min',
            underscore: 'libs/underscore',
            text: 'libs/text'             //用于requirejs导入html类型的依赖
        },
        shim: {                     //引入没有使用requirejs模块写法的类库。
            underscore: {
                exports: '_'
            },
            zepto: {
                exports: '$'
            },
            director: {
                exports: 'Router'
            }
        }
    };

    require.config(config);
    require(['zepto', 'router', 'underscore'], function($, router, _){
        win.appView = $('#container');      //用于各个模块控制视图变化
        win.$ = $;                          //暴露必要的全局变量,没必要拘泥于requirejs的强制模块化
        win._ = _;
        router.init();                      //开始监控url变化
    });


})(window);

新万博manbetx官网 10

新万博manbetx官网 11

Backbone.Router.extend那些语法,相信就无须多说了,说多了也说不清楚,大家去官网才是王道:http://backbonejs.org

director.js没有英特尔写法,照旧坚守shim的办法引入。其它,由于$和_的使用率太高,所以那里直接当面为全局变量。

backbone的路由写法跟angular类似,但对于可选参数的写法是不均等的。angular使用:param?的方法,而backbone使用(:param),哪个格局好,见仁见智吧。

除外,还加了appView变量,目标是造福种种子模块修改界面。

那边定义了2个默许路由,和四个业务路由。

 

原理很简短,便是蒙受module1的哈希(hash)就执行后面那一个字符串对应的函数

其三步,router.js配置路由

估价大家已经知道这家伙。而上述代码中,关键区别点是,那里运用了requirejs做了模块化,路由跳转后做的持有逻辑都在其它的js中定义。

此地运用的路由类库是director(https://github.com/flatiron/director),相对简单的路由,但实在对于大家那几个顺序来说,貌似还不够精简。先凑合着吧。

重在的重庆大学,这里运用了url,而且是单身变量的方法安排模块的js,而不是

director官网给出的演示也一定简单,便是“路径”对应“函数”,卓殊明晰而且实用的法门。

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

新万博manbetx官网 12

指标是grunt做requirejs打包时,能切断两侧的js,不要合并在1个大js中。

      var author = function () { console.log("author"); };
      var books = function () { console.log("books"); };
      var viewBook = function (bookId) {
        console.log("viewBook: bookId is populated: " + bookId);
      };

      var routes = {
        '/author': author,
        '/books': [books, function() {
          console.log("An inline route handler.");
        }],
        '/books/view/:bookId': viewBook
      };

      var router = Router(routes);

      router.init();

 

新万博manbetx官网 13

再此外,大家可以善用一下router.on(‘route’,
function)那么些接口,及时做一下风云解绑和有个别清理工科作。

 

 

来看望大家友好的本子:

第六步,写贰个简约模块

新万博manbetx官网 14

controller1.js

define(['director', 'underscore'], function (Router, _) {

    //先设置一个路由信息表,可以由html直出,纯字符串配置
    var routes = {
        'module1': 'module1/controller1.js',
        'module2/:name': 'module2/controller2.js'     //director内置了普通必选参数的写法,这种路由,必须用路径“#module2/kenko”才能匹配,无法缺省
//        'module2/?([^\/]*)/?([^\/]*)': 'module2/controller2.js'    //可缺省参数的写法,其实就是正则表达式,括号内部分会被抽取出来变成参数值。backbone做得比较好,把这个语法简化了
                                                                    //  “ /?([^\/]*) ”  这样的一段表示一个可选参数,接受非斜杠/的任意字符
    };

    var currentController = null;

    //用于把字符串转化为一个函数,而这个也是路由的处理核心
    var routeHandler = function (config) {
        return function () {
            var url = config;
            var params = arguments;
            require([url], function (controller) {
                if(currentController && currentController !== controller){
                    currentController.onRouteChange && currentController.onRouteChange();
                }
                currentController = controller;
                controller.apply(null, params);
            });
        }
    };

    for (var key in routes) {
        routes[key] = routeHandler(routes[key]);
    }

    return Router(routes);
});

新万博manbetx官网 15

新万博manbetx官网 16

define(['module1/view1'], function (View) {

    var controller = function () {
        var view = new View();
        view.render('kenko');
    };
    return controller;
});

那里把director的路由配置修改了一晃,原来只好接受<String,
Function>那样的key
value对,但参考以前backbone篇,更好措施应该是让路由表尽量唯有字符串配置,不要写逻辑(函数)。

新万博manbetx官网 17

之所以,上述代码中,多了八个routeHandler,指标正是确立闭包,把string(配置)转换为一个闭包函数。

view1.js

结果,运维效果就是,碰到2个路由,就依照安插加载对应的子模块代码。后续实际执行怎么着,由子模块本身支配。那样main/router就能彻底跟子模块解耦。

新万博manbetx官网 18

 

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;
});

第陆步,建立一个模块

新万博manbetx官网 19

tpl.html

tpl.html

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

controller1.js

 模版的写法跟angular不一样等,选用的是更宽广的办法,jquery、underscore都以那些格局。不难说正是<%%>包蕴js代码,用=等号能够直接出口变量值。

新万博manbetx官网 20

此间做了最不难易行的MVC,M只是二个值name,C就是controller了,V正是view1。

define(['text!module1/tpl.html'], function (tpl) {

    var controller = function () {
        appView.html(_.template(tpl, {name: 'kenko'}));
    };
    return controller;
});

View1的写法需求坚守Backbone的语法,不然那里用Backbone就没意义了。el指向对应的视图dom成分,用的是css采取器,在View中能够选取this.$el获取到这几个jquery风格变量。render是自定义的函数。

新万博manbetx官网 21

到此地,运营程序,就能看到module1的机能了。

自家觉得能落到实处工作逻辑的前提下,越简单的架构就越好,便于传承和保险。

新万博manbetx官网 22

controller就是其一子模块要做的逻辑,appView是全部视图根节点,想怎么玩就怎么玩,这对于不熟悉angular、backbone的同班最爽但是了。

 

此处根本是接纳了requirejs做模块化和依靠加载,并用了underscore的模板库template。

第⑤步,再写第三个模块,严俊MVC的

 

 model2.js

第4步,再做三个模块,加上部分销毁接口

新万博manbetx官网 23

tpl.html

define([], function () {
    var Model2 = Backbone.Model.extend({

        //模型默认的数据
        defaults: function () {
            return {
                name: "noname"
            };
        },

        // 定义一些方法
        fetch: function () {
            var o = this;
            //可以做一些http请求
            setTimeout(function(){
                o.set({name:'vivi'});
                o.trigger('nameEvent');     //向view触发事件
            }, 1000);
        }

    });

    return Model2;
});
<div>
    Here is module 2. My name: <%=name %><br>
    <button>click me!</button>
    <a href="#module1">turn to module 1</a>
</div>

新万博manbetx官网 24

controller2.js

Model的语法也是服从Backbone必要了,defaults是暗中同意属性值。读写这几个属性,须要经过model.get/set接口,不然就算用toJSON重返整个对象,再不然就解剖式的接纳model.attributes.xxx。

新万博manbetx官网 25

fetch是自定义方法,模拟http请求,那是很健康的做法了,可是这一个事例没利用backbone的rest化接口。

define(['text!module2/tpl.html'], function (tpl) {

    var controller = function (name) {
        appView.html(_.template(tpl, {name: name?name:'vivi'}));

        $('button').on('click', function clickHandler() {
            alert('hello');
        });

        controller.onRouteChange = function () {
            console.log('change');      //可以做一些销毁工作,例如取消事件绑定
            $('button').off('click');   //解除所有click事件监听
        };
    };

    return controller;
});

数码再次回到后,使用backbone内建的trigger触发事件,文告监听者,也正是view层了。backbone跟angular最大分别正是,backbone不关怀view层的组件化,更关爱的是model和事件机制,而angular则不主要提事件机制,接纳双向绑定把数据更新的破事隐藏起来。各有各的功利,见仁见智吧。

新万博manbetx官网 26

 

 

view2.js

至此,整个大约的框架就形成了。

新万博manbetx官网 27

大道至简,笔者卓殊喜爱那样归纳的框架结构。希望对新手朋友有所帮衬。

define(['text!module2/tpl.html'], function (tpl) {

    var View2 = Backbone.View.extend({
        el: '#container',

        events: {
            'click button': 'clickSpan'     //使用代理监听交互,好处是界面即使重新rander了,事件还能触发,不需要重新绑定。如果使用zepto手工逐个元素绑定,当元素刷新后,事件绑定就无效了
        },

        initialize: function () {
            this.model.on('nameEvent', this.render, this);      //监听事件
        },

        render: function () {
            this.$el.html(_.template(tpl, {name: this.model.get('name')}));     //类似java的DAO思想,一切通过get set操作
        },

        clickSpan: function (e) {
            alert('you clicked the button');
        }
    });

    return View2;
});

 

新万博manbetx官网 28

最后,关于director的路由,要吐槽一下,这一个并不曾backbone那一个这么好用,它并未内置的缺省参数写法,须要自个儿知道正则表明式,写复杂的([?*。参照上面router.js的代码。

随着,我们看看backbone3个典型视图怎么玩。先看initialize方法,这么些是new
View2()时先进行的早先化逻辑。

路由十分的真相,其实是正则表明式的exec匹配和领取参数。笔者三番五次会再整五个粗略好用的路由,参考backbone的形式,猛击那里:http://www.cnblogs.com/kenkofox/p/4650824.html

大家在此处监听name伊芙nt那个消息,也正是model2抛出的事件。收到这么些布告,就革新界面。逻辑很简短。

 

此处有三个相比好用的events,交互事件代理体制。

正文代码:https://github.com/kenkozheng/HTML5\_research/tree/master/UnderscoreRequireJS

咱俩不需求单独的写zepto
on对dom分别绑定事件,只要求在那边配置3个events映射表即可。

click button等同于zepto的

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

此间绑定的就是clickSpan事件。

以此事件代理体制,好处是,在路由切换的时候,可以轻松移除事件监听。

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

新万博manbetx官网 29

define(['module2/model2', 'module2/view2'], function (Model, View) {

    var controller = function (name) {
        var model = new Model();
        name && model.set({
            name:name               //设置默认的属性值
        });
        var view = new View({model:model});
        view.render();      //利用Model定义的默认属性初始化界面
        model.fetch();          //拉取cgi等等,获取数据,再触发事件,界面收到消息做相应的动作
    };

    return controller;
});

新万博manbetx官网 30

controller负责的做的事便是揉合数据,放到view中。先让view用私下认可数据渲染,再让model去拉取最新数据,最终经过事件机制立异界面。

当然,那些controller并不是backbone规范,咱们能够痛快表明。

说到底回来路由表中,当hash变成module2时,就推行:

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

迄今,简单的requirejs+backbone框架已经落成了。除了router的耦合度很高外,每种模块逻辑代码都曾经独立,app能够轻松达成按需加载。

那么追求机制的骚年,要停下来呢?按这一个方案,在实际上付出中,四个人日常会在router那个点子上海高校打入手,那里的配置化还不够完善。

 

 

第④步,优化router,彻底配置化

现有方案的难题是,router中除去写路由安顿外,还须要充足相应的function,那样既冗余又不难争辩,那么是还是不是监听route事件,做1个联结的路由处理器?贰个处理函数,处理整个路由响应。

新万博manbetx官网 31

define(['backbone'], function () {

    var routesMap = {
        'module1': 'module1/controller1.js',            //原来应该是一个方法名,这里取巧改为模块路径
        '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();
    //彻底用on route接管路由的逻辑,这里route是路由对应的value
    router.on('route', function (route, params) {
        require([route], function (controller) {
            if(router.currentController && router.currentController !== controller){
                router.currentController.onRouteChange && router.currentController.onRouteChange();
            }
            router.currentController = controller;
            controller.apply(null, params);     //每个模块约定都返回controller
        });
    });

    return router;
});

新万博manbetx官网 32

上述代码,把路由表抽离,目标是足以停放index.html中,能够在服务器做直出,保持0缓存,轻松达成对外网版本的支配。

除此以外Router中,没有了各样路由对应的函数,而路由表中的key/value改为确实含义的一个字符串——模块路径。

谢谢backbone的身心健康,笔者起来还觉得这么必然会报错,结果backbone没找到呼应函数就终止实施了,不错,赞二个。

不曾了三个个的对应函数,取而代之的是route事件处理器。

总括机中,利用了配置表的value,拉取对应的模块,并调用相应的controller。有了那些小把戏,我们能够自由发挥了,配置成种种字符串,几个controller集合在三个requirejs模块中等等。。。

除此以外,那里约定controller中有onRouteChange的接口,用于接收路由切换的关照,好做一些销毁工作。

来探视新的controller代码:

新万博manbetx官网 33

define(['module2/model2', 'module2/view2'], function (Model, View) {

    var controller = function (name) {
        var model = new Model();
        name && model.set({
            name:name               //设置默认的属性值
        });
        var view = new View({model:model});
        view.render();      //利用Model定义的默认属性初始化界面
        model.fetch();          //拉取cgi等等,获取数据,再触发事件,界面收到消息做相应的动作

        controller.onRouteChange = function () {
            console.log('change');  //可以做一些销毁工作,例如view.undelegateEvents()
            view.undelegateEvents();
        };
    };

    return controller;
});

新万博manbetx官网 34

由来,大功告成,多少人支付中,须要修改路由,只须求修改3个安顿,不在那里写任何逻辑,利用svn合并功用,轻松做到联合开发。

正文代码:https://github.com/kenkozheng/HTML5\_research/tree/master/BackboneRequireJS