From b8727b8d518dcec938120763783bda8e1a505888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Thu, 2 Mar 2017 17:31:37 +0100 Subject: [PATCH] Fix asyncData on hot reloading --- TODO.md | 1 + lib/app/client.js | 71 +++++++++++++++++++++++------------------------ lib/app/server.js | 10 ++++--- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/TODO.md b/TODO.md index 695af7a08b..628ba948ea 100644 --- a/TODO.md +++ b/TODO.md @@ -8,6 +8,7 @@ Tasks for `0.9.10`: - [ ] Fork preload-webpack-plugin and use it in package.json - [ ] Test + Coverage performance, cache, filenames - [ ] Manual tests on router.base & publicPath +- [ ] asyncData, fetch, transition, validate, scrollToTop can be in mixins and extend (super) -> Not possible to have custom layout for a page, it should do the condition inside the layout itself (because of the middleware strategy) diff --git a/lib/app/client.js b/lib/app/client.js index 030013a4cc..b85e261bbd 100644 --- a/lib/app/client.js +++ b/lib/app/client.js @@ -216,7 +216,7 @@ function fixPrepatch (to, ___) { // Set layout this.setLayout(this.$options._nuxt.err ? NuxtError.layout : to.matched[0].components.default.options.layout) // hot reloading - hotReloadAPI(this) + Vue.nextTick(() => hotReloadAPI(this)) }) } @@ -224,6 +224,8 @@ function fixPrepatch (to, ___) { function hotReloadAPI (_app) { if (!module.hot) return const $nuxt = _app.$nuxt + if ($nuxt._hasHotReload) return + $nuxt._hasHotReload = true var _forceUpdate = $nuxt.$forceUpdate.bind($nuxt) $nuxt.$forceUpdate = function () { let Component = getMatchedComponents(router.currentRoute)[0] @@ -233,13 +235,15 @@ function hotReloadAPI (_app) { Component = Vue.extend(Component) Component._Ctor = Component } + _app.error() let promises = [] // If layout changed + Component.options.layout = Component.options.layout || 'default' if (_app.layoutName !== Component.options.layout) { let promise = _app.loadLayout(Component.options.layout) promise.then(() => { _app.setLayout(Component.options.layout) - hotReloadAPI(_app) + Vue.nextTick(() => hotReloadAPI(_app)) }) promises.push(promise) } @@ -248,37 +252,22 @@ function hotReloadAPI (_app) { router.push(path) } const context = getContext({ route: router.currentRoute<%= (store ? ', store' : '') %>, isClient: true, next: next.bind(this), error: _app.error }) - // Check if data has been updated - const originalDataFn = (Component._data || noopData).toString().replace(/\s/g, '') - const newDataFn = (Component._Ctor.options.data || noopData).toString().replace(/\s/g, '') - if (originalDataFn !== newDataFn) { - Component._data = Component._Ctor.options.data || noopData - let p = promisify(Component._data, context) - p.then((data) => { - Component._cData = () => data || {} - Component.options.data = Component._cData - Component._dataFn = Component.options.data.toString().replace(/\s/g, '') - Component._Ctor.options.data = Component.options.data - <%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %> - }) - promises.push(p) - } else if (Component._cData) { - Component._data = Component.options.data - Component.options.data = Component._cData + // Call asyncData() + let pAsyncData = promisify(Component.options.asyncData || noopData, context) + pAsyncData.then((asyncDataResult) => { + let data = (typeof Component.options.data === 'function' ? Component.options.data() : noopData()) + data = Object.assign(data, asyncDataResult) + Component.options.data = () => data Component._Ctor.options.data = Component.options.data - } - // Check if fetch has been updated - const originalFetchFn = (Component.options.fetch || noopFetch).toString().replace(/\s/g, '') - const newFetchFn = (Component._Ctor.options.fetch || noopFetch).toString().replace(/\s/g, '') - // Fetch has been updated, we call it to update the store - if (originalFetchFn !== newFetchFn) { - Component.options.fetch = Component._Ctor.options.fetch || noopFetch - let p = Component.options.fetch(context) - if (!(p instanceof Promise)) { p = Promise.resolve(p) } - <%= (loading ? 'p.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %> - promises.push(p) - } - if (!promises.length) return; + <%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %> + }) + promises.push(pAsyncData) + // Call fetch() + Component.options.fetch = Component.options.fetch || noopFetch + let pFetch = Component.options.fetch(context) + if (!(pFetch instanceof Promise)) { pFetch = Promise.resolve(pFetch) } + <%= (loading ? 'pFetch.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %> + promises.push(pFetch) <%= (loading ? 'this.$loading.start && this.$loading.start()' : '') %> return Promise.all(promises).then(() => { <%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %> @@ -306,7 +295,13 @@ const resolveComponents = flatMapComponents(router.match(path), (Component, _, m Component.extendOptions = Component.options } if (NUXT.serverRendered) { - Component.options.data = () => NUXT.data[index] + let data = {} + if (Component.options.data && typeof Component.options.data === 'function') { + data = Component.options.data() + } + // Merge data() and asyncData() results + data = Object.assign(data, NUXT.data[index]) + Component.options.data = () => data if (Component._Ctor && Component._Ctor.options) { Component._Ctor.options.data = Component.options.data } @@ -346,10 +341,12 @@ Promise.all(resolveComponents) .then(({ _app, Components }) => { const mountApp = () => { _app.$mount('#__nuxt') - // Hot reloading - hotReloadAPI(_app) - // Call window.onNuxtReady callbacks - Vue.nextTick(() => nuxtReady(_app)) + Vue.nextTick(() => { + // Hot reloading + hotReloadAPI(_app) + // Call window.onNuxtReady callbacks + nuxtReady(_app) + }) } <% if (store) { %> // Replace store state diff --git a/lib/app/server.js b/lib/app/server.js index ee2dfdc21d..1bdae940ec 100644 --- a/lib/app/server.js +++ b/lib/app/server.js @@ -133,14 +133,18 @@ export default context => { Component._Ctor.options.data = Component.options.data }) promises.push(promise) + } else { + promises.push(null) } if (Component.options.fetch) { promises.push(Component.options.fetch(ctx)) + } else { + promises.push(null) } return Promise.all(promises) })) }) - .then(() => { + .then((res) => { if (!Components.length) { context.nuxt.error = context.error({ statusCode: 404, message: 'This page could not be found.' }) <%= (store ? 'context.nuxt.state = store.state' : '') %> @@ -150,9 +154,7 @@ export default context => { debug('Data fetching ' + context.req.url + ': ' + (Date.now() - s) + 'ms') <% } %> // datas are the first row of each - context.nuxt.data = Components.map((Component) => { - return (typeof Component.options.data === 'function' ? Component.options.data() : {}) - }) + context.nuxt.data = res.map((r) => (r[0] || {})) context.nuxt.error = _app.$options._nuxt.err <%= (store ? '// Add the state from the vuex store' : '') %> <%= (store ? 'context.nuxt.state = store.state' : '') %>