乐道

乐在其中,道不出口

 

seajs+backbone实现单页面应用模块自动加载

这不是一篇介绍seajsbackbone的文章,如果对这两个库不太了解,可以去他们官网查阅。欢迎转载,注明出处:http://www.cnblogs.com/mapping/archive/2013/03/03/2941812.html

单页面应用(spa:single page application)的业务逻辑多是比较复杂的,如果一次性把整个spa用到的文件加出估计得卡到不行,使用seajs可以很好的实现按需加载;使用backbone可以比较方便地实现数据、逻辑、表现分离,有人称他为前端的MVC框架,又有人说他不算是MVC,这里就不做讨论。

本文主要讲解如何使用这两个库实现spa里模块根据路由的自动加载

首先建立整个app的文件结构,如图:

根目录:autoload;

根目录下index.html作为app首页及入口,js目录放置app的js文件;

js目录中base放置基本的依赖js库,main目录放置app的框架代码,module目录就是用来放置各个模块的功能js啦;

下一步就是把underscore、jquery、backbone源码包装成CMD规范(或参考这里)的文件,underscore和backbone已内置AMD接口,jquery最新版也已添加AMD的exports接口,如果是旧版的jquery需要return $.noConfilt(true),如下:

base/jquery.js

define('base/jquery', function(require) {
      //jquery官方源码
      return $.noConflict(true);
});

base/underscore.js

define('base/underscore', function(require, exports, module) {
    //underscore官方源码
});

base/backbone.js

define('base/backbone', ['underscore', '$'], function(require, exports, module) {
    this._ = require('underscore');
    this.jQuery = require('$');
    //backbone官方源码
});

 

用过backbone的人都知道,使用他的路由功能来做spa非常方便,页面较少的情况还可以,如果页面一多,每次都要改路由配置;虽然backbone也支持多个Router对象,但每次新增模块还是避免不了增加对应的router规则,就需要维护多个文件;而且配置的router越多,出现重复的概率就会越大,所以我们就要制定一个统一的router规则来实现模块自动加载,其实就是把模块的功能文件路径及文件名作为参数传给router处理函数,然后使用seajs的require.async方法去加载对应的文件,如下:

main/app.js

define('main/app', function(require, exports) {
    var Backbone = require('backbone');

    //配置路由
    var autoRouter = Backbone.Router.extend({
        routes: {
            '': 'home',
            'at/:module/:action(/*condition)': 'loadmodule'
        },
        home: function() {
            this.loadmodule('home', 'index');
        },
        //按照at/module/action(/conditions)格式的請求自動加載模塊
        loadmodule: function(md, ac, con) {
            //将参数字符串'a:123/b:456'转换为json对象{a:123, b:456}
            var cj = {};
            if(con && con.indexOf(':') > -1) {
                con.replace(/(\w+)\s*:\s*([\w-]+)/g, function(a, b, c) {
                    b && (cj[b] = c);
                });
            } else {
                cj = con;
            }
            //加载module目录下对应的模块
            require.async(['module', md, ac].join('/'), function(cb) {
                if(cb) {
                    cb(cj);
                } else {
                    alert('模塊加載失敗!');
                }
            })
        }
    });

    //定义全局变量App
    window.App = {
        Models: {},  
        Views: {},  
        Collections: {},
        initialize: function() {
            new autoRouter();
            Backbone.history.start();
        }  
    };

    exports.run = App.initialize;
})

这里给路由一个非常yy的名称at(把模块@出来^_^),任何匹配'at/:module/:action(/*condition)'规则的路径都会调用loadmodule方法,在这里condition是一个可选参数,如果要传递多个参数可以写成‘a:123/b:456’的格式,loadmodule方法会将这个格式字符串转换成json方便模块里的代码调用

autoRouter对象中还有一个空路由用于app的启动,实际这条路由相当于'at/home/index';我们现在要做的就是在module目录里加模块文件了:

module/home/index.js

define('module/home/index', ['backbone'], function(reuqire) {
    var Backbone = reuqire('backbone');
    App.Models.Home = Backbone.Model.extend({});

    App.Collections.Home = Backbone.Collection.extend({
        model: App.Models.Home
    });

    App.Views.Home = Backbone.View.extend({
        el: '#container',
        initialize: function(c) {
            this.Collections = c;
            this.render();
        },
        render: function() {
            var html = '';
            this.Collections.each(function(m) {
                html += '<div><a href="' + m.get('link') + '">' + m.get('name') + '</a></div>';
            });
            this.$el.html(html);
        }
    })

    return function() {
        //模拟数据
        var hc = new App.Collections.Home();
        hc.add([
            {'name': '加载模块A', 'link': '#at/m/a/name:moduleA/other:nothing'},
            {'name': '加载模块B', 'link': '#at/m/b'}
        ]);
        new App.Views.Home(hc);
    }
});

注意:每个自动加载的模块都必须返回一个function,用于loadmodule方法callback,否则,提示模块加载失败;

这个function一般都用来将加出来的模块初始化,如上面的index.js,这里会在页面添加两个链接用来加载模块A和模块B,再看下页面index.html的代码:

index.html

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>seajs+backbone实现单页面模块自动加载</title>
</head>
<body>
    <div id="container">
    </div>
    <script type="text/javascript" src="js/base/sea.js"></script>
    <script type="text/javascript">
    seajs.config({
        base: '/js/',
        alias: {
            //基础库
            'underscore': 'base/underscore',
            '$': 'base/jquery',
            'backbone': 'base/backbone',
            'app': 'main/app'
        }
    });
    seajs.use('app', function(app) {
        app.run();
    });
    </script>
</body>
</html>

到这里,整个app已经可以运行啦

但是点击“加载模块A”或“加载模块B”都会弹出模块加载失败,那是因为我们还没添加这两个模块

module/m/a.js

define('module/m/a', ['$'], function(reuqire) {
    var $ = reuqire('$');
    return function(c) {
        alert('moduleA加载成功,参数:' + $.param(c));
    }
});

加载模块A运行结果:

 

module/m/b.js

define('module/m/b', ['$'], function(reuqire) {
    return function(c) {
        alert('moduleB加载成功,参数:' + c);
    }
});

加载模块B运行结果:

 

好了,我们整个demo已经结束了,本文只是简单介绍一种模块自动加载的思路。

如果是做一个完整的spa,那么还有模块依赖的样式css文件、模板html文件需要加载以及从api拉取数据,这些功能借助seajs和backbone都可以比较方便实现,加载模板html文件你或许需要seajs的plugin-text.js插件

 

 本文demo打包下载:autoload.zip

 

欢迎转载,注明出处:http://www.cnblogs.com/mapping/archive/2013/03/03/2941812.html

 

 

 

 

posted on 2013-03-03 19:41  乐道  阅读(8360)  评论(5编辑  收藏  举报

导航