Skip to content
Permalink
Browse files

fix(vue-app): fix `asyncData` memory leak on client-side (#4966)

Co-authored-by: Sébastien Chopin <seb@chopin.io>
  • Loading branch information...
pi0 and Atinux committed Feb 8, 2019
1 parent 17cc12f commit 408680046c7b2c92f1ba763490962bcb3433a712
Showing with 49 additions and 6 deletions.
  1. +12 −6 packages/vue-app/template/utils.js
  2. +20 −0 test/fixtures/spa/pages/async.vue
  3. +17 −0 test/unit/spa.test.js
@@ -1,7 +1,5 @@
import Vue from 'vue'

const noopData = () => ({})

// window.{{globals.loadedCallback}} hook
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
if (process.client) {
@@ -24,19 +22,27 @@ export function interopDefault(promise) {
}

export function applyAsyncData(Component, asyncData) {
const ComponentData = Component.options.data || noopData
// Prevent calling this method for each request on SSR context
if (!asyncData && Component.options.hasAsyncData) {
if (
// For SSR, we once all this function without second param to just apply asyncData
// Prevent doing this for each SSR request
!asyncData && Component.options.__hasNuxtData
) {
return
}
Component.options.hasAsyncData = true

const ComponentData = Component.options._originDataFn || Component.options.data || function () { return {} }
Component.options._originDataFn = ComponentData

Component.options.data = function () {
const data = ComponentData.call(this)
if (this.$ssrContext) {
asyncData = this.$ssrContext.asyncData[Component.cid]
}
return { ...data, ...asyncData }
}

Component.options.__hasNuxtData = true

if (Component._Ctor && Component._Ctor.options) {
Component._Ctor.options.data = Component.options.data
}
@@ -0,0 +1,20 @@
<template>
<pre>
{{ debug }}
</pre>
</template>

<script>
export default {
asyncData() {
return {
[Math.random()]: true
}
},
computed: {
debug() {
return JSON.stringify(this.$data, null, 2)
}
}
}
</script>
@@ -82,6 +82,23 @@ describe('spa', () => {
expect(consola.log).toHaveBeenCalledWith('mounted')
consola.log.mockClear()
})

test('/async no asyncData leak', async () => {
const window = await nuxt.server.renderAndGetWindow(url('/async'))

const navigate = url => new Promise((resolve, reject) => {
window.$nuxt.$router.push(url, resolve, reject)
})

for (let i = 0; i < 3; i++) {
await navigate('/')
await navigate('/async')
}

const { $data } = window.$nuxt.$route.matched[0].instances.default
expect(Object.keys($data).length).toBe(1)
})

// Close server and ask nuxt to stop listening to file changes
afterAll(async () => {
await nuxt.close()

0 comments on commit 4086800

Please sign in to comment.
You can’t perform that action at this time.