Fix asyncData on hot reloading

This commit is contained in:
Sébastien Chopin 2017-03-02 17:31:37 +01:00
parent 8253f5e75b
commit b8727b8d51
3 changed files with 41 additions and 41 deletions

View File

@ -8,6 +8,7 @@ Tasks for `0.9.10`:
- [ ] Fork preload-webpack-plugin and use it in package.json - [ ] Fork preload-webpack-plugin and use it in package.json
- [ ] Test + Coverage performance, cache, filenames - [ ] Test + Coverage performance, cache, filenames
- [ ] Manual tests on router.base & publicPath - [ ] 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) -> Not possible to have custom layout for a page, it should do the condition inside the layout itself (because of the middleware strategy)

View File

@ -216,7 +216,7 @@ function fixPrepatch (to, ___) {
// Set layout // Set layout
this.setLayout(this.$options._nuxt.err ? NuxtError.layout : to.matched[0].components.default.options.layout) this.setLayout(this.$options._nuxt.err ? NuxtError.layout : to.matched[0].components.default.options.layout)
// hot reloading // hot reloading
hotReloadAPI(this) Vue.nextTick(() => hotReloadAPI(this))
}) })
} }
@ -224,6 +224,8 @@ function fixPrepatch (to, ___) {
function hotReloadAPI (_app) { function hotReloadAPI (_app) {
if (!module.hot) return if (!module.hot) return
const $nuxt = _app.$nuxt const $nuxt = _app.$nuxt
if ($nuxt._hasHotReload) return
$nuxt._hasHotReload = true
var _forceUpdate = $nuxt.$forceUpdate.bind($nuxt) var _forceUpdate = $nuxt.$forceUpdate.bind($nuxt)
$nuxt.$forceUpdate = function () { $nuxt.$forceUpdate = function () {
let Component = getMatchedComponents(router.currentRoute)[0] let Component = getMatchedComponents(router.currentRoute)[0]
@ -233,13 +235,15 @@ function hotReloadAPI (_app) {
Component = Vue.extend(Component) Component = Vue.extend(Component)
Component._Ctor = Component Component._Ctor = Component
} }
_app.error()
let promises = [] let promises = []
// If layout changed // If layout changed
Component.options.layout = Component.options.layout || 'default'
if (_app.layoutName !== Component.options.layout) { if (_app.layoutName !== Component.options.layout) {
let promise = _app.loadLayout(Component.options.layout) let promise = _app.loadLayout(Component.options.layout)
promise.then(() => { promise.then(() => {
_app.setLayout(Component.options.layout) _app.setLayout(Component.options.layout)
hotReloadAPI(_app) Vue.nextTick(() => hotReloadAPI(_app))
}) })
promises.push(promise) promises.push(promise)
} }
@ -248,37 +252,22 @@ function hotReloadAPI (_app) {
router.push(path) router.push(path)
} }
const context = getContext({ route: router.currentRoute<%= (store ? ', store' : '') %>, isClient: true, next: next.bind(this), error: _app.error }) const context = getContext({ route: router.currentRoute<%= (store ? ', store' : '') %>, isClient: true, next: next.bind(this), error: _app.error })
// Check if data has been updated // Call asyncData()
const originalDataFn = (Component._data || noopData).toString().replace(/\s/g, '') let pAsyncData = promisify(Component.options.asyncData || noopData, context)
const newDataFn = (Component._Ctor.options.data || noopData).toString().replace(/\s/g, '') pAsyncData.then((asyncDataResult) => {
if (originalDataFn !== newDataFn) { let data = (typeof Component.options.data === 'function' ? Component.options.data() : noopData())
Component._data = Component._Ctor.options.data || noopData data = Object.assign(data, asyncDataResult)
let p = promisify(Component._data, context) Component.options.data = () => data
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
Component._Ctor.options.data = Component.options.data Component._Ctor.options.data = Component.options.data
} <%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %>
// Check if fetch has been updated })
const originalFetchFn = (Component.options.fetch || noopFetch).toString().replace(/\s/g, '') promises.push(pAsyncData)
const newFetchFn = (Component._Ctor.options.fetch || noopFetch).toString().replace(/\s/g, '') // Call fetch()
// Fetch has been updated, we call it to update the store Component.options.fetch = Component.options.fetch || noopFetch
if (originalFetchFn !== newFetchFn) { let pFetch = Component.options.fetch(context)
Component.options.fetch = Component._Ctor.options.fetch || noopFetch if (!(pFetch instanceof Promise)) { pFetch = Promise.resolve(pFetch) }
let p = Component.options.fetch(context) <%= (loading ? 'pFetch.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %>
if (!(p instanceof Promise)) { p = Promise.resolve(p) } promises.push(pFetch)
<%= (loading ? 'p.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %>
promises.push(p)
}
if (!promises.length) return;
<%= (loading ? 'this.$loading.start && this.$loading.start()' : '') %> <%= (loading ? 'this.$loading.start && this.$loading.start()' : '') %>
return Promise.all(promises).then(() => { return Promise.all(promises).then(() => {
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %> <%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
@ -306,7 +295,13 @@ const resolveComponents = flatMapComponents(router.match(path), (Component, _, m
Component.extendOptions = Component.options Component.extendOptions = Component.options
} }
if (NUXT.serverRendered) { 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) { if (Component._Ctor && Component._Ctor.options) {
Component._Ctor.options.data = Component.options.data Component._Ctor.options.data = Component.options.data
} }
@ -346,10 +341,12 @@ Promise.all(resolveComponents)
.then(({ _app, Components }) => { .then(({ _app, Components }) => {
const mountApp = () => { const mountApp = () => {
_app.$mount('#__nuxt') _app.$mount('#__nuxt')
// Hot reloading Vue.nextTick(() => {
hotReloadAPI(_app) // Hot reloading
// Call window.onNuxtReady callbacks hotReloadAPI(_app)
Vue.nextTick(() => nuxtReady(_app)) // Call window.onNuxtReady callbacks
nuxtReady(_app)
})
} }
<% if (store) { %> <% if (store) { %>
// Replace store state // Replace store state

View File

@ -133,14 +133,18 @@ export default context => {
Component._Ctor.options.data = Component.options.data Component._Ctor.options.data = Component.options.data
}) })
promises.push(promise) promises.push(promise)
} else {
promises.push(null)
} }
if (Component.options.fetch) { if (Component.options.fetch) {
promises.push(Component.options.fetch(ctx)) promises.push(Component.options.fetch(ctx))
} else {
promises.push(null)
} }
return Promise.all(promises) return Promise.all(promises)
})) }))
}) })
.then(() => { .then((res) => {
if (!Components.length) { if (!Components.length) {
context.nuxt.error = context.error({ statusCode: 404, message: 'This page could not be found.' }) context.nuxt.error = context.error({ statusCode: 404, message: 'This page could not be found.' })
<%= (store ? 'context.nuxt.state = store.state' : '') %> <%= (store ? 'context.nuxt.state = store.state' : '') %>
@ -150,9 +154,7 @@ export default context => {
debug('Data fetching ' + context.req.url + ': ' + (Date.now() - s) + 'ms') debug('Data fetching ' + context.req.url + ': ' + (Date.now() - s) + 'ms')
<% } %> <% } %>
// datas are the first row of each // datas are the first row of each
context.nuxt.data = Components.map((Component) => { context.nuxt.data = res.map((r) => (r[0] || {}))
return (typeof Component.options.data === 'function' ? Component.options.data() : {})
})
context.nuxt.error = _app.$options._nuxt.err context.nuxt.error = _app.$options._nuxt.err
<%= (store ? '// Add the state from the vuex store' : '') %> <%= (store ? '// Add the state from the vuex store' : '') %>
<%= (store ? 'context.nuxt.state = store.state' : '') %> <%= (store ? 'context.nuxt.state = store.state' : '') %>