【问题标题】:How to fix memory leak in my vue.js / axios app?如何修复我的 vue.js / axios 应用程序中的内存泄漏?
【发布时间】:2021-01-11 07:30:07
【问题描述】:

我有一个页面,它使用内联 vue.js 脚本显示表中的行列表,它会在页面加载时获取行列表,但也会在收到推送事件时再次获取表。

我有一个模式窗口,它打开并允许用户将新人添加到表中。当用户按下提交(在表单上)时,模式会发布一个 ajax 请求(使用 axios)来提交记录。这会向 Pusher 发送一个 App\Events\NewQueueMember 事件,我的 vue 应用程序也会监听该事件,当发生这种情况时,将调用 updateData 方法来刷新表。

我遇到的问题是提交表单时,页面在大约 5 秒钟内有些无响应,然后才能再次在页面上滚动。查看 Chromes 任务管理器,我可以看到选项卡的 RAM 使用量翻了一番,CPU 使用率也相当高。

我相信这是某种内存泄漏,但我不确定如何解决,希望得到一些指导。

这是驻留在我的模式中的表单。

@csrf 顾客姓名 名称将在内部显示给客户
<div class="form-group">
    <label for="add-person-mobile">Mobile Number</label>
    <input type="phone" class="form-control" name="phone_number" id="add-person-mobile"
           v-model="addPersonForm.phone_number">
    <small id="emailHelp" class="form-text text-muted">Please ensure number starts with +44 </small>
</div>

<div class="form-group">
    <label for="add-person-party-size">Party Size</label>
    <input type="number" class="form-control" name="party_size" id="add-person-party-size"
           v-model="addPersonForm.party_size">
</div>

这是同一页面内的站点的 vue.js 脚本块

<script>

    Vue.use(VueTimeago, {
        name: 'Timeago',
    })

    let vm = new Vue({
        el: '#app',

        data() {
            return {
                live_count: null,
                queue: null,
                loading: true,
                errored: false,
                buttons: {
                    add_to_queue_label: 'Add to Queue',
                    add_to_queue_disabled: false
                },
                addPersonForm: {
                    name: '',
                    phone_number: '',
                    party_size: ''
                }
            }
        },

        mounted() {
            axios
                .get(`/business/queue-live-feed`)
                .then(response => {
                    console.log('got response data')
                    console.log(response.data)
                    this.queue = response.data.queue
                    this.live_count = response.data.live_count
                })
                .catch(error => {
                    console.log(error)
                    this.errored = true
                })
                .finally(() => this.loading = false);
        },
        
        methods: {
            updateData: function () {
                axios.get(`/business/queue-live-feed`)
                    .then(response => {
                        this.queue = response.data.queue
                        this.live_count = response.data.live_count
                    })
                    .catch(error => {
                        console.log(error)
                        this.errored = true
                    });
            },

            addPerson: function (event) {
                console.log('calling resource...');
                this.buttons.add_to_queue_disabled = true;
                this.buttons.add_to_queue_label = "Adding...";

                axios.post(`/business/queue/add2`, this.$data.addPersonForm)
                    .then(response => {
                        console.log('got a response!');
                        console.log(response.data);
                        // this.updateData(); <- when I comment this line, the unresponsiveness lasts around 5-10 seconds.
                    })
                    .catch(error => {
                        console.log('some error happened :(')
                        this.buttons.add_to_queue_disabled = true;
                        this.buttons.add_to_queue_label = "Retry";
                        // $('#manual-add-people').modal('hide');
                        // console.log(error)
                    })
                    .finally(() => {
                        console.log('finished method');
                        this.buttons.add_to_queue_disabled = false;
                        this.buttons.add_to_queue_label = "Add to Queue";
                        $('#manual-add-people').modal('hide');
                    });

                this.addPersonForm = {};
            },

        }
    })


    // PUSHER CODE
    Pusher.logToConsole = true;

    const pusher = new Pusher('{{ env('PUSHER_APP_KEY') }}', {
        cluster: '{{ env('PUSHER_APP_CLUSTER') }}',
        authEndpoint: '/broadcasting/auth',
        auth: {
            headers: {
                'X-CSRF-TOKEN': '{{ csrf_token() }}',
            }
        }
    });

    pusher.subscribe(`private-queue.business.{{ $business_id }}`)
        .bind('App\\Events\\NewQueueMember', (data) => {
            vm.updateData()
        })
        .bind('App\\Events\\QueueMemberKicked', (data) => {
            vm.updateData()
        })
        .bind('App\\Events\\QueueMemberLeft', (data) => {
            vm.updateData()
        })
        .bind('App\\Events\\QueueMemberNoShow', (data) => {
            vm.updateData()
        })
    ;

</script>

有问题的方法是提交表单时调用的addPerson。

如果我将其切换为空数组,我认为该问题与我对 this.$data.addPersonForm 的使用有关。

编辑: 这是从我的/business/queue/add2 端点返回的示例响应。

{guest_id: 55, queue_lane_id: 1, party_size: 2, short_id: "RIS0MRk0",…}
business: {id: 2, name: "Acme Ltd", business_type_id: 2, is_active: 1,…}
business_id: 2
created_at: "2020-09-27T23:53:11.000000Z"
customer: null
guest: {id: 55, name: "AJAX Test 29", phone_number: "+1234567890", business_id: 2,…}
guest_id: 55
id: 79
party_size: 2
queue_entry_time: "2020-09-28 00:53:11"
queue_lane_id: 1
short_id: "RIS0MRk0"
updated_at: "2020-09-27T23:53:11.000000Z"

这只是一个 JSON blob,其中包含他们刚刚创建的记录的详细信息,也不是很大。

我还更新了上面的 Vue 脚本,以反映我在 addPerson 下的 then() 函数中进行的附加方法调用。

【问题讨论】:

  • 您是如何得出使用this.$data.addPersonForm 导致问题的结论的?您是否尝试使用模拟数据并检查页面是否仍然无响应?另外,如果您可以共享代码和框以查看有帮助的问题。
  • 当我将该变量更改为空列表时,内存使用量不会激增。我很喜欢,今晚会更新问题。
  • 你为什么用this.$data.addPersonForm而不是this.addPersonForm?你能把调试器断点(或只是console.log)调用updateData吗?也许它被多次调用,并且 debounce-ish 方法可能会有所帮助
  • @DipenShah 我已经更新了帖子以包含更多信息,如果您需要更多信息,请告诉我。明天我将尝试在 codepen 上创建一个模型页面,因为它全部在本地构建,很难在线显示页面。 updateData 方法或多或少与 mount 方法相同,所以我不确定它为什么会导致问题。
  • @DipenShah 请参阅上面的评论。我不认为它的推送事件订阅会导致问题,因为我尝试简单地注释掉所有订阅者事件以调用 updateData 方法。似乎泄漏的大部分过程来自updateData。当我不在addPerson 中调用此方法时,虽然仍然需要大约 2 秒的响应时间,但它比在.then 函数中使用updateData() 时页面无响应超过 5 秒的时间要短得多。

标签: javascript vue.js


【解决方案1】:

在 chrome 开发工具中,打开“性能”选项卡,点击记录,重复使一切变慢的问题,然后深入调用堆栈。 Vue 编译代码可能会使跟踪变得更加混乱。在这种情况下,chrome“Vue 开发工具”可能会有所帮助。

仅通过查看代码,我还将向 updateData() 函数添加两个不同的计数器和记录器 - 记录最初调用它的次数,然后记录 axios 返回的时间。如果函数导致问题时返回的数据不多,则推送器可能会在返回之前调用它太多次。您也可能会在开发工具的网络选项卡中看到这一点。

【讨论】:

  • 我已经运行了性能选项卡,如果我说实话,我不确定我应该看什么。当我按照我的 OP 中的描述记录和运行任务时,我可以看到 CPU 在几秒钟内达到 100%,但这就是我真正能从中得到的。
  • Vue 性能选项卡对我来说显示得更少,它只显示发布操作需要几秒钟才能加载。
  • 大致思路是得到一个火焰图来分析整个javascript调用栈的性能。 Firefox 也有类似的工具 - developer.mozilla.org/en-US/docs/Tools/Performance/Flame_Chart ... 从概念上讲,它会告诉你每个功能需要多长时间。您还可以尝试记录每个函数的开始和结束、调用次数的计数以及带有 performance.now() 的时间戳。我怀疑你在反复打电话 - 即:
  • 类似:console.log('updateDataStart ' + updateDataStart++ + ' ' + performance.now());
猜你喜欢
  • 1970-01-01
  • 2017-06-28
  • 2020-05-25
  • 1970-01-01
  • 1970-01-01
  • 2017-05-09
  • 1970-01-01
  • 1970-01-01
  • 2022-08-05
相关资源
最近更新 更多