2013-11-14 60 views
2

我有一個基於Backbone.Marionette構建的應用程序,帶有一個CollectionView,用於實例化許多CompositeView,這些CompositeView呈現樹結構。在Marionette中正確關閉視圖

我已閱讀殭屍視圖(Bailey on Zombies)並通過視圖和區域文檔。但是,如果在閱讀時看起來很簡單,執行完全是另一回事。

當我碰到我的任何路線時,我的鍵盤快捷鍵最終被多次觸發。我找到了解決辦法,但是此解決方法會導致在視圖中呈現更改時出現其他問題。

以下是多次觸發鍵盤快捷鍵的實際代碼。在Snippet A,我已經添加了關閉,我能想到的視圖的任何方式,儘管通常情況下,關閉視圖應該只需要App.contentRegion.currentView.treeRegion.close()

showContentView: (tree) -> 
    if @treeView? 
    App.contentRegion.currentView.treeRegion.reset() 
    App.contentRegion.currentView.treeRegion.close() 
    @treeView.close() 
    delete @treeView 
    @treeView = new App.Note.TreeView(collection: tree) 
    App.contentRegion.currentView.treeRegion.show @treeView 

Snippet B,下面,修復了鍵盤快捷鍵的問題。但是,它會導致另外創建的模型(CompositeView)不會呈現給用戶的問題。

showContentView: (tree) -> 
    if @treeView? 
    @treeView.collection = tree 
    @treeView.render() 
    else 
    @treeView = new App.Note.TreeView(collection: tree) 
    App.contentRegion.currentView.treeRegion.show @treeView 

這裏就是我初始化的CollectionView,這反過來又致使CompositeViews

initialize: -> # collectionView 
    @listenTo @collection, "sort", @render 
    @listenTo @collection, "destroy", @addDefaultNote 
    Note.eventManager.on 'createNote', @createNote, this 
    Note.eventManager.on 'change', @dispatchFunction, this 
    @drag = undefined 

initialize: -> # compositeView 
    @collection = @model.descendants 
    @bindKeyboardShortcuts() 
    @listenTo @collection, "sort", @render 
    Note.eventManager.on "setCursor:#{@model.get('guid')}", @setCursor, @ 
    Note.eventManager.on "render:#{@model.get('guid')}", @render, @ 
    Note.eventManager.on "setTitle:#{@model.get('guid')}", @setNoteTitle, @ 

這是我結合我的鍵盤快捷鍵在CompositeViews

bindKeyboardShortcuts: -> 
    @.$el.on 'keydown', null, 'ctrl+shift+backspace', @triggerShortcut 'deleteNote' 
    @.$el.on 'keydown', null, 'tab', @triggerShortcut 'tabNote' 
    @.$el.on 'keydown', null, 'shift+tab', @triggerShortcut 'unTabNote' 
    @.$el.on 'keydown', null, 'alt+right', @triggerShortcut 'tabNote' 
    @.$el.on 'keydown', null, 'alt+left', @triggerShortcut 'unTabNote' 
    @.$el.on 'keydown', null, 'alt+up', @triggerShortcut 'jumpPositionUp' 
    @.$el.on 'keydown', null, 'alt+down', @triggerShortcut 'jumpPositionDown' 
    @.$el.on 'keydown', null, 'up', @triggerShortcut 'jumpFocusUp' 
    @.$el.on 'keydown', null, 'down', @triggerShortcut 'jumpFocusDown' 
    @.$el.on 'keydown', null, 'alt+ctrl+left', @triggerShortcut 'zoomOut' 
    @.$el.on 'keydown', null, 'alt+ctrl+right', @triggerShortcut 'zoomIn' 

和我如何觸發他們

triggerShortcut: (event) -> (e) => 
    e.preventDefault() 
    e.stopPropagation() 
    @triggerEvent(event).apply(@, Note.sliceArgs arguments) 
triggerEvent: (event) -> 
    (e) => 
    @updateNote() 
    args = ['change', event, @model].concat(Note.sliceArgs arguments, 0) 
    Note.eventManager.trigger.apply(Note.eventManager, args) 

最後,爲了確保一切都乾淨,我解開了onBeforeClose中的每個快捷方式。我還解除了任何eventManager的偵聽器。

onBeforeClose: -> 
    console.log "view being closed", @ 
    @$el.off() 
    Note.eventManager.off "setCursor:#{@model.get('guid')}" 
    Note.eventManager.off "render:#{@model.get('guid')}" 
    Note.eventManager.off "setTitle:#{@model.get('guid')}" 
    Note.eventManager.off "timeoutUpdate:#{@model.get('guid')}" 

我知道問題來自@treeView = new App.Note.TreeView(collection: tree)。如果我在每個@showContentView(代碼片段A)上創建了新的 TreeView,則每個添加的模型都會正確呈現給視圖,但快捷方式會變得瘋狂。另一方面,如果我創建一個TreeView並交換它的集合(代碼片段B),我會在視圖中看到渲染問題,但快捷方式沒問題!

我試圖包括所有你需要的東西,沒有更多的東西(它已經有一些代碼..)但如果你們需要別的東西,請問!
希望我能得到足夠清晰..

[編輯] 我已經嘗試了許多不同的組合來擺脫快捷的bug,但如果我創建每個showContentView一個新的TreeView,似乎沒有任何關閉視圖正常。 我認爲這是來自更深的內存泄漏問題。我可能會在這方面編寫一個其他的StackOverflow問題,並鏈接到這個問題以獲取更多信息。

非常感謝!

回答

2

我想出了這裏的問題。

使用Snippet A和chrome devtool的分析器,我可以追蹤泄漏。我在我的問題中提供的onClose方法來自CompositeView,其中綁定了鍵盤快捷鍵。
問題是CollectionView沒有得到垃圾收集,因爲使用了Note.eventManager.on,它保留了對視圖的引用。 所以我增加了一個onBeforeClose方法到TreeView(的CollectionView)

onBeforeClose: -> 
    Note.eventManager.off('createNote', @createNote, this) 
    Note.eventManager.off('change', @dispatchFunction, this) 
    @drag = undefined 

有了這個onBeforeClose,這種觀點正在被正確關閉,這反過來又使孩子們的意見,又和關閉,並停止收聽快捷方式被解僱。

我想這很明顯,一旦我發現了,但我想添加這個答案,以便它清楚地表明任何您沒有設置@listenTo的事件偵聽器都不會被Marionette清除,並且需要正確處理。

[編輯]

要上的評論跟進,這裏本來是從一開始更好的解決方案:

更換

initialize: -> # compositeView 
    /* ... */ 
    Note.eventManager.on "setCursor:#{@model.get('guid')}", @setCursor, @ 
    Note.eventManager.on "render:#{@model.get('guid')}", @render, @ 
    Note.eventManager.on "setTitle:#{@model.get('guid')}", @setNoteTitle, @ 

而且

initialize: -> # collectionView 
    /* ... */ 
    Note.eventManager.on 'createNote', @createNote, this 
    Note.eventManager.on 'change', @dispatchFunction, this 

initialize: -> # compositeView 
    /* ... */ 
    @listenTo Note.eventManager, "setCursor:#{@model.get('guid')}", @setCursor 
    @listenTo Note.eventManager, "render:#{@model.get('guid')}", @render 
    @listenTo Note.eventManager, "setTitle:#{@model.get('guid')}", @setNoteTitle 

/* ... */ 

initialize: -> # collectionView 
    /* ... */ 
    @listenTo Note.eventManager, 'createNote', @createNote, this 
    @listenTo Note.eventManager, 'change', @dispatchFunction, this 

使用listenTo語法可以防止內存泄漏。因此,onBeforeClose塊可以完全刪除!

+2

爲什麼不在這種情況下使用'@ listenTo'? –

+1

除了我在熟悉'@ listenTo'之前寫下這些事件之外,我沒有看到任何好的理由。這可能是正確的解決方案。 – Mikechaos