• 让你的App飞一会


    在基于Backbone的单页应用中,我们可能会有下面这些疑问:

    1 如果多次访问同一个页面(hash)时,被多次实例化的视图所占内存释放了么?

    2 当你关闭视图后,是不是会发现,它上面的事件还没有移除掉?

    3 你是不是在为进一个新页面之前的清理工作而懊恼呢?

    因为Backbone是事件驱动的,在Backbone App 应用的许多地方你都能看到事件的身影:

    在View中,通过events属性来配置事件的注册:

    1 MyView = Backbone.View.extend({
    2   events: {
    3     "click #someButton": "doThat",
    4     "change #someInput": "changeIt"
    5   },
    6   doThat: function(){ ... },
    7   changeIt: function(){ ... }
    8 });

    我们会监听model的变化来渲染页面:

    1 MyView = Backbone.View.extend({
    2   initialize: function(){
    3     this.listenTo(this.model, this.render, this);
    4   },
    5   render: function(){ ... }
    6 });

    我们还可以用全局/应用级的事件对象,来触发页面渲染:

    1 MyView = Backbone.View.extend({
    2   initialize: function(){
    3     this.options.vent.bind("app:event", this.render, this);
    4   },
    5   render: function() { ... }
    6 });
    7 var vent = new _.extend({}, Backbone.Events);
    8 new MyView({vent: vent});
    9 vent.trigger("app:event");

    事件在给我们带好处的同时,也有其负面影响。通过事件,多个对象会被绑在一起,它们之间也就有了引用关系。而这种引用关系如果不被清除,对象所占内存很可能不会被回收,引发内存泄露。

    很常见的场景:

     1 MyRouter = Backbone.Router.extend({
     2   routes: {
     3     "": "home",
     4     "post/:id": "showPost"
     5   },
     6  
     7   home: function(){
     8     var homeView = new HomeView();
     9     $("#mainContent").html(homeView.render().el);
    10   },
    11  
    12   showPost: function(id){
    13     var post = posts.get(id);
    14     var postView = new PostView({model: post});
    15     $("#mainContent").html(postView.render().el);
    16   }
    17 });

    如你所想,页面运行是没问题的。但你想过,当进入PostView视图时,HomeView视图上的事件有被清理过吗?它的实例会被回收吗?

    我们来简单构造一个类,它专门负责视图上的清理工作:

     1 function AppView(){
     2  
     3    this.showView(view) {
     4     if (this.currentView){
     5       this.currentView.close();
     6     }
     7  
     8     this.currentView = view;
     9     this.currentView.render();
    10  
    11     $("#mainContent").html(this.currentView.el);
    12   }
    13  
    14 }

    那之前的代码可优化如下:

     1 MyRouter = Backbone.Router.extend({
     2   routes: {
     3     "": "home",
     4     "post/:id": "showPost"
     5   },
     6  
     7   initialize: function(options){
     8     this.appView = options.appView;
     9   },
    10  
    11   home: function(){
    12     var homeView = new HomeView();
    13     this.appView.showView(homeView);
    14   },
    15  
    16   showPost: function(id){
    17     var post = posts.get(id);
    18     var postView = new PostView({model: post});
    19     this.appView.showView(postView);
    20   }
    21 });

    关闭视图

    上面的例子中,当移除一个视图时,都会调用一个close方法,但Backbone.View是没有这个方法的,先讨论下,close方法要具备什么样的功能呢?

    1 移除DOM元素上面的事件;

    2 移除视图上面的事件;

    3 从DOM树中移除相关HTML;

    所以,我想close方法大概是这么个样子:

    1 Backbone.View.prototype.close = function(){
    2   this.remove();
    3   this.off();
    4 }

    首先我们这里调用Backbone.View的remove方法:

    1    // Remove this view by taking the element out of the DOM, and removing any
    2    // applicable Backbone.Events listeners.
    3    remove: function () {
    4             this.$el.remove();
    5             this.stopListening();
    6             return this;
    7   }

    “this.$el.remove()” 从DOM树中移除HTML,“this.stopListening()” 移除视图监听的其他对象上面的事件,” this.off()”移除视图自身的事件,但是看了zepto的remove方法:

    1 remove: function(){
    2       return this.each(function(){
    3         if (this.parentNode != null)
    4           this.parentNode.removeChild(this)
    5       })
    6 },

    对于视图里通过”events”来配置注册的事件是否被移除我持怀疑态度,这个暂不作讨论。

    设计视图

     1     Jass.extend = Backbone.View.extend;
     2     Jass.View = Backbone.View.extend({
     3         initialize: function () {
     4            var data=arguments;
     5            if(this.beforeRender) data=this.beforeRender.apply(this,arguments)||{};
     6            this.render(this.tpl(data));
     7            if(this.afterRender) this.afterRender.call(this,data);
     8         },
     9         close: function () {
    10             if(this.beforeClose) this.beforeClose();
    11             this.remove();
    12             this.off();
    13             if(this.onClosed) this.onClosed();
    14             //this.trigger('close');
    15             //delete this;
    16         },
    17         render: function (el) {
    18             this.$el.html(el);
    19         },
    20     });

    在视图的渲染时,渲染前调用beforeRender,渲染后调用afterRender,可看成其生命周期的两个不同阶段,其中this.tpl= _.template(html模板)。

     在close方法中,也有调用beforeClose和onClosed,做你想做的事情吧。

    清道夫说,没有我,你们都是瞎忙活:

     1    Jass.region = function (node) {
     2         this.node = $(node);
     3     }
     4    Jass.region.prototype.show = function (view) {
     5         var node = this.node;
     6         var currentView = this.currentView;
     7         if (!view.el) {
     8             var err = new Error('View Must Extend from Jass.View');
     9             err.name = 'NoError';
    10             throw err;
    11         }
    12         if (view != currentView) {
    13             if (currentView) {
    14                 currentView.close();
    15                 delete this.currentView;
    16             }
    17         }
    18         node.html(view.el);
    19         if(view.onShow) view.onShow();
    20         view.trigger('render',view);
    21         this.currentView = view;
    22         return view;
    23 }

    在框架里,先做些事情:

    1     Jass.Application = function () {
    2         this.vent = _.extend({}, Backbone.Events);
    3         this._initCallbacks = new $.Callbacks();
    4     };
    5 
    6     App = _.extend(new Jass.Application,{
    7         "header": new Jass.region("#header"),
    8         "body": new Jass.region("#main"),
    9     });

    然后在我们的App里会这样用:

    1   var head=new headView();
    2   App.header.show(head);
    3 
    4   var body=new homeView();
    5   App.body.show(body);

    打完收工,希望对你有所启示!

    http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/

  • 相关阅读:
    Vivado生成及使用edf文件
    ZYNQ Block Design中总线位宽的截取与合并操作
    Modelsim问题集锦
    Questasim10.6c下载安装教程
    ZYNQ工程PL顶层创建block module报错解决
    Linux自定义应用程序及其菜单图标
    OpenWRT DNS无法解析WAN连接的内网服务器域名
    Wishbone总线从接口转Xilinx MIG (Spartan 6)
    Wishbone总线接口RAM
    Windows系统下将目录挂载为一个磁盘并分配盘符
  • 原文地址:https://www.cnblogs.com/stenson/p/3929137.html
Copyright © 2020-2023  润新知