diff --git a/packages/types/app/index.d.ts b/packages/types/app/index.d.ts index 677d005bacf3..9ec79d885c24 100644 --- a/packages/types/app/index.d.ts +++ b/packages/types/app/index.d.ts @@ -72,6 +72,7 @@ export interface Context { redirected: boolean next: NextFunction beforeRenderFns: Array<() => any> + beforeSerializeFns: Array<() => any> fetchCounters: Record nuxt: { layout: string @@ -87,6 +88,7 @@ export interface Context { error(params: NuxtError): void nuxtState: NuxtState beforeNuxtRender(fn: (params: { Components: VueRouter['getMatchedComponents'], nuxtState: NuxtState }) => void): void + beforeSerialize(fn: (nuxtState: NuxtState) => void): void enablePreview?: (previewData?: Record) => void $preview?: Record } diff --git a/packages/vue-app/template/index.js b/packages/vue-app/template/index.js index 13fdeca4933b..00a20345f8c0 100644 --- a/packages/vue-app/template/index.js +++ b/packages/vue-app/template/index.js @@ -183,6 +183,7 @@ async function createApp(ssrContext, config = {}) { req: ssrContext ? ssrContext.req : undefined, res: ssrContext ? ssrContext.res : undefined, beforeRenderFns: ssrContext ? ssrContext.beforeRenderFns : undefined, + beforeSerializeFns: ssrContext ? ssrContext.beforeSerializeFns : undefined, ssrContext }) diff --git a/packages/vue-app/template/server.js b/packages/vue-app/template/server.js index 1d62d425d1b6..27f89c6f59f2 100644 --- a/packages/vue-app/template/server.js +++ b/packages/vue-app/template/server.js @@ -85,6 +85,8 @@ export default async (ssrContext) => { ssrContext.next = createNext(ssrContext) // Used for beforeNuxtRender({ Components, nuxtState }) ssrContext.beforeRenderFns = [] + // for beforeSerialize(nuxtState) + ssrContext.beforeSerializeFns = [] // Nuxt object (window.{{globals.context}}, defaults to window.__NUXT__) ssrContext.nuxt = { <% if (features.layouts) { %>layout: 'default', <% } %>data: [], <% if (features.fetch) { %>fetch: {}, <% } %>error: null<%= (store ? ', state: null' : '') %>, serverRendered: true, routePath: '' } <% if (features.fetch) { %> @@ -120,16 +122,21 @@ export default async (ssrContext) => { const beforeRender = async () => { // Call beforeNuxtRender() methods await Promise.all(ssrContext.beforeRenderFns.map(fn => promisify(fn, { Components, nuxtState: ssrContext.nuxt }))) - <% if (store) { %> + ssrContext.rendered = () => { + // Call beforeSerialize() hooks + ssrContext.beforeSerializeFns.forEach(fn => fn(ssrContext.nuxt)) + + <% if (store) { %> // Add the state from the vuex store ssrContext.nuxt.state = store.state + <% } %> + <% if (isFullStatic && store) { %> // Stop recording store mutations ssrContext.unsetMutationObserver() <% } %> } - <% } %> } const renderErrorPage = async () => { diff --git a/packages/vue-app/template/utils.js b/packages/vue-app/template/utils.js index 6d6e9978eb72..2b52d75862fb 100644 --- a/packages/vue-app/template/utils.js +++ b/packages/vue-app/template/utils.js @@ -259,6 +259,7 @@ export async function setContext (app, context) { } if (process.server) { app.context.beforeNuxtRender = fn => context.beforeRenderFns.push(fn) + app.context.beforeSerialize = fn => context.beforeSerializeFns.push(fn) } if (process.client) { app.context.nuxtState = window.<%= globals.context %> diff --git a/test/dev/basic.ssr.test.js b/test/dev/basic.ssr.test.js index acdc078ae870..743c5ea18ff7 100644 --- a/test/dev/basic.ssr.test.js +++ b/test/dev/basic.ssr.test.js @@ -201,10 +201,16 @@ describe('basic ssr', () => { }) }) - test('/special-state -> check window.__NUXT__.test = true', async () => { - const window = await nuxt.server.renderAndGetWindow(url('/special-state')) + test('/before-nuxt-render -> check window.__NUXT__.beforeNuxtRender = true', async () => { + const window = await nuxt.server.renderAndGetWindow(url('/before-nuxt-render')) expect(window.document.title).toBe('Nuxt') - expect(window.__NUXT__.test).toBe(true) + expect(window.__NUXT__.beforeNuxtRender).toBe(true) + }) + + test('/before-serialize -> check window.__NUXT__.beforeSerialize = true', async () => { + const window = await nuxt.server.renderAndGetWindow(url('/before-serialize')) + expect(window.document.title).toBe('Nuxt') + expect(window.__NUXT__.beforeSerialize).toBe(true) }) test('/error', async () => { diff --git a/test/fixtures/basic/pages/special-state.vue b/test/fixtures/basic/pages/before-nuxt-render.vue similarity index 84% rename from test/fixtures/basic/pages/special-state.vue rename to test/fixtures/basic/pages/before-nuxt-render.vue index cc6aed9c87ab..44a190e71631 100644 --- a/test/fixtures/basic/pages/special-state.vue +++ b/test/fixtures/basic/pages/before-nuxt-render.vue @@ -7,7 +7,7 @@ export default { middleware ({ beforeNuxtRender }) { if (process.server) { beforeNuxtRender(({ nuxtState }) => { - nuxtState.test = true + nuxtState.beforeNuxtRender = true }) } } diff --git a/test/fixtures/basic/pages/before-serialize.vue b/test/fixtures/basic/pages/before-serialize.vue new file mode 100644 index 000000000000..caa1e4e2712d --- /dev/null +++ b/test/fixtures/basic/pages/before-serialize.vue @@ -0,0 +1,26 @@ + + +