Layout with code-splitting!

This commit is contained in:
Sébastien Chopin 2016-12-24 12:34:41 +01:00
parent afd7eb86e6
commit 3a0fcdee73
5 changed files with 106 additions and 61 deletions

View File

@ -1,5 +1,8 @@
<template> <template>
<h1>Custom error page</h1> <div class="container">
<h1>Sorry, page not found</h1>
<nuxt-link to="/">Home page</nuxt-link>
</div>
</template> </template>
<script> <script>
@ -7,3 +10,14 @@ export default {
props: ['error'] props: ['error']
} }
</script> </script>
<style scoped>
.container {
font-family: sans-serif;
padding-top: 10%;
text-align: center;
}
h1 {
font-size: 20px;
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -1,6 +1,6 @@
<template> <template>
<nuxt-container> <nuxt-container>
<component :is="layout"></component> <component v-if="layout" :is="layout"></component>
</nuxt-container> </nuxt-container>
</template> </template>
@ -9,19 +9,37 @@ let layouts = {
<% <%
var layoutsKeys = Object.keys(layouts); var layoutsKeys = Object.keys(layouts);
layoutsKeys.forEach(function (key, i) { %> layoutsKeys.forEach(function (key, i) { %>
_<%= key %>: require('<%= layouts[key] %>')<%= (i + 1) < layoutsKeys.length ? ',' : '' %> _<%= key %>: process.BROWSER_BUILD ? () => System.import('<%= layouts[key] %>') : require('<%= layouts[key] %>')<%= (i + 1) < layoutsKeys.length ? ',' : '' %>
<% }) %> <% }) %>
} }
export default { export default {
data () { data () {
return { layout: layouts._default } return { layout: null }
}, },
methods: { methods: {
setLayout (layout) { setLayout (layout) {
if (!layout || !layouts['_' + layout]) layout = 'default' if (!layout || !layouts['_' + layout]) layout = 'default'
this.layout = layouts['_' + layout] let _layout = '_' + layout
return layout if (typeof layouts[_layout] === 'function') {
return this.loadLayout(_layout)
}
this.layout = layouts[_layout]
return Promise.resolve(this.layout)
},
loadLayout (_layout) {
return layouts[_layout]()
.then((Component) => {
layouts[_layout] = Component
this.layout = layouts[_layout]
return this.layout
})
.catch((e) => {
if (this.$nuxt) {
return this.$nuxt.error({ statusCode: 500, message: e.message })
}
console.error(e)
})
} }
} }
} }

View File

@ -58,8 +58,13 @@ function loadAsyncComponents (to, ___, next) {
function render (to, from, next) { function render (to, from, next) {
let Components = getMatchedComponents(to) let Components = getMatchedComponents(to)
if (!Components.length) { if (!Components.length) {
this.error({ statusCode: 404, message: 'This page could not be found.', url: to.path }) // Default layout
return next() this.setLayout()
.then(() => {
this.error({ statusCode: 404, message: 'This page could not be found.', url: to.path })
return next()
})
return
} }
// Update ._data and other properties if hot reloaded // Update ._data and other properties if hot reloaded
Components.forEach(function (Component) { Components.forEach(function (Component) {
@ -79,57 +84,61 @@ function render (to, from, next) {
} }
} }
}) })
this.setLayout(Components[0].options.layout)
this.setTransitions(mapTransitions(Components, to, from)) this.setTransitions(mapTransitions(Components, to, from))
this.error() this.error()
let nextCalled = false let nextCalled = false
let isValid = true // Set layout
Components.forEach((Component) => { this.setLayout(Components[0].options.layout)
if (!isValid) return .then(() => {
if (typeof Component.options.validate !== 'function') return // Pass validation?
isValid = Component.options.validate({ let isValid = true
params: to.params || {}, Components.forEach((Component) => {
query: to.query || {} if (!isValid) return
}) if (typeof Component.options.validate !== 'function') return
}) isValid = Component.options.validate({
if (!isValid) { params: to.params || {},
this.error({ statusCode: 404, message: 'This page could not be found.', url: to.path }) query: to.query || {}
return next()
}
Promise.all(Components.map((Component, i) => {
// Check if only children route changed
Component._path = compile(to.matched[i].path)(to.params)
if (Component._path === _lastPaths[i] && (i + 1) !== Components.length) {
return Promise.resolve()
}
let promises = []
const _next = function (path) {
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
nextCalled = true
next(path)
}
const context = getContext({ to<%= (store ? ', store' : '') %>, isClient: true, next: _next.bind(this), error: this.error.bind(this) })
// Validate method
if (Component._data && typeof Component._data === 'function') {
var promise = promisify(Component._data, context)
promise.then((data) => {
Component.options.data = () => data || {}
Component._dataFn = Component.options.data.toString().replace(/\s/g, '')
if (Component._Ctor && Component._Ctor.options) {
Component._Ctor.options.data = Component.options.data
}
<%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %>
}) })
promises.push(promise) })
if (!isValid) {
this.error({ statusCode: 404, message: 'This page could not be found.', url: to.path })
return next()
} }
if (Component.options.fetch) { return Promise.all(Components.map((Component, i) => {
var p = Component.options.fetch(context) // Check if only children route changed
if (!(p instanceof Promise)) { p = Promise.resolve(p) } Component._path = compile(to.matched[i].path)(to.params)
<%= (loading ? 'p.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %> if (Component._path === _lastPaths[i] && (i + 1) !== Components.length) {
promises.push(p) return Promise.resolve()
} }
return Promise.all(promises) let promises = []
})) const _next = function (path) {
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
nextCalled = true
next(path)
}
const context = getContext({ to<%= (store ? ', store' : '') %>, isClient: true, next: _next.bind(this), error: this.error.bind(this) })
// Validate method
if (Component._data && typeof Component._data === 'function') {
var promise = promisify(Component._data, context)
promise.then((data) => {
Component.options.data = () => data || {}
Component._dataFn = Component.options.data.toString().replace(/\s/g, '')
if (Component._Ctor && Component._Ctor.options) {
Component._Ctor.options.data = Component.options.data
}
<%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %>
})
promises.push(promise)
}
if (Component.options.fetch) {
var 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)
}
return Promise.all(promises)
}))
})
.then(() => { .then(() => {
_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()' : '') %>
@ -175,8 +184,8 @@ function hotReloadAPI (_app) {
Component = Vue.extend(Component) Component = Vue.extend(Component)
Component._Ctor = Component Component._Ctor = Component
} }
_app.setLayout(Component.options.layout)
let promises = [] let promises = []
promises.push(_app.setLayout(Component.options.layout))
const next = function (path) { const next = function (path) {
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %> <%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
router.push(path) router.push(path)
@ -268,6 +277,13 @@ function nuxtReady (app) {
Promise.all(resolveComponents) Promise.all(resolveComponents)
.then((Components) => { .then((Components) => {
const _app = new Vue(app) const _app = new Vue(app)
return _app.setLayout(Components.length ? Components[0].options.layout : '')
.then(() => {
return { _app, Components }
})
})
.then(({ _app, Components }) => {
const mountApp = () => { const mountApp = () => {
_app.$mount('#__nuxt') _app.$mount('#__nuxt')
<% if (loading) { %> <% if (loading) { %>
@ -285,9 +301,6 @@ Promise.all(resolveComponents)
store.replaceState(NUXT.state) store.replaceState(NUXT.state)
} }
<% } %> <% } %>
if (Components.length) {
_app.setLayout(Components[0].options.layout)
}
_app.setTransitions = _app.$options._nuxt.setTransitions.bind(_app) _app.setTransitions = _app.$options._nuxt.setTransitions.bind(_app)
if (Components.length) { if (Components.length) {
_app.setTransitions(mapTransitions(Components, router.currentRoute)) _app.setTransitions(mapTransitions(Components, router.currentRoute))

View File

@ -74,9 +74,9 @@ export default context => {
return Component return Component
}) })
// Set layout // Set layout
if (Components.length && Components[0].options.layout) { return _app.setLayout(Components.length ? Components[0].options.layout : '')
_app.setLayout(Components[0].options.layout) })
} .then(() => {
// Call .validate() // Call .validate()
let isValid = true let isValid = true
Components.forEach((Component) => { Components.forEach((Component) => {