当前位置: 首页 > 面试题库 >

BackboneJS渲染问题

花飞尘
2023-03-14
问题内容

在过去的六个月中,我一直在与Backbone合作。前两个月很混乱,学习并弄清楚我要如何围绕它构建代码。接下来的4个月,该公司推出了适合生产的应用程序。不要误会我的意思,Backbone使我摆脱了以前标准的成千上万行客户端代码的混乱,但是它使我能够在更短的时间内完成更多宏伟的事情,从而带来了一系列全新的问题。对于我在这里提出的所有问题,有一些简单的解决方案,感觉像是骇客或只是
错误 。我承诺提供300分的赏金,以提供出色的解决方案。开始:

  1. 加载 -对于我们的用例(管理面板),悲观同步是不好的。对于某些事情,我需要先在服务器上验证它们,然后再接受它们。我们是在“同步”事件合并到Backbone之前开始的,

并且我们使用了以下代码来模拟加载事件:

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")

大。它按预期工作,但感觉不正确。我们将此事件绑定到所有相关视图,并显示一个加载图标,直到从该模型收到成功或错误事件为止。有没有更好,更聪明的方法呢?

现在为难者:

  1. 太多的东西使自己变得太多了 -假设我们的应用程序具有选项卡。每个选项卡控制一个集合。在左侧,您可以获取收藏集。单击模型以开始在中心对其进行编辑。您更改其名称,然后按Tab键转到下一个表单项。现在,您的应用程序是一个“实时的东西”,它注意到差异,运行验证,并将更改自动同步到服务器,不需要保存按钮!很好,但是表格开头的H2与输入中的名称相同-您需要对其进行更新。哦,您需要将列表上的名称更新到侧面。哦,列表按名称排序!

这是另一个示例:您要在集合中创建一个新项目。您按下“新”按钮,然后开始填写表格。您是否立即将项目添加到集合中?但是,如果您决定丢弃它,会发生什么?或者,如果您将整个收藏保存在另一个选项卡上?并且,有一个文件上传-
您需要先保存并同步模型,然后才能开始上传文件(以便可以将文件附加到模型)。因此,一切开始震撼地呈现:保存模型和列表,然后表单再次呈现自己-
现在已同步,因此您获得了一个新的Delete按钮,并在列表中显示-但现在文件上传完成了上传,因此一切重新开始渲染。

将子视图添加到混合中,一切开始看起来像Fellini电影。

  1. 它一直是子视图 - 这是一篇有关此内容的好文章。对于所有神圣的事物,我无法找到一种将jQuery插件或DOM事件附加到具有子视图的任何视图的正确方法。地狱随之而来。工具提示会听到渲染很长一段时间并开始变乱,子视图变得像僵尸一样或没有响应。这是实际错误所在的主要痛点,但是我仍然没有一个全面的解决方案。

  2. 闪烁 -渲染速度很快。实际上,它是如此之快,以至于我的屏幕看起来好像被卡住了。有时是必须重新加载图像(通过另一个服务器调用!),因此html最小化然后突然再次最大化-该元素的css width + height将解决此问题。有时我们可以使用fadeIn和fadeOut解决这个问题,因为这有时会很麻烦,因为有时我们会重用视图,有时会重新创建视图。

TL; DR- 我在Backbone中的视图和子视图遇到问题-它渲染太多次,渲染时闪烁,子视图使我的DOM事件分离并吞噬了我的大脑。

谢谢!

更多详细信息:BackboneJS与Ruby on Rails Gem。使用UnderscoreJS模板的模板。


问题答案:

视图的部分渲染

为了最大程度地减少DOM层次结构的完整呈现,您可以在DOM中设置特殊的节点,以反映给定属性的更新。

让我们使用这个简单的Underscore模板,一个名称列表:

<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>

注意class model-<%= model.cid %>-name,这将是我们的注入点。

然后,我们可以定义一个基本视图(或修改Backbone.View),以便在更新这些节点时为其添加适当的值:

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

代码会有所不同,以适应模型而不是集合。与http://jsfiddle.net/nikoshr/cfcDX/一起玩的小提琴

限制DOM操作

将渲染委派给子视图可能会花费很多,它们的HTML片段必须插入到父视图的DOM中。看看这个jsperf测试,比较不同的渲染方法

其要点在于,生成完整的HTML结构然后应用视图比构建视图和子视图然后级联呈现要快得多。例如,

<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显示了可以将模板拆分为子模板而不会带来太多损失,这将使您能够为行提供部分渲染。

值得一提的是,不要在连接到DOM的节点上工作,这将导致不必要的重排。在操作它之前,要么创建一个新的DOM,要么分离该节点。

Squash zombies

德里克·贝利(DerickBailey)撰写了一篇出色的文章,主题是消除僵尸观点

基本上,您必须记住,丢弃视图时,必须取消绑定所有侦听器并执行任何其他清除操作,例如销毁jQuery插件实例。我使用的是类似于Derick在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);
        }
    }
});

更新我之前的示例以使行具有可拖动的行为,如下所示:

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/

销毁根视图将遍历视图的层次结构并执行必要的清理。

注意:对JS代码感到抱歉,我对Coffeescript不够熟悉,无法提供准确的摘要。



 类似资料:
  • 我是android studio的新手,安装了一个新版本(1.5.1版)。出于某种原因,我一直收到一条错误消息(每次我使用应用程序主题时),说有渲染问题:缺少样式。我在网上寻找解决方案,但大多数都过时了,或者根本不起作用。 谢谢你的帮助。 清单文件: 构建渐变:

  • 从昨天开始,我已经开始与Android Studio的工作。我的问题是当我做我的布局。我有下一个错误(观看屏幕截图了解更多信息): 渲染问题:此版本的渲染库比您的Android Studio版本更新。请更新Android Studio 如果我检查更新,他会说我已经有了最新版本的Android studio,所以我无法更新。 我也在SDK管理器中安装了一些东西,请看下面的截图。 有人能帮我解决这个问

  • 我们刚刚将本地Android SDK更新到了新的22版,我们也在尝试更新我们的应用程序,但在Android Studio的布局设计视图中SearchView呈现似乎有一个问题:每当设计师试图呈现SearchView元素时,它都会抛出一个异常: java.lang.nullPointerException · 在Android.content.res.typedarray.hasValueOremp

  • 我有这个xml文件。如果我编译,它可以正常工作,但如果我去看预览,它会显示出这个错误: 我试着找这个身份证,但到处都找不到。这个@id/visible是什么?我怎么才能修好?谢谢

  • 问题内容: 我刚刚安装了grails渲染插件,并希望将其用于生成PDF文件。我已经创建了简单的模板,但是没有任何CSS样式即可将其导出。如果仅从grails渲染模板,则页面将在Web浏览器中显示所有样式。 所以,我的问题是-在PDF生成过程中如何正确包含CSS文件? 我的模板: 而且我的webui.css中具有样式.odd,但是它不适用于该行。 任何帮助,将不胜感激。 Edit1:我发现以以下方式

  • 当我尝试使用设计视图同时查看activity_main.xml时,我有一个警告,上面写着“渲染问题Android Nougat需要IDE运行Java1.8或更高版本安装受支持的JDK”,但我的计算机上有当前版本的Java。