diff --git a/examples/custom-layout/layouts/error.vue b/examples/custom-layout/layouts/error.vue index 522cd13f4b..0a6bb332e3 100644 --- a/examples/custom-layout/layouts/error.vue +++ b/examples/custom-layout/layouts/error.vue @@ -1,5 +1,8 @@ + + diff --git a/examples/custom-layout/static/logo.png b/examples/custom-layout/static/logo.png index 9d716abf22..7f238b598c 100644 Binary files a/examples/custom-layout/static/logo.png and b/examples/custom-layout/static/logo.png differ diff --git a/lib/app/App.vue b/lib/app/App.vue index b3ecb7b7af..c69572212e 100644 --- a/lib/app/App.vue +++ b/lib/app/App.vue @@ -1,6 +1,6 @@ @@ -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) + }) } } } diff --git a/lib/app/client.js b/lib/app/client.js index 7e1c226ad1..6dfd426abd 100644 --- a/lib/app/client.js +++ b/lib/app/client.js @@ -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)) diff --git a/lib/app/server.js b/lib/app/server.js index 2846e56716..0a6704de44 100644 --- a/lib/app/server.js +++ b/lib/app/server.js @@ -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) => {