san*_*e89 1 javascript turbolinks vue.js
我有一个使用 Turbolinks 的 Rails 应用程序。如果您不熟悉,turbolinks 会使document和window对象始终保持不变(页面永远不会刷新),并且它会通过 AJAX 拦截所有链接点击并替换 body 标记。
当在安装了 Vue 应用程序的页面之间导航时,这是我们观察到的内存/节点模式(它会增长到无穷大):
正如您所看到的,在每次页面更改时,内存只会增加,而不会被回收。
我们的应用程序使用https://github.com/jeffreyguenther/vue-turbolinks,基本上是这样的代码:
beforeMount: function() {
if (this === this.$root && this.$el) {
document.addEventListener('turbolinks:visit', function teardown() {
this.$destroy();
document.removeEventListener('turbolinks:visit', teardown);
})
// cache original element
this.$cachedHTML = this.$el.outerHTML;
// register root hook to restore original element on destroy
this.$once('hook:destroyed', function() {
if( this.$el.parentNode )
this.$el.outerHTML = this.$cachedHTML
});
}
}
Run Code Online (Sandbox Code Playgroud)
我在 Vue 应用程序上或其他任何地方都没有任何参考window(实例化代码只是new Vue({ options});没有全局变量,使用 const/let 的现代代码。
没有document.addEventListener从应用程序内部添加;所有事件侦听器均由 v-on 添加,并在销毁时由 Vue 自动删除。
为了进一步调试,我修补了上面的代码以添加存储,WeakRef并在销毁 Vue 实例之前对其进行了设置,如下所示:
document.addEventListener('turbolinks:visit', function teardown() {
window.weakReferences = window.weakReferences || {};
window.weakReferences[new Date()] = new window.WeakRef(this);
this.$destroy();
})
Run Code Online (Sandbox Code Playgroud)
更改屏幕一段时间后,我可以确认 Vue 对象仍然存在于内存中(WeakRef.deref()不会返回未定义),即使它们都被_isDestroyedVue 内部标记为内部。
如何调试为什么 Vue 应用程序实例没有被垃圾收集以及为什么浏览器保留对它们的引用?
天哪,我是来兜风的吗?我的应用程序中有 6 处内存泄漏。
要发现它们,请执行以下操作:
开始注意“选择 Javascript VM 实例”中的 MB 指示符(那里可能只有一行)。您可以忽略那里的向上/向下箭头指示符,只关注第一个数字,即您的选项卡当前使用的 MB 大小。
在您的例子中,每次导航到应用程序都会增加 20 MB 的内存使用量,最终在 10 次左右后达到 200 MB;单击垃圾收集图标仅减少了大约 10MB 的使用量,因此很明显我们正在发生泄漏。
将页面置于此泄漏状态并单击 GC 垃圾桶图标后,在单选选项中选择“堆快照”。它将收集快照。点击它。
在我们的例子中,由于泄漏,我们的 Vue 应用程序被保留在内存中。因此我们重点关注列表中的“VueComponent”项。单击小三角形并观察屏幕的下部。它将开始向您显示哪些代码片段将这些引用保留在内存中。有时您会很幸运,甚至会在那里得到一些行号,您可以单击它们,它将在源选项卡中打开。
我们的应用程序中有 6 个以上的泄漏;下面我展示了其中之一,您会看到内存跟踪指向$notify,这是我们正在使用的一个库(vue-notification):
是的,你可能会从其他人的代码中泄漏,这真是太糟糕了。看看那个库,我发现罪魁祸首是钩子上定义的两个事件处理程序created,它们从未被释放;我在这里发出了拉取请求。
库中的“错误” mitt(微小的事件发射器);eventEmitter.off()应该清除所有事件发射器,但它没有;在这里打开了一个问题;
created我们在(using )中订阅了 Vuex 事件this.$store.subscribe(),但忘记在 beforeDestroy() 中取消订阅;该subscribe()函数返回一个取消订阅的函数;将其保存在实例本身上(如this.unsubscribeVuex = this.$store.subscribe(...))并this.unscubribeVuex在您的beforeDestroy()钩子中调用
小心你是否没有留下window.myApp = new Vue()参考资料;没有任何内容window.被垃圾收集;window.myApp = null;挂上钩子是个好主意beforeDestroy;
Google Chart 也导致了内存泄漏;我粘贴了下面的快速修复,请注意差异中我们如何泄漏对 vue 应用程序的永久引用:
| 归档时间: |
|
| 查看次数: |
1201 次 |
| 最近记录: |