BackboneJS呈现问题 [英] BackboneJS Rendering Problems

查看:95
本文介绍了BackboneJS呈现问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在过去的六个月里,我一直在与骨干。前两个月分别瞎搞,学习,搞清楚如何我要组织我code周围。在接下来的4个月里捣走生产配合应用。不要误会我的意思,骨干为我节省了从客户端code的数千线的混乱,和以前的标准,但它使我在更少的时间做更多的事情浮夸,开辟的一个全新的堆栈问题。对于所有我这里养问题有一些感觉就像黑客简单的解决方案,或只是感觉的错误的。我答应一个300点赏金一个真棒的解决方案。这里所说:

For the last six months I've been working with Backbone. The first two months were messing around, learning and figuring out how I want to structure my code around it. The next 4 months were pounding away a production-fit application. Don't get me wrong, Backbone has saved me from the thousands-lines mess of client side code that were the standard before, but it enabled me to do more grandiose things in less time, opening up a complete new stack of problems. For all the questions I raise here there are simple solutions that feels like hacks or just feel wrong. I promise a 300 points bounty for an awesome solution. Here goes:


  1. 加载 - 对于我们的用例(管理面板)悲观同步是坏的。对于一些事情,我需要接受他们之前验证服务器上的东西。我们开始之前,同步事件被合并到主干,

  1. Loading - For our use case (an admin panel) pessimistic syncing is bad. For some things I need to validate things on the server before accepting them. We started out before the 'sync' event was merged into Backbone,

和我们使用这个小code为模仿加载事件:

and we used this little code for mimicking the loading event:

window.old_sync = Backbone.sync

# Add a loading event to backbone.sync
Backbone.sync = (method, model, options) ->
  old_sync(method, model, options)
  model.trigger("loading")

大。它的工作原理如预期,但给人的感觉并不正确。我们结合本次活动所有相关的看法,直到我们收到该模型成功或错误事件显示一个加载图标。有没有更好的,更理智,方式做到这一点?

Great. It works as expected but it doesn't feel correct. We bind this event to all the relevant views and display a loading icon until we receive a success or error event from that model. Is there a better, saner, way to do this?

现在的硬的:


  1. 有太多的东西呈现自己太多 - 比方说,我们的应用程序有标签。每个标签控件的集合。在左侧你得到的集合。你点击一个模型,开始在该中心编辑。您可以更改其名称和preSS选项卡,进入下一个表单项。现在,你的应用程序是一个实时一些东西的告示的差异,运行验证,并自动将更改同步到服务器,没有保存按钮需要!伟大的,但在形式开始H2是相同的名称输入 - 你需要更新。哦,你需要在列表中端修改其名称。 OH,而列表排序本身的名字!

  1. Too many things render themselves too much - Let's say our application have tabs. Every tab controls a collection. On the left side you get the collection. You click a model to start editing it at the center. You change its name and press tab to get to the next form item. Now, your app is a "real time something something" that notices the difference, runs validations, and automatically sync the change to the server, no save button required! Great, but the H2 at the start of the form is the same name as in the input - you need to update it. Oh, and you need to update the name on the list to the side. OH, and the list sorts itself by names!

下面是另一个例子:你想在集合中创建一个新的项目。您preSS的新建按钮,然后你开始填写表格。你马上添加项目到集合?但是,如果你决定放弃它,会发生什么?或者,如果您保存整个集合另一个选项卡上?而且,有一个文件上传 - 你需要保存和同步模型,然后才能开始上传文件(这样你就可以附加文件到模型)。所以一切都开始呈现在颤抖:您保存模型和列表的形式再次呈现自己 - 它现在已同步的,所以你得到一个新的删除按钮,它显示在列表中 - 但现在的文件上传完成上传,所以一切再次开始渲染。

Here's another example: You want to create a new item in the collection. You press the "new" button and you start filling out the form. Do you immediately add the item to the collection? But what happens if you decided to discard it? Or if you save the entire collection on another tab? And, there's a file upload - You need to save and sync the model before you can start uploading the file (so you can attach the file to the model). So everything starts rendering in tremors: You save the model and the list and the form renders themselves again - it's synced now, so you get a new delete button, and it shows in the list - but now the file upload finished uploading, so everything starts rendering again.

添加子视图的组合,一切都开始寻找像费里尼的电影。

Add subviews to the mix and everything starts looking like a Fellini movie.


  1. 这是子视图一路走低 - 这里这个东西一篇好文章。我不能,因为这是一切神圣的爱,找到jQuery插件或DOM事件附加到具有子视图的任何视图的正确道路。地狱随之而来及时。提示听到即将呈现一个漫长而吓坏身边,子视图成为行尸走肉般的或不响应。这是主要的痛点这里实际的错误立场,但我仍然没有一个包罗万象的解决方案。

  1. It's subviews all the way down - Here's a good article about this stuff. I could not, for the love of everything that is holy, find a correct way to attach jQuery plugins or DOM events to any view that has subviews. Hell ensues promptly. Tooltips hear a render coming a long and start freaking around, subviews become zombie-like or do not respond. This is the main pain points as here actual bugs stand, but I still don't have an all encompassing solution.

闪烁 - 渲染速度快。事实上,它是如此之快,我的屏幕看起来像是过癫痫。有时,它是具有再次加载(与其他服务器的来电!)的图像,因此HTML最小化,然后再突然最大化 - 一个CSS宽+高该元素将解决这个问题。有时我们可以用淡入和淡出一个解决这个问题 - 这是在屁股写一个痛苦,因为有时我们重用视图,有时重新创造它

Flickering - Rendering is fast. In fact, it is so fast that my screen looks like it had a seizure. Sometimes it's images that has to load again (with another server call!), so the html minimizes and then maximizes again abruptly - a css width+height for that element will fix that. sometimes we can solve this with a fadeIn and a fadeOut - which are a pain in the ass to write, since sometimes we're reusing a view and sometimes creating it anew.

TL; DR - 我有一个享有和子视图中的骨干问题 - 它呈现过很多次,当它使得它闪烁,子视图取下我的DOM事件,吃我的脑子

TL;DR - I'm having problems with views and subviews in Backbone - It renders too many times, it flickers when it renders, subviews detach my DOM events and eat my brains.

感谢您!

更多细节:用Ruby on Rails的宝石BackboneJS。使用UnderscoreJS模板模板。

More details: BackboneJS with the Ruby on Rails Gem. Templates using UnderscoreJS templates.

推荐答案

为了尽量减少你的DOM层级的完整呈现,你可以设置你的DOM节点特殊,将反映在一个给定的属性更新。

Partial rendering of views

In order to minimize the full rendering of your DOM hierarchy, you can set up special nodes in your DOM that will reflect updates on a given property.

让我们用这个简单的模板下划线,名称的列表:

Let's use this simple Underscore template, a list of names:

<ul>
  <% _(children).each(function(model) { %>
    <li>
        <span class='model-<%= model.cid %>-name'><%= model.name %></span> :
        <span class='model-<%= model.cid %>-name'><%= model.name %></span>
    </li>
  <% }); %>
</ul>

注意类建模&LT;%= model.cid%方式&gt; -name ,这将是我们注射点

我们可以再定义一个基础视图(或修改Backbone.View)与当他们更新适当的值来填充这些节点:

We can then define a base view (or modify Backbone.View) to fill these nodes with the appropriate values when they are updated:

var V = Backbone.View.extend({
    initialize: function () {
        // bind all changes to the models in the collection
        this.collection.on('change', this.autoupdate, this);
    },

    // grab the changes and fill any zone set to receive the values
    autoupdate: function (model) {
        var _this = this,
            changes = model.changedAttributes(),
            attrs = _.keys(changes);

        _.each(attrs, function (attr) {
            _this.$('.model-' + model.cid + '-' + attr).html(model.get(attr));
        });
    },

    // render the complete template
    // should only happen when there really is a dramatic change to the view
    render: function () {
        var data, html;

        // build the data to render the template
        // this.collection.toJSON() with the cid added, in fact
        data = this.collection.map(function (model) {
            return _.extend(model.toJSON(), {cid: model.cid});
        });

        html = template({children: data});
        this.$el.html(html);

        return this;
    }
});

在code将有一些变化,以适应模型而不是一个集合。
小提琴与 http://jsfiddle.net/nikoshr/cfcDX/

委派渲染到子视图可能是昂贵的,它们的HTML片段具有被插入到父的DOM。
看看这个 jsperf测试对比渲染不同的方法

Delegating the rendering to the subviews can be costly, their HTML fragments have to be inserted into the DOM of the parent. Have a look at this jsperf test comparing different methods of rendering

它的要点是,生成完整的HTML结构,然后将意见比建设的意见和子视图,然后层叠渲染更快。例如,

The gist of it is that generating the complete HTML structure and then applying views is much faster than building views and subviews and then cascading the rendering. For example,

<script id="tpl-table" type="text/template">
    <table>
        <thead>
            <tr>
                <th>Row</th>
                <th>Name</th>
            </tr>
        </thead>
        <tbody>
        <% _(children).each(function(model) { %>
            <tr id='<%= model.cid %>'>
                <td><%= model.row %></td>
                <td><%= model.name %></td>
            </tr>
        <% }); %>
        </tbody>
     </table>
</script>

var ItemView = Backbone.View.extend({
});

var ListView = Backbone.View.extend({
    render: function () {
        var data, html, $table, template = this.options.template;

        data = this.collection.map(function (model) {
            return _.extend(model.toJSON(), {
                cid: model.cid
            });
        });

        html = this.options.template({
            children: data
        });

        $table = $(html);

        this.collection.each(function (model) {
            var subview = new ItemView({
                el: $table.find("#" + model.cid),
                model: model
            });
        });

        this.$el.empty();
        this.$el.append($table);

        return this;
    }
});


var view = new ListView({
    template: _.template($('#tpl-table').html()),
    collection: new Backbone.Collection(data)
});

http://jsfiddle.net/nikoshr/UeefE/

注意jsperf表明模板可拆分成子模板没有太多的处罚,这将允许您为行提供部分呈现。

Note that the jsperf shows that the template can be be split into subtemplates without too much penalty, which would allow you to provide a partial rendering for the rows.

在一个相关的说明,不要附加到DOM节点的工作,这样就会造成不必要的回流。可以创建一个新的DOM或操纵它之前分离的节点。

On a related note, don't work on nodes attached to the DOM, this will cause unnecessary reflows. Either create a new DOM or detach the node before manipulating it.

德里克·贝利写上的<一个主题的优秀文章href=\"http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/\">eradicating僵尸观点

基本上,你要记住,当你放弃一个视图,必须解除绑定的所有监听器,并执行任何额外的清理喜欢破坏jQuery插件实例。我用的是类似于德里克用于 Backbone.Marionette 相结合的方法:

Basically, you have to remember that when you discard a view, you must unbind all listeners and perform any additional cleanup like destroying the jQuery plugin instances. What I use is a combination of methods similar to what Derick uses in Backbone.Marionette:

var BaseView = Backbone.View.extend({

    initialize: function () {
        // list of subviews
        this.views = [];
    },

    // handle the subviews
    // override to destroy jQuery plugin instances
    unstage: function () {
        if (!this.views) {
            return;
        }

        var i, l = this.views.length;

        for (i = 0; i < l; i = i + 1) {
            this.views[i].destroy();
        }
        this.views = [];
    },

    // override to setup jQuery plugin instances
    stage: function () {
    },

    // destroy the view
    destroy: function () {
        this.unstage();
        this.remove();
        this.off();

        if (this.collection) {
            this.collection.off(null, null, this);
        }
        if (this.model) {
            this.model.off(null, null, this);
        }
    }
});

更新我的previous例子给行的可拖动的行为是这样的:

Updating my previous example to give the rows a draggable behavior would look like this:

var ItemView = BaseView.extend({
    stage: function () {
        this.$el.draggable({
            revert: "invalid",
            helper: "clone"
        });
    },

    unstage: function () {
        this.$el.draggable('destroy');
        BaseView.prototype.unstage.call(this);
    }
});

var ListView = BaseView.extend({

    render: function () {
       //same as before

        this.unstage();
        this.collection.each(function (model) {
            var subview = new ItemView({
                el: $table.find("#" + model.cid),
                model: model
            });
            subview.stage();
            this.views.push(subview);
        }, this);
        this.stage();

        this.$el.empty();
        this.$el.append($table);

        return this;
    }
});

http://jsfiddle.net/nikoshr/yL7g6/

销毁根视图会遍历视图的层次结构,并进行必要的清理。

Destroying the root view will traverse the hierarchy of views and perform the necessary cleanups.

注:关于JS code对不起,我不是CoffeeScript的不够熟悉而不能提供准确的片段

NB: sorry about the JS code, I'm not familiar enough with Coffeescript to provide accurate snippets.

这篇关于BackboneJS呈现问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆