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>
<h1>Custom error page</h1>
<div class="container">
<h1>Sorry, page not found</h1>
<nuxt-link to="/">Home page</nuxt-link>
</div>
</template>
<script>
@ -7,3 +10,14 @@ export default {
props: ['error']
}
</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>
<nuxt-container>
<component :is="layout"></component>
<component v-if="layout" :is="layout"></component>
</nuxt-container>
</template>
@ -9,19 +9,37 @@ let layouts = {
<%
var layoutsKeys = Object.keys(layouts);
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 {
data () {
return { layout: layouts._default }
return { layout: null }
},
methods: {
setLayout (layout) {
if (!layout || !layouts['_' + layout]) layout = 'default'
this.layout = layouts['_' + layout]
return layout
let _layout = '_' + 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) {
let Components = getMatchedComponents(to)
if (!Components.length) {
this.error({ statusCode: 404, message: 'This page could not be found.', url: to.path })
return next()
// Default layout
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
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.error()
let nextCalled = false
let isValid = true
Components.forEach((Component) => {
if (!isValid) return
if (typeof Component.options.validate !== 'function') return
isValid = Component.options.validate({
params: to.params || {},
query: to.query || {}
})
})
if (!isValid) {
this.error({ statusCode: 404, message: 'This page could not be found.', url: to.path })
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)' : '') %>
// Set layout
this.setLayout(Components[0].options.layout)
.then(() => {
// Pass validation?
let isValid = true
Components.forEach((Component) => {
if (!isValid) return
if (typeof Component.options.validate !== 'function') return
isValid = Component.options.validate({
params: to.params || {},
query: to.query || {}
})
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) {
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)
}))
return 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 (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(() => {
_lastPaths = Components.map((Component, i) => compile(to.matched[i].path)(to.params))
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
@ -175,8 +184,8 @@ function hotReloadAPI (_app) {
Component = Vue.extend(Component)
Component._Ctor = Component
}
_app.setLayout(Component.options.layout)
let promises = []
promises.push(_app.setLayout(Component.options.layout))
const next = function (path) {
<%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
router.push(path)
@ -268,6 +277,13 @@ function nuxtReady (app) {
Promise.all(resolveComponents)
.then((Components) => {
const _app = new Vue(app)
return _app.setLayout(Components.length ? Components[0].options.layout : '')
.then(() => {
return { _app, Components }
})
})
.then(({ _app, Components }) => {
const mountApp = () => {
_app.$mount('#__nuxt')
<% if (loading) { %>
@ -285,9 +301,6 @@ Promise.all(resolveComponents)
store.replaceState(NUXT.state)
}
<% } %>
if (Components.length) {
_app.setLayout(Components[0].options.layout)
}
_app.setTransitions = _app.$options._nuxt.setTransitions.bind(_app)
if (Components.length) {
_app.setTransitions(mapTransitions(Components, router.currentRoute))

View File

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