Remove dynamic components

This commit is contained in:
Sébastien Chopin 2017-07-09 23:06:17 +02:00
parent f07d9855b6
commit 2cc78c0446
5 changed files with 14 additions and 83 deletions

View File

@ -160,11 +160,9 @@ async function render (to, from, next) {
return Promise.resolve() return Promise.resolve()
} }
let promises = [] let promises = []
// Create this context for asyncData & fetch (used for dynamic component injection)
const _this = { components: {} }
// asyncData method // asyncData method
if (Component.options.asyncData && typeof Component.options.asyncData === 'function') { if (Component.options.asyncData && typeof Component.options.asyncData === 'function') {
var promise = promisify(Component.options.asyncData.bind(_this), context) var promise = promisify(Component.options.asyncData, context)
promise.then((asyncDataResult) => { promise.then((asyncDataResult) => {
applyAsyncData(Component, asyncDataResult) applyAsyncData(Component, asyncDataResult)
<%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %> <%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %>
@ -172,19 +170,12 @@ async function render (to, from, next) {
promises.push(promise) promises.push(promise)
} }
if (Component.options.fetch) { if (Component.options.fetch) {
var p = Component.options.fetch.call(_this, context) var p = Component.options.fetch(context)
if (!p || (!(p instanceof Promise) && (typeof p.then !== 'function'))) { p = Promise.resolve(p) } if (!p || (!(p instanceof Promise) && (typeof p.then !== 'function'))) { p = Promise.resolve(p) }
<%= (loading ? 'p.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %> <%= (loading ? 'p.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %>
promises.push(p) promises.push(p)
} }
return Promise.all(promises) return Promise.all(promises)
.then(() => {
Object.keys(_this.components).forEach((name) => {
// Sanetize resolved components (Temporary workaround for vue-loader 13.0.0)
_this.components[name] = _this.components[name].default || _this.components[name]
Component.options.components[name] = _this.components[name]
})
})
})) }))
_lastPaths = Components.map((Component, i) => compile(to.matched[i].path)(to.params)) _lastPaths = Components.map((Component, i) => compile(to.matched[i].path)(to.params))
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %> <%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>

View File

@ -56,11 +56,7 @@ export default async context => {
// Add route to the context // Add route to the context
context.route = router.currentRoute context.route = router.currentRoute
// Components array (for dynamic components)
context.hasDynamicComponents = false
context.components = []
// Nuxt object // Nuxt object
context.nuxt = { layout: 'default', data: [], error: null<%= (store ? ', state: null' : '') %>, serverRendered: true } context.nuxt = { layout: 'default', data: [], error: null<%= (store ? ', state: null' : '') %>, serverRendered: true }
@ -171,12 +167,9 @@ export default async context => {
let asyncDatas = await Promise.all(Components.map(Component => { let asyncDatas = await Promise.all(Components.map(Component => {
let promises = [] let promises = []
// Create this context for asyncData & fetch (used for dynamic component injection)
const _this = { components: {} }
// Call asyncData(context) // Call asyncData(context)
if (Component.options.asyncData && typeof Component.options.asyncData === 'function') { if (Component.options.asyncData && typeof Component.options.asyncData === 'function') {
let promise = promisify(Component.options.asyncData.bind(_this), ctx) let promise = promisify(Component.options.asyncData, ctx)
promise.then(asyncDataResult => { promise.then(asyncDataResult => {
context.asyncData[Component.options.name] = asyncDataResult context.asyncData[Component.options.name] = asyncDataResult
applyAsyncData(Component) applyAsyncData(Component)
@ -189,39 +182,13 @@ export default async context => {
// Call fetch(context) // Call fetch(context)
if (Component.options.fetch) { if (Component.options.fetch) {
promises.push(Component.options.fetch.call(_this, ctx)) promises.push(Component.options.fetch(ctx))
} }
else { else {
promises.push(null) promises.push(null)
} }
return Promise.all(promises).then(data => { return Promise.all(promises)
// If no dynamic component, return data directly
if (Object.keys(_this.components).length === 0) {
return data
}
// Sanetize resolved components (Temporary workaround for vue-loader 13.0.0)
Object.keys(_this.components).forEach(name => {
_this.components[name] = _this.components[name].default || _this.components[name]
})
// Tell renderer that dynamic components has been added
context.hasDynamicComponents = true
// Add Component on server side (clone of it)
Component.options.components = {
...Component.options.components,
...clone(_this.components) // Clone it to avoid vue to overwrite references
}
// Add components into __NUXT__ for client-side hydration
// We clone it since vue-server-renderer will update the component definition
context.components.push(sanitizeDynamicComponents(_this.components))
// Return data to server-render them
return data
})
})) }))
// If no Components found, returns 404 // If no Components found, returns 404
@ -257,23 +224,3 @@ export default async context => {
return _app return _app
} }
function sanitizeDynamicComponents(components) {
Object.keys(components).forEach((name) => {
const component = components[name]
// Remove SSR register hookd
if (Array.isArray(component.beforeCreate)) {
component.beforeCreate = component.beforeCreate.filter((fn) => fn !== component._ssrRegister)
if (!component.beforeCreate.length) delete component.beforeCreate
}
// Remove SSR & informations properties
delete component._ssrRegister
delete component.__file
if (component.staticRenderFns && !component.staticRenderFns.length) {
delete component.staticRenderFns
}
// Add Component to NUXT.components[i][name]
components[name] = component
})
return clone(components)
}

View File

@ -345,12 +345,7 @@ export default class Renderer extends Tapable {
HEAD += resourceHints HEAD += resourceHints
} }
HEAD += context.renderStyles() HEAD += context.renderStyles()
APP += `<script type="text/javascript">` APP += `<script type="text/javascript">window.__NUXT__=${serialize(context.nuxt, { isJSON: true })};</script>`
APP += `window.__NUXT__=${serialize(context.nuxt, { isJSON: true })};`
if (context.hasDynamicComponents) {
APP += `window.__COMPONENTS__=${serialize(context.components)};`
}
APP += `</script>`
APP += context.renderScripts() APP += context.renderScripts()
let html = this.resources.appTemplate({ let html = this.resources.appTemplate({

View File

@ -5,14 +5,11 @@
</template> </template>
<script> <script>
const AsyncTest = () => import('@/components/test.vue').then((m) => m.default || m)
export default { export default {
async asyncData({ error }) { components:{
try { AsyncTest
this.components.AsyncTest = await import(`@/components/test.vue`)
} catch (e) {
error({ statusCode: 404, message: 'Can not load test component' })
}
} }
} }
</script> </script>

View File

@ -62,9 +62,10 @@ test('unique responses with component', async t => {
await uniqueTest(t, '/component') await uniqueTest(t, '/component')
}) })
test('unique responses with async components', async t => { test.todo('unique responses with async components (wait Vue 2.4)')
await uniqueTest(t, '/asyncComponent') // test('unique responses with async components', async t => {
}) // await uniqueTest(t, '/asyncComponent')
// })
test('unique responses with asyncData()', async t => { test('unique responses with asyncData()', async t => {
await uniqueTest(t, '/asyncData') await uniqueTest(t, '/asyncData')