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

Co-authored-by: Sébastien Chopin <seb@chopin.io>
This commit is contained in:
Pooya Parsa 2019-02-08 20:03:45 +03:30 committed by GitHub
parent 17cc12f005
commit 408680046c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 6 deletions

View File

@ -1,7 +1,5 @@
import Vue from 'vue' import Vue from 'vue'
const noopData = () => ({})
// window.{{globals.loadedCallback}} hook // window.{{globals.loadedCallback}} hook
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading) // Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
if (process.client) { if (process.client) {
@ -24,12 +22,17 @@ export function interopDefault(promise) {
} }
export function applyAsyncData(Component, asyncData) { export function applyAsyncData(Component, asyncData) {
const ComponentData = Component.options.data || noopData if (
// Prevent calling this method for each request on SSR context // For SSR, we once all this function without second param to just apply asyncData
if (!asyncData && Component.options.hasAsyncData) { // Prevent doing this for each SSR request
!asyncData && Component.options.__hasNuxtData
) {
return return
} }
Component.options.hasAsyncData = true
const ComponentData = Component.options._originDataFn || Component.options.data || function () { return {} }
Component.options._originDataFn = ComponentData
Component.options.data = function () { Component.options.data = function () {
const data = ComponentData.call(this) const data = ComponentData.call(this)
if (this.$ssrContext) { if (this.$ssrContext) {
@ -37,6 +40,9 @@ export function applyAsyncData(Component, asyncData) {
} }
return { ...data, ...asyncData } return { ...data, ...asyncData }
} }
Component.options.__hasNuxtData = true
if (Component._Ctor && Component._Ctor.options) { if (Component._Ctor && Component._Ctor.options) {
Component._Ctor.options.data = Component.options.data Component._Ctor.options.data = Component.options.data
} }

20
test/fixtures/spa/pages/async.vue vendored Normal file
View File

@ -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>

View File

@ -82,6 +82,23 @@ describe('spa', () => {
expect(consola.log).toHaveBeenCalledWith('mounted') expect(consola.log).toHaveBeenCalledWith('mounted')
consola.log.mockClear() 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 // Close server and ask nuxt to stop listening to file changes
afterAll(async () => { afterAll(async () => {
await nuxt.close() await nuxt.close()