Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from Feb 8, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 14 additions & 6 deletions packages/vue-app/template/utils.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) {
Expand All @@ -24,19 +22,29 @@ 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.__isNuxt) ||
// Prevent chain and leak on client-side
(Component.options.data && Component.options.data.__isNuxt)
) {
return
}
Component.options.hasAsyncData = true

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

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

Component.options.__isNuxt = true
Component.options.data.__isNuxt = true

if (Component._Ctor && Component._Ctor.options) {
Component._Ctor.options.data = Component.options.data
}
Expand Down
20 changes: 20 additions & 0 deletions test/fixtures/spa/pages/async.vue
@@ -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>
17 changes: 17 additions & 0 deletions test/unit/spa.test.js
Expand Up @@ -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)
pi0 marked this conversation as resolved.
Show resolved Hide resolved
})

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