Jquery 重构Me:Backbone.js子视图渲染优化

Jquery 重构Me:Backbone.js子视图渲染优化,jquery,model-view-controller,backbone.js,Jquery,Model View Controller,Backbone.js,我正在使用Backbone.js v0.5.5开发一个单页应用程序,在该应用程序的一个“页面”上,我有一个主视图(audioOnDemand)和两个辅助视图(audioPlay和audioNav)。我希望二级视图是可路由的,例如,用户可以转到“#audio_ondemand”、“audio_ondemand/id/:id”或“#audio_ondemand/page/:page”,然后加载所有内容。为了实现这一点,我不得不在我的控制器/路由器中添加一些jQuery代码,这让我感觉非常不舒服。这就

我正在使用Backbone.js v0.5.5开发一个单页应用程序,在该应用程序的一个“页面”上,我有一个主视图(audioOnDemand)和两个辅助视图(audioPlay和audioNav)。我希望二级视图是可路由的,例如,用户可以转到“#audio_ondemand”、“audio_ondemand/id/:id”或“#audio_ondemand/page/:page”,然后加载所有内容。为了实现这一点,我不得不在我的控制器/路由器中添加一些jQuery代码,这让我感觉非常不舒服。这就是我来这里的原因,看看有没有更好的办法

这是我的控制器/路由器

audio_ondemand: function(splat) {
    this._audioondemandview = new AudioOnDemandView();
    this._audioondemandview.render();

    if (splat) {
        var id_patt = /id\/(\d*)/i;
        var id = id_patt.exec(splat);
        id = (id) ? id[1] : null;
        //console.log("id is: "+id);

        var page_patt = /^page\/(\d*)/i;
        var page = page_patt.exec(splat);
    }
    page = (page) ? page[1] : 1;
    //console.log("page is: "+page);

    if (id) {
        setTimeout('app.play_audio("'+id+'");', 500);
    }

    setTimeout('app.audio_nav("'+page+'");', 500);
},

audio_nav: function(page) {
    var nav_holder = $('#pagination-nav');
    if (nav_holder.length == 0) {
        app.audio_ondemand('page/'+page);
    } else {
        var audios = this._audios;
        var nav = new AudioNavView({el: nav_holder, audios: audios, page: page});
        nav.render();
    }
},

play_audio: function(id) {
    var audio_holder = $('#audio-player');
    if (audio_holder.length == 0) {
        app.audio_ondemand('id/'+id);
    } else {
        var audio = this._audios.getByUid(id);
        this._audioview = new AudioView({el: $('#audio-player'), model: audio});
        this._audioview.render();
    }
}
这些视图没有做任何特别的事情,但下面是我的“主”模板(使用jQuery模板):


音频随需应变
正在播放
我想现在你已经陷入困境了。主视图必须在次视图之前呈现,否则次视图没有要附加的适当DOM元素。但是,如果我希望二级视图是可路由的,我必须执行“hacky”jQuery检查,以查看主视图是否已渲染

感谢您的任何想法或建议,如果您需要更多详细信息,请告诉我

更新:因为这可能有助于更好地表达想法,下面是导航视图和模板

<script id="audioNavTmpl" type="text/x-jquery-tmpl">
    {{if prev > 0}}<a href="#audio_ondemand/page/${prev}">Prev</a> | {{/if}}${current}{{if next}} | <a href="#audio_ondemand/page/${next}">Next</a>{{/if}}
</script>

<script id="audioTmpl" type="text/x-jquery-tmpl">
    <div class="audio-holder">
        <div class="title-author">
            <div class="title"><a href="#audio_ondemand/id/${attributes.uid}">${attributes.title}</a></div>
            <div class="author">${attributes.author}</div>
        </div>
        <div class="topic-date">
            <div class="topic"><strong>${attributes.topic}</strong></div>
            <div class="date">${attributes.date}</div>
        </div>
    </div>
</script>

var AudioNavView = Backbone.View.extend({
audioNavTemplate: $('#audioNavTmpl').template(),
audioTemplate: $('#audioTmpl').template(),

initialize: function(options) {
    this.audios = options.audios.models;

    var temp_page = parseInt(options.page);
    this.page = [
        {
            prev: temp_page - 1,
            current: temp_page,
            next: temp_page + 1
        }
    ];

    this.slice_start = (temp_page - 1) * 11;
    this.slice_end = temp_page * 11;

    this.audios = this.audios.slice(this.slice_start, this.slice_end);

    if (this.audios.length <= 11) {
        this.page[0]["next"] = false;
    }
},

render: function() {
    //console.log(this.page);
    this.el.empty();
    var sg = this;
    $('#audios-holder').fadeOut('fast', function() {
        $.tmpl(sg.audioNavTemplate, sg.page).appendTo(sg.el);
        $(this).empty();
        $.tmpl(sg.audioTemplate, sg.audios).appendTo($(this));
        $(this).fadeIn('fast');
    });
    return this;
}

{{if prev>0}}}{{/if}}${current}{{if next}}}{/if}}
${attributes.author}
${attributes.topic}
${attributes.date}
var AudioNavView=Backbone.View.extend({
audioNavTemplate:$('#audioNavTmpl').template(),
audioTemplate:$('#audioTmpl').template(),
初始化:函数(选项){
this.audios=options.audios.models;
var temp_page=parseInt(options.page);
此页=[
{
上一页:临时第1页,
当前:临时页面,
下一页:临时页面+1
}
];
this.slice_start=(temp_page-1)*11;
this.slice_end=temp_第11页;
this.audios=this.audios.slice(this.slice\u开始,this.slice\u结束);

如果(this.audios.length据我所知,您的嵌套视图只有1层深(即,主视图有子视图,但没有任何子视图),因此您可以通过在路由器的initialize方法中呈现主视图,然后在路由回调中呈现任何子视图来解决问题。下面是一个非常简单的示例。我使用的是下划线的模板引擎,但其思想与jQuery的模板引擎相同

模板:

<script id="audioOnDemandTmpl" type="text/x-jquery-tmpl">
    <div class="sub-header">
        <strong>Audio</strong> On Demand
    </div>
    <div id="currently-playing">
        <div id="currently-header">Currently Playing</div>
        <div id="current-holder"></div>
    </div>
    <div id="audio-player"></div>
    <div id="pagination-nav"></div>
    <div id="audios-holder"></div>
</script>
<script id="master-view" type="text/html">
    Hi, this is a master view. <a href="#subview">Render sub-view now.</a>
    <div id="subview-container"></div>
</script>
<script id="sub-view" type="text/html">
    Hi, this is a sub view.
</script>

当您将浏览器指向该页面时,您将首先只看到主视图。单击链接,您将看到url哈希更改为#子视图,子视图将被呈现。然后您可以重新加载页面(url哈希仍位于#子视图),子视图将在主视图之后正确呈现。

主视图之间的区别视图和“次要”视图对我来说不清楚。它们看起来都像可路由视图。在我看来,路由器应该有当前呈现的所有视图的轨迹(路由本身是一个理想的键),并作为路由显示或构造并显示视图。为导航视图添加了更多代码示例,以更好地了解正在进行的操作。
var MasterView = Backbone.View.extend({
    _template: _.template($('#master-view').html()),
    render: function () {
        $(this.el).html(this._template());
        return this;
    }
});

var SubView = Backbone.View.extend({
    _template: _.template($('#sub-view').html()),
    render: function () {
        $(this.el).html(this._template());
        return this;
    }
});

var MyRouter = Backbone.Router.extend({

    initialize: function () {
        var view = new MasterView();
        $('body').append(view.render().el);
    },

    routes: {
        '': 'home',
        'subview': 'subview'
    },

    home: function () {
        // probably don't have to do anything
    },

    subview: function () {
        // render subview here
        var view = new SubView({ el: $('#subview-container')[0] });
        view.render();
    }
});

new MyRouter();
Backbone.history.start();