mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-13 09:33:54 +00:00
Refactor to use only one context and add route.meta
This commit is contained in:
parent
031b0d58c8
commit
7534001123
@ -1,4 +1,3 @@
|
|||||||
<script>
|
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
<% if (loading) { %>import NuxtLoading from '<%= (typeof loading === "string" ? loading : "./components/nuxt-loading.vue") %>'<% } %>
|
<% if (loading) { %>import NuxtLoading from '<%= (typeof loading === "string" ? loading : "./components/nuxt-loading.vue") %>'<% } %>
|
||||||
<% css.forEach(function (c) { %>
|
<% css.forEach(function (c) { %>
|
||||||
@ -19,7 +18,6 @@ export default {
|
|||||||
head: <%= JSON.stringify(head) %>,
|
head: <%= JSON.stringify(head) %>,
|
||||||
render(h, props) {
|
render(h, props) {
|
||||||
<% if (loading) { %>const loadingEl = h('nuxt-loading', { ref: 'loading' })<% } %>
|
<% if (loading) { %>const loadingEl = h('nuxt-loading', { ref: 'loading' })<% } %>
|
||||||
|
|
||||||
const layoutEl = h(this.nuxt.err ? 'nuxt' : this.layout, {
|
const layoutEl = h(this.nuxt.err ? 'nuxt' : this.layout, {
|
||||||
key: this.layoutName
|
key: this.layoutName
|
||||||
})
|
})
|
||||||
@ -45,7 +43,7 @@ export default {
|
|||||||
layoutName: ''
|
layoutName: ''
|
||||||
}),
|
}),
|
||||||
beforeCreate () {
|
beforeCreate () {
|
||||||
Vue.util.defineReactive(this, 'nuxt', this.$options._nuxt)
|
Vue.util.defineReactive(this, 'nuxt', this.$options.nuxt)
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
// Add this.$nuxt in child instances
|
// Add this.$nuxt in child instances
|
||||||
@ -104,5 +102,4 @@ export default {
|
|||||||
<%= (loading ? 'NuxtLoading' : '') %>
|
<%= (loading ? 'NuxtLoading' : '') %>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'nuxt-link',
|
name: 'nuxt-link',
|
||||||
functional: true,
|
functional: true,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
<script>
|
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import NuxtChild from './nuxt-child'
|
import NuxtChild from './nuxt-child'
|
||||||
import NuxtError from '<%= components.ErrorPage ? ((components.ErrorPage.includes('~') || components.ErrorPage.includes('@')) ? components.ErrorPage : "../" + components.ErrorPage) : "./nuxt-error.vue" %>'
|
import NuxtError from '<%= components.ErrorPage ? ((components.ErrorPage.includes('~') || components.ErrorPage.includes('@')) ? components.ErrorPage : "../" + components.ErrorPage) : "./nuxt-error.vue" %>'
|
||||||
@ -22,7 +21,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
beforeCreate () {
|
beforeCreate () {
|
||||||
Vue.util.defineReactive(this, 'nuxt', this.$root.$options._nuxt)
|
Vue.util.defineReactive(this, 'nuxt', this.$root.$options.nuxt)
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
routerViewKey () {
|
routerViewKey () {
|
||||||
@ -38,4 +37,3 @@ export default {
|
|||||||
NuxtError
|
NuxtError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
|
@ -6,9 +6,9 @@ import NoSSR from './components/no-ssr.js'
|
|||||||
import NuxtChild from './components/nuxt-child.js'
|
import NuxtChild from './components/nuxt-child.js'
|
||||||
import NuxtLink from './components/nuxt-link.js'
|
import NuxtLink from './components/nuxt-link.js'
|
||||||
import NuxtError from '<%= components.ErrorPage ? components.ErrorPage : "./components/nuxt-error.vue" %>'
|
import NuxtError from '<%= components.ErrorPage ? components.ErrorPage : "./components/nuxt-error.vue" %>'
|
||||||
import Nuxt from './components/nuxt.vue'
|
import Nuxt from './components/nuxt.js'
|
||||||
import App from '<%= appPath %>'
|
import App from '<%= appPath %>'
|
||||||
import { getContext, getLocation } from './utils'
|
import { setContext, getLocation } from './utils'
|
||||||
<% if (store) { %>import { createStore } from './store.js'<% } %>
|
<% if (store) { %>import { createStore } from './store.js'<% } %>
|
||||||
<% plugins.forEach(plugin => { %>import <%= plugin.name %> from '<%= plugin.name %>'
|
<% plugins.forEach(plugin => { %>import <%= plugin.name %> from '<%= plugin.name %>'
|
||||||
<% }) %>
|
<% }) %>
|
||||||
@ -37,7 +37,8 @@ const defaultTransition = <%=
|
|||||||
serialize(transition)
|
serialize(transition)
|
||||||
.replace('beforeEnter(', 'function(').replace('enter(', 'function(').replace('afterEnter(', 'function(')
|
.replace('beforeEnter(', 'function(').replace('enter(', 'function(').replace('afterEnter(', 'function(')
|
||||||
.replace('enterCancelled(', 'function(').replace('beforeLeave(', 'function(').replace('leave(', 'function(')
|
.replace('enterCancelled(', 'function(').replace('beforeLeave(', 'function(').replace('leave(', 'function(')
|
||||||
.replace('afterLeave(', 'function(').replace('leaveCancelled(', 'function(')
|
.replace('afterLeave(', 'function(').replace('leaveCancelled(', 'function(').replace('beforeAppear(', 'function(')
|
||||||
|
.replace('appear(', 'function(').replace('afterAppear(', 'function(').replace('appearCancelled(', 'function(')
|
||||||
%>
|
%>
|
||||||
|
|
||||||
async function createApp (ssrContext) {
|
async function createApp (ssrContext) {
|
||||||
@ -55,7 +56,7 @@ async function createApp (ssrContext) {
|
|||||||
const app = {
|
const app = {
|
||||||
router,
|
router,
|
||||||
<% if (store) { %>store,<% } %>
|
<% if (store) { %>store,<% } %>
|
||||||
_nuxt: {
|
nuxt: {
|
||||||
defaultTransition,
|
defaultTransition,
|
||||||
transitions: [ defaultTransition ],
|
transitions: [ defaultTransition ],
|
||||||
setTransitions (transitions) {
|
setTransitions (transitions) {
|
||||||
@ -72,29 +73,30 @@ async function createApp (ssrContext) {
|
|||||||
}
|
}
|
||||||
return transition
|
return transition
|
||||||
})
|
})
|
||||||
this.$options._nuxt.transitions = transitions
|
this.$options.nuxt.transitions = transitions
|
||||||
return transitions
|
return transitions
|
||||||
},
|
},
|
||||||
err: null,
|
err: null,
|
||||||
dateErr: null,
|
dateErr: null,
|
||||||
error (err) {
|
error (err) {
|
||||||
err = err || null
|
err = err || null
|
||||||
if (typeof err === 'string') {
|
if (typeof err === 'string') err = { statusCode: 500, message: err }
|
||||||
err = { statusCode: 500, message: err }
|
const nuxt = this.nuxt || this.$options.nuxt
|
||||||
}
|
nuxt.dateErr = Date.now()
|
||||||
const _nuxt = this._nuxt || this.$options._nuxt
|
nuxt.err = err
|
||||||
_nuxt.dateErr = Date.now()
|
// Used in lib/server.js
|
||||||
_nuxt.err = err
|
if (ssrContext) ssrContext.nuxt.error = err
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
...App
|
...App
|
||||||
}
|
}
|
||||||
<% if (store) { %>
|
<% if (store) { %>
|
||||||
// Make app available in store
|
// Make app available into store via this.app
|
||||||
store.app = app
|
store.app = app
|
||||||
<% } %>
|
<% } %>
|
||||||
const next = ssrContext ? ssrContext.next : location => app.router.push(location)
|
const next = ssrContext ? ssrContext.next : location => app.router.push(location)
|
||||||
|
// Resolve route
|
||||||
let route
|
let route
|
||||||
if (ssrContext) {
|
if (ssrContext) {
|
||||||
route = router.resolve(ssrContext.url).route
|
route = router.resolve(ssrContext.url).route
|
||||||
@ -102,17 +104,19 @@ async function createApp (ssrContext) {
|
|||||||
const path = getLocation(router.options.base)
|
const path = getLocation(router.options.base)
|
||||||
route = router.resolve(path).route
|
route = router.resolve(path).route
|
||||||
}
|
}
|
||||||
const ctx = getContext({
|
|
||||||
|
// Set context to app.context
|
||||||
|
await setContext(app, {
|
||||||
isServer: !!ssrContext,
|
isServer: !!ssrContext,
|
||||||
isClient: !ssrContext,
|
isClient: !ssrContext,
|
||||||
route,
|
route,
|
||||||
next,
|
next,
|
||||||
error: app._nuxt.error.bind(app),
|
error: app.nuxt.error.bind(app),
|
||||||
<% if (store) { %>store,<% } %>
|
<% if (store) { %>store,<% } %>
|
||||||
req: ssrContext ? ssrContext.req : undefined,
|
req: ssrContext ? ssrContext.req : undefined,
|
||||||
res: ssrContext ? ssrContext.res : undefined,
|
res: ssrContext ? ssrContext.res : undefined,
|
||||||
beforeRenderFns: ssrContext ? ssrContext.beforeRenderFns : undefined
|
beforeRenderFns: ssrContext ? ssrContext.beforeRenderFns : undefined
|
||||||
}, app)
|
})
|
||||||
|
|
||||||
const inject = function (key, value) {
|
const inject = function (key, value) {
|
||||||
if (!key) throw new Error('inject(key, value) has no key provided')
|
if (!key) throw new Error('inject(key, value) has no key provided')
|
||||||
@ -120,11 +124,12 @@ async function createApp (ssrContext) {
|
|||||||
key = '$' + key
|
key = '$' + key
|
||||||
// Add into app
|
// Add into app
|
||||||
app[key] = value
|
app[key] = value
|
||||||
// Add into vm
|
// Check if plugin not already installed
|
||||||
|
const installKey = '__nuxt_' + key + '_installed__'
|
||||||
|
if (Vue[installKey]) return
|
||||||
|
Vue[installKey] = true
|
||||||
|
// Call Vue.use() to install the plugin into vm
|
||||||
Vue.use(() => {
|
Vue.use(() => {
|
||||||
const installKey = '__nuxt_' + key + '_installed__'
|
|
||||||
if (Vue[installKey]) return
|
|
||||||
Vue[installKey] = true
|
|
||||||
if (!Vue.prototype.hasOwnProperty(key)) {
|
if (!Vue.prototype.hasOwnProperty(key)) {
|
||||||
Object.defineProperty(Vue.prototype, key, {
|
Object.defineProperty(Vue.prototype, key, {
|
||||||
get () {
|
get () {
|
||||||
@ -148,16 +153,15 @@ async function createApp (ssrContext) {
|
|||||||
}
|
}
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
|
// Plugin execution
|
||||||
<% plugins.filter(p => p.ssr).forEach(plugin => { %>
|
<% plugins.filter(p => p.ssr).forEach(plugin => { %>
|
||||||
if (typeof <%= plugin.name %> === 'function') await <%= plugin.name %>(ctx, inject)<% }) %>
|
if (typeof <%= plugin.name %> === 'function') await <%= plugin.name %>(app.context, inject)<% }) %>
|
||||||
<% if (plugins.filter(p => !p.ssr).length) { %>
|
<% if (plugins.filter(p => !p.ssr).length) { %>
|
||||||
if (process.browser) { <% plugins.filter(p => !p.ssr).forEach(plugin => { %>
|
if (process.browser) { <% plugins.filter(p => !p.ssr).forEach(plugin => { %>
|
||||||
if (typeof <%= plugin.name %> === 'function') await <%= plugin.name %>(ctx, inject)<% }) %>
|
if (typeof <%= plugin.name %> === 'function') await <%= plugin.name %>(app.context, inject)<% }) %>
|
||||||
}<% } %>
|
}<% } %>
|
||||||
|
|
||||||
// Inject context
|
// If server-side, wait for async component to be resolved first
|
||||||
inject('ctx', ctx)
|
|
||||||
|
|
||||||
if (process.server && ssrContext && ssrContext.url) {
|
if (process.server && ssrContext && ssrContext.url) {
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
router.push(ssrContext.url, resolve, reject)
|
router.push(ssrContext.url, resolve, reject)
|
||||||
|
@ -13,11 +13,11 @@ const isDev = <%= isDev %>
|
|||||||
|
|
||||||
const noopApp = () => new Vue({ render: (h) => h('div') })
|
const noopApp = () => new Vue({ render: (h) => h('div') })
|
||||||
|
|
||||||
const createNext = context => opts => {
|
const createNext = ssrContext => opts => {
|
||||||
context.redirected = opts
|
ssrContext.redirected = opts
|
||||||
// If nuxt generate
|
// If nuxt generate
|
||||||
if (!context.res) {
|
if (!ssrContext.res) {
|
||||||
context.nuxt.serverRendered = false
|
ssrContext.nuxt.serverRendered = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
opts.query = stringify(opts.query)
|
opts.query = stringify(opts.query)
|
||||||
@ -26,14 +26,14 @@ const createNext = context => opts => {
|
|||||||
opts.path = urlJoin('<%= router.base %>', opts.path)
|
opts.path = urlJoin('<%= router.base %>', opts.path)
|
||||||
}
|
}
|
||||||
// Avoid loop redirect
|
// Avoid loop redirect
|
||||||
if (opts.path === context.url) {
|
if (opts.path === ssrContext.url) {
|
||||||
context.redirected = false
|
ssrContext.redirected = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
context.res.writeHead(opts.status, {
|
ssrContext.res.writeHead(opts.status, {
|
||||||
'Location': opts.path
|
'Location': opts.path
|
||||||
})
|
})
|
||||||
context.res.end()
|
ssrContext.res.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This exported function will be called by `bundleRenderer`.
|
// This exported function will be called by `bundleRenderer`.
|
||||||
@ -41,137 +41,142 @@ const createNext = context => opts => {
|
|||||||
// state of our application before actually rendering it.
|
// state of our application before actually rendering it.
|
||||||
// Since data fetching is async, this function is expected to
|
// Since data fetching is async, this function is expected to
|
||||||
// return a Promise that resolves to the app instance.
|
// return a Promise that resolves to the app instance.
|
||||||
export default async context => {
|
export default async ssrContext => {
|
||||||
// Create context.next for simulate next() of beforeEach() when wanted to redirect
|
// Create ssrContext.next for simulate next() of beforeEach() when wanted to redirect
|
||||||
context.redirected = false
|
ssrContext.redirected = false
|
||||||
context.next = createNext(context)
|
ssrContext.next = createNext(ssrContext)
|
||||||
context.beforeRenderFns = []
|
// Used for beforeNuxtRender({ Components, nuxtState })
|
||||||
|
ssrContext.beforeRenderFns = []
|
||||||
|
|
||||||
const { app, router<%= (store ? ', store' : '') %> } = await createApp(context)
|
// Create the app definition and the instance (created for each request)
|
||||||
|
const { app, router<%= (store ? ', store' : '') %> } = await createApp(ssrContext)
|
||||||
const _app = new Vue(app)
|
const _app = new Vue(app)
|
||||||
|
|
||||||
<% if (store) { %>
|
// Nuxt object (window.__NUXT__)
|
||||||
// Add store to the context
|
ssrContext.nuxt = { layout: 'default', data: [], error: null<%= (store ? ', state: null' : '') %>, serverRendered: true }
|
||||||
context.store = store
|
// Add meta infos (used in renderer.js)
|
||||||
<% } %>
|
ssrContext.meta = _app.$meta()
|
||||||
|
// Keep asyncData for each matched component in ssrContext (used in app/utils.js via this.$ssrContext)
|
||||||
|
ssrContext.asyncData = {}
|
||||||
|
|
||||||
// Add route to the context
|
const beforeRender = async () => {
|
||||||
context.route = router.currentRoute
|
// Call beforeNuxtRender() methods
|
||||||
|
await Promise.all(ssrContext.beforeRenderFns.map((fn) => promisify(fn, { Components, nuxtState: ssrContext.nuxt })))
|
||||||
// Nuxt object
|
<% if (store) { %>
|
||||||
context.nuxt = { layout: 'default', data: [], error: null<%= (store ? ', state: null' : '') %>, serverRendered: true }
|
// Add the state from the vuex store
|
||||||
|
ssrContext.nuxt.state = store.state
|
||||||
// Add meta infos
|
<% } %>
|
||||||
context.meta = _app.$meta()
|
}
|
||||||
|
const renderErrorPage = async () => {
|
||||||
// Error function
|
// Load layout for error page
|
||||||
context.error = _app.$options._nuxt.error.bind(_app)
|
let errLayout = (typeof NuxtError.layout === 'function' ? NuxtError.layout(app.context) : NuxtError.layout)
|
||||||
|
ssrContext.nuxt.layout = errLayout || ''
|
||||||
// Keep asyncData for each matched component in context
|
await _app.loadLayout(errLayout)
|
||||||
context.asyncData = {}
|
_app.setLayout(errLayout)
|
||||||
|
await beforeRender()
|
||||||
// Create shared ctx
|
return _app
|
||||||
const ctx = getContext(context, app)
|
}
|
||||||
|
const render404Page = async () => {
|
||||||
|
app.context.error({ statusCode: 404, message: '<%= messages.error_404 %>' })
|
||||||
|
return await renderErrorPage()
|
||||||
|
}
|
||||||
|
|
||||||
<% if (isDev) { %>const s = isDev && Date.now()<% } %>
|
<% if (isDev) { %>const s = isDev && Date.now()<% } %>
|
||||||
|
|
||||||
// Resolve components
|
// Components are already resolved by setContext -> getRouteData (app/utils.js)
|
||||||
let Components = []
|
const Components = getMatchedComponents(router.match(ssrContext.url))
|
||||||
try {
|
|
||||||
Components = await Promise.all(getMatchedComponents(router.match(context.url)).map(Component => {
|
|
||||||
if (typeof Component !== 'function' || Component.cid) {
|
|
||||||
return sanitizeComponent(Component)
|
|
||||||
}
|
|
||||||
return Component().then(Component => sanitizeComponent(Component))
|
|
||||||
}))
|
|
||||||
} catch (err) {
|
|
||||||
// Throw back error to renderRoute()
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
|
|
||||||
<% if (store) { %>
|
<% if (store) { %>
|
||||||
// Dispatch store nuxtServerInit
|
/*
|
||||||
|
** Dispatch store nuxtServerInit
|
||||||
|
*/
|
||||||
if (store._actions && store._actions.nuxtServerInit) {
|
if (store._actions && store._actions.nuxtServerInit) {
|
||||||
await store.dispatch('nuxtServerInit', ctx)
|
await store.dispatch('nuxtServerInit', app.context)
|
||||||
}
|
}
|
||||||
// ...If there is a redirect
|
// ...If there is a redirect or an error, stop the process
|
||||||
if (context.redirected) return noopApp()
|
if (ssrContext.redirected) return noopApp()
|
||||||
|
if (ssrContext.nuxt.error) return await renderErrorPage()
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
// Call global middleware (nuxt.config.js)
|
/*
|
||||||
|
** Call global middleware (nuxt.config.js)
|
||||||
|
*/
|
||||||
let midd = <%= serialize(router.middleware, { isJSON: true }) %>
|
let midd = <%= serialize(router.middleware, { isJSON: true }) %>
|
||||||
midd = midd.map((name) => {
|
midd = midd.map((name) => {
|
||||||
if (typeof middleware[name] !== 'function') {
|
if (typeof middleware[name] !== 'function') {
|
||||||
context.nuxt.error = context.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
ssrContext.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
||||||
}
|
}
|
||||||
return middleware[name]
|
return middleware[name]
|
||||||
})
|
})
|
||||||
if (!context.nuxt.error) {
|
await middlewareSeries(midd, app.context)
|
||||||
await middlewareSeries(midd, ctx)
|
// ...If there is a redirect or an error, stop the process
|
||||||
}
|
if (ssrContext.redirected) return noopApp()
|
||||||
// ...If there is a redirect
|
if (ssrContext.nuxt.error) return await renderErrorPage()
|
||||||
if (context.redirected) return noopApp()
|
|
||||||
|
|
||||||
// Set layout
|
/*
|
||||||
|
** Set layout
|
||||||
|
*/
|
||||||
let layout = Components.length ? Components[0].options.layout : NuxtError.layout
|
let layout = Components.length ? Components[0].options.layout : NuxtError.layout
|
||||||
if (typeof layout === 'function') layout = layout(ctx)
|
if (typeof layout === 'function') layout = layout(app.context)
|
||||||
await _app.loadLayout(layout)
|
await _app.loadLayout(layout)
|
||||||
layout = _app.setLayout(layout)
|
layout = _app.setLayout(layout)
|
||||||
// ...Set layout to __NUXT__
|
// ...Set layout to __NUXT__
|
||||||
context.nuxt.layout = _app.layoutName
|
ssrContext.nuxt.layout = _app.layoutName
|
||||||
|
|
||||||
// Call middleware (layout + pages)
|
/*
|
||||||
if (!context.nuxt.error) {
|
** Call middleware (layout + pages)
|
||||||
midd = []
|
*/
|
||||||
if (layout.middleware) midd = midd.concat(layout.middleware)
|
midd = []
|
||||||
Components.forEach((Component) => {
|
if (layout.middleware) midd = midd.concat(layout.middleware)
|
||||||
if (Component.options.middleware) {
|
Components.forEach((Component) => {
|
||||||
midd = midd.concat(Component.options.middleware)
|
if (Component.options.middleware) {
|
||||||
}
|
midd = midd.concat(Component.options.middleware)
|
||||||
})
|
}
|
||||||
midd = midd.map((name) => {
|
})
|
||||||
if (typeof middleware[name] !== 'function') {
|
midd = midd.map((name) => {
|
||||||
context.nuxt.error = context.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
if (typeof middleware[name] !== 'function') {
|
||||||
}
|
app.context.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
||||||
return middleware[name]
|
}
|
||||||
})
|
return middleware[name]
|
||||||
|
})
|
||||||
|
await middlewareSeries(midd, app.context)
|
||||||
|
// ...If there is a redirect or an error, stop the process
|
||||||
|
if (ssrContext.redirected) return noopApp()
|
||||||
|
if (ssrContext.nuxt.error) return await renderErrorPage()
|
||||||
|
|
||||||
await middlewareSeries(midd, ctx)
|
/*
|
||||||
|
** Call .validate()
|
||||||
// If there is a redirect
|
*/
|
||||||
if (context.redirected) return noopApp()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call .validate()
|
|
||||||
let isValid = true
|
let isValid = true
|
||||||
Components.forEach((Component) => {
|
Components.forEach((Component) => {
|
||||||
if (!isValid) return
|
if (!isValid) return
|
||||||
if (typeof Component.options.validate !== 'function') return
|
if (typeof Component.options.validate !== 'function') return
|
||||||
isValid = Component.options.validate({
|
isValid = Component.options.validate({
|
||||||
params: context.route.params || {},
|
params: app.context.route.params || {},
|
||||||
query: context.route.query || {},
|
query: app.context.route.query || {},
|
||||||
<%= (store ? 'store: ctx.store' : '') %>
|
<%= (store ? 'store' : '') %>
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
// ...If .validate() returned false
|
// ...If .validate() returned false
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
// Don't server-render the page in generate mode
|
// Don't server-render the page in generate mode
|
||||||
if (context._generate) {
|
if (ssrContext._generate) ssrContext.nuxt.serverRendered = false
|
||||||
context.nuxt.serverRendered = false
|
// Render a 404 error page
|
||||||
}
|
return render404Page()
|
||||||
// Call the 404 error by making the Components array empty
|
|
||||||
Components = []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If no Components found, returns 404
|
||||||
|
if (!Components.length) return render404Page()
|
||||||
|
|
||||||
// Call asyncData & fetch hooks on components matched by the route.
|
// Call asyncData & fetch hooks on components matched by the route.
|
||||||
let asyncDatas = await Promise.all(Components.map(Component => {
|
let asyncDatas = await Promise.all(Components.map(Component => {
|
||||||
let promises = []
|
let promises = []
|
||||||
|
|
||||||
// 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, ctx)
|
let promise = promisify(Component.options.asyncData, app.context)
|
||||||
promise.then(asyncDataResult => {
|
promise.then(asyncDataResult => {
|
||||||
context.asyncData[Component.cid] = asyncDataResult
|
ssrContext.asyncData[Component.cid] = asyncDataResult
|
||||||
applyAsyncData(Component)
|
applyAsyncData(Component)
|
||||||
return asyncDataResult
|
return asyncDataResult
|
||||||
})
|
})
|
||||||
@ -182,7 +187,7 @@ export default async context => {
|
|||||||
|
|
||||||
// Call fetch(context)
|
// Call fetch(context)
|
||||||
if (Component.options.fetch) {
|
if (Component.options.fetch) {
|
||||||
promises.push(Component.options.fetch(ctx))
|
promises.push(Component.options.fetch(app.context))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
promises.push(null)
|
promises.push(null)
|
||||||
@ -191,38 +196,17 @@ export default async context => {
|
|||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// If no Components found, returns 404
|
<% if (isDev) { %>if (asyncDatas.length) debug('Data fetching ' + ssrContext.url + ': ' + (Date.now() - s) + 'ms')<% } %>
|
||||||
if (!Components.length) {
|
|
||||||
context.nuxt.error = context.error({ statusCode: 404, message: '<%= messages.error_404 %>' })
|
|
||||||
}
|
|
||||||
|
|
||||||
<% if (isDev) { %>if (asyncDatas.length) debug('Data fetching ' + context.url + ': ' + (Date.now() - s) + 'ms')<% } %>
|
|
||||||
|
|
||||||
// datas are the first row of each
|
// datas are the first row of each
|
||||||
context.nuxt.data = asyncDatas.map(r => r[0] || {})
|
ssrContext.nuxt.data = asyncDatas.map(r => r[0] || {})
|
||||||
|
|
||||||
// If an error occured in the execution
|
// ...If there is a redirect or an error, stop the process
|
||||||
if (_app.$options._nuxt.err) {
|
if (ssrContext.redirected) return noopApp()
|
||||||
context.nuxt.error = _app.$options._nuxt.err
|
if (ssrContext.nuxt.error) return await renderErrorPage()
|
||||||
}
|
|
||||||
|
|
||||||
<% if (store) { %>
|
|
||||||
// Add the state from the vuex store
|
|
||||||
context.nuxt.state = store.state
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
await Promise.all(context.beforeRenderFns.map((fn) => promisify(fn, { Components, nuxtState: context.nuxt })))
|
|
||||||
|
|
||||||
// If no error, return main app
|
|
||||||
if (!context.nuxt.error) {
|
|
||||||
return _app
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load layout for error page
|
|
||||||
layout = (typeof NuxtError.layout === 'function' ? NuxtError.layout(ctx) : NuxtError.layout)
|
|
||||||
context.nuxt.layout = layout || ''
|
|
||||||
await _app.loadLayout(layout)
|
|
||||||
_app.setLayout(layout)
|
|
||||||
|
|
||||||
|
// Call beforeNuxtRender methods & add store state
|
||||||
|
await beforeRender()
|
||||||
|
|
||||||
return _app
|
return _app
|
||||||
}
|
}
|
||||||
|
150
lib/app/utils.js
150
lib/app/utils.js
@ -11,7 +11,7 @@ if (process.browser) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyAsyncData (Component, asyncData) {
|
export function applyAsyncData(Component, asyncData) {
|
||||||
const ComponentData = Component.options.data || noopData
|
const ComponentData = Component.options.data || noopData
|
||||||
// Prevent calling this method for each request on SSR context
|
// Prevent calling this method for each request on SSR context
|
||||||
if (!asyncData && Component.options.hasAsyncData) {
|
if (!asyncData && Component.options.hasAsyncData) {
|
||||||
@ -30,7 +30,11 @@ export function applyAsyncData (Component, asyncData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sanitizeComponent (Component) {
|
export function sanitizeComponent(Component) {
|
||||||
|
// If Component already sanitized
|
||||||
|
if (Component.options && Component._Ctor === Component) {
|
||||||
|
return Component
|
||||||
|
}
|
||||||
if (!Component.options) {
|
if (!Component.options) {
|
||||||
Component = Vue.extend(Component) // fix issue #6
|
Component = Vue.extend(Component) // fix issue #6
|
||||||
Component._Ctor = Component
|
Component._Ctor = Component
|
||||||
@ -45,7 +49,7 @@ export function sanitizeComponent (Component) {
|
|||||||
return Component
|
return Component
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMatchedComponents (route) {
|
export function getMatchedComponents(route) {
|
||||||
return [].concat.apply([], route.matched.map(function (m) {
|
return [].concat.apply([], route.matched.map(function (m) {
|
||||||
return Object.keys(m.components).map(function (key) {
|
return Object.keys(m.components).map(function (key) {
|
||||||
return m.components[key]
|
return m.components[key]
|
||||||
@ -53,7 +57,7 @@ export function getMatchedComponents (route) {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMatchedComponentsInstances (route) {
|
export function getMatchedComponentsInstances(route) {
|
||||||
return [].concat.apply([], route.matched.map(function (m) {
|
return [].concat.apply([], route.matched.map(function (m) {
|
||||||
return Object.keys(m.instances).map(function (key) {
|
return Object.keys(m.instances).map(function (key) {
|
||||||
return m.instances[key]
|
return m.instances[key]
|
||||||
@ -61,7 +65,7 @@ export function getMatchedComponentsInstances (route) {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function flatMapComponents (route, fn) {
|
export function flatMapComponents(route, fn) {
|
||||||
return Array.prototype.concat.apply([], route.matched.map(function (m, index) {
|
return Array.prototype.concat.apply([], route.matched.map(function (m, index) {
|
||||||
return Object.keys(m.components).map(function (key) {
|
return Object.keys(m.components).map(function (key) {
|
||||||
return fn(m.components[key], m.instances[key], m, key, index)
|
return fn(m.components[key], m.instances[key], m, key, index)
|
||||||
@ -69,53 +73,79 @@ export function flatMapComponents (route, fn) {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getContext (context, app) {
|
export async function resolveRouteComponents(route) {
|
||||||
let ctx = {
|
await Promise.all(
|
||||||
isServer: !!context.isServer,
|
flatMapComponents(route, async (Component, _, match, key) => {
|
||||||
isClient: !!context.isClient,
|
// If component is a function, resolve it
|
||||||
isStatic: process.static,
|
if (typeof Component === 'function' && !Component.options) {
|
||||||
isDev: <%= isDev %>,
|
Component = await Component()
|
||||||
isHMR: context.isHMR || false,
|
}
|
||||||
app: app,
|
return match.components[key] = sanitizeComponent(Component)
|
||||||
<%= (store ? 'store: context.store,' : '') %>
|
|
||||||
route: (context.to ? context.to : context.route),
|
|
||||||
payload: context.payload,
|
|
||||||
error: context.error,
|
|
||||||
base: '<%= router.base %>',
|
|
||||||
env: <%= JSON.stringify(env) %>
|
|
||||||
}
|
|
||||||
const next = context.next
|
|
||||||
ctx.params = ctx.route.params || {}
|
|
||||||
ctx.query = ctx.route.query || {}
|
|
||||||
ctx.redirect = function (status, path, query) {
|
|
||||||
if (!status) return
|
|
||||||
ctx._redirected = true // Used in middleware
|
|
||||||
// if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
|
|
||||||
if (typeof status === 'string' && (typeof path === 'undefined' || typeof path === 'object')) {
|
|
||||||
query = path || {}
|
|
||||||
path = status
|
|
||||||
status = 302
|
|
||||||
}
|
|
||||||
next({
|
|
||||||
path: path,
|
|
||||||
query: query,
|
|
||||||
status: status
|
|
||||||
})
|
})
|
||||||
}
|
)
|
||||||
if (context.req) ctx.req = context.req
|
|
||||||
if (context.res) ctx.res = context.res
|
|
||||||
if (context.from) ctx.from = context.from
|
|
||||||
if (ctx.isServer && context.beforeRenderFns) {
|
|
||||||
ctx.beforeNuxtRender = (fn) => context.beforeRenderFns.push(fn)
|
|
||||||
}
|
|
||||||
if (ctx.isClient && window.__NUXT__) {
|
|
||||||
ctx.nuxtState = window.__NUXT__
|
|
||||||
}
|
|
||||||
return ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function middlewareSeries (promises, context) {
|
async function getRouteData(route) {
|
||||||
if (!promises.length || context._redirected) {
|
// Make sure the components are resolved (code-splitting)
|
||||||
|
await resolveRouteComponents(route)
|
||||||
|
// Send back a copy of route with meta based on Component definition
|
||||||
|
return {
|
||||||
|
...route,
|
||||||
|
meta: getMatchedComponents(route).map((Component) => {
|
||||||
|
return Component.options.meta || {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setContext(app, context) {
|
||||||
|
const route = (context.to ? context.to : context.route)
|
||||||
|
// If context not defined, create it
|
||||||
|
if (!app.context) {
|
||||||
|
app.context = {
|
||||||
|
isServer: !!context.isServer,
|
||||||
|
isClient: !!context.isClient,
|
||||||
|
isStatic: process.static,
|
||||||
|
isDev: <%= isDev %>,
|
||||||
|
isHMR: false,
|
||||||
|
app,
|
||||||
|
<%= (store ? 'store: app.store,' : '') %>
|
||||||
|
payload: context.payload,
|
||||||
|
error: context.error,
|
||||||
|
base: '<%= router.base %>',
|
||||||
|
env: <%= JSON.stringify(env) %>
|
||||||
|
}
|
||||||
|
// Only set once
|
||||||
|
if (context.req) app.context.req = context.req
|
||||||
|
if (context.res) app.context.res = context.res
|
||||||
|
app.context.redirect = function (status, path, query) {
|
||||||
|
if (!status) return
|
||||||
|
ctx._redirected = true // Used in middleware
|
||||||
|
// if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
|
||||||
|
if (typeof status === 'string' && (typeof path === 'undefined' || typeof path === 'object')) {
|
||||||
|
query = path || {}
|
||||||
|
path = status
|
||||||
|
status = 302
|
||||||
|
}
|
||||||
|
app.context.next({
|
||||||
|
path: path,
|
||||||
|
query: query,
|
||||||
|
status: status
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (app.context.isServer) app.context.beforeNuxtRender = (fn) => context.beforeRenderFns.push(fn)
|
||||||
|
if (app.context.isClient) app.context.nuxtState = window.__NUXT__
|
||||||
|
}
|
||||||
|
// Dynamic keys
|
||||||
|
app.context.next = context.next
|
||||||
|
app.context.isHMR = !!context.isHMR
|
||||||
|
if (context.route) app.context.route = await getRouteData(context.route)
|
||||||
|
app.context.params = app.context.route.params || {}
|
||||||
|
app.context.query = app.context.route.query || {}
|
||||||
|
if (context.from) app.context.from = await getRouteData(context.from)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function middlewareSeries(promises, appContext) {
|
||||||
|
if (!promises.length || appContext._redirected || appContext.app.nuxt.err) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
return promisify(promises[0], context)
|
return promisify(promises[0], context)
|
||||||
@ -124,7 +154,7 @@ export function middlewareSeries (promises, context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function promisify (fn, context) {
|
export function promisify(fn, context) {
|
||||||
let promise
|
let promise
|
||||||
if (fn.length === 2) {
|
if (fn.length === 2) {
|
||||||
// fn(context, callback)
|
// fn(context, callback)
|
||||||
@ -147,7 +177,7 @@ export function promisify (fn, context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Imported from vue-router
|
// Imported from vue-router
|
||||||
export function getLocation (base, mode) {
|
export function getLocation(base, mode) {
|
||||||
var path = window.location.pathname
|
var path = window.location.pathname
|
||||||
if (mode === 'hash') {
|
if (mode === 'hash') {
|
||||||
return window.location.hash.replace(/^#\//, '')
|
return window.location.hash.replace(/^#\//, '')
|
||||||
@ -158,7 +188,7 @@ export function getLocation (base, mode) {
|
|||||||
return (path || '/') + window.location.search + window.location.hash
|
return (path || '/') + window.location.search + window.location.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
export function urlJoin () {
|
export function urlJoin() {
|
||||||
return [].slice.call(arguments).join('/').replace(/\/+/g, '/')
|
return [].slice.call(arguments).join('/').replace(/\/+/g, '/')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,7 +201,7 @@ export function urlJoin () {
|
|||||||
* @param {Object=} options
|
* @param {Object=} options
|
||||||
* @return {!function(Object=, Object=)}
|
* @return {!function(Object=, Object=)}
|
||||||
*/
|
*/
|
||||||
export function compile (str, options) {
|
export function compile(str, options) {
|
||||||
return tokensToFunction(parse(str, options))
|
return tokensToFunction(parse(str, options))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,7 +230,7 @@ const PATH_REGEXP = new RegExp([
|
|||||||
* @param {Object=} options
|
* @param {Object=} options
|
||||||
* @return {!Array}
|
* @return {!Array}
|
||||||
*/
|
*/
|
||||||
function parse (str, options) {
|
function parse(str, options) {
|
||||||
var tokens = []
|
var tokens = []
|
||||||
var key = 0
|
var key = 0
|
||||||
var index = 0
|
var index = 0
|
||||||
@ -272,7 +302,7 @@ function parse (str, options) {
|
|||||||
* @param {string}
|
* @param {string}
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function encodeURIComponentPretty (str) {
|
function encodeURIComponentPretty(str) {
|
||||||
return encodeURI(str).replace(/[\/?#]/g, function (c) {
|
return encodeURI(str).replace(/[\/?#]/g, function (c) {
|
||||||
return '%' + c.charCodeAt(0).toString(16).toUpperCase()
|
return '%' + c.charCodeAt(0).toString(16).toUpperCase()
|
||||||
})
|
})
|
||||||
@ -284,7 +314,7 @@ function encodeURIComponentPretty (str) {
|
|||||||
* @param {string}
|
* @param {string}
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function encodeAsterisk (str) {
|
function encodeAsterisk(str) {
|
||||||
return encodeURI(str).replace(/[?#]/g, function (c) {
|
return encodeURI(str).replace(/[?#]/g, function (c) {
|
||||||
return '%' + c.charCodeAt(0).toString(16).toUpperCase()
|
return '%' + c.charCodeAt(0).toString(16).toUpperCase()
|
||||||
})
|
})
|
||||||
@ -293,7 +323,7 @@ function encodeAsterisk (str) {
|
|||||||
/**
|
/**
|
||||||
* Expose a method for transforming tokens into the path function.
|
* Expose a method for transforming tokens into the path function.
|
||||||
*/
|
*/
|
||||||
function tokensToFunction (tokens) {
|
function tokensToFunction(tokens) {
|
||||||
// Compile all the tokens into regexps.
|
// Compile all the tokens into regexps.
|
||||||
var matches = new Array(tokens.length)
|
var matches = new Array(tokens.length)
|
||||||
|
|
||||||
@ -304,7 +334,7 @@ function tokensToFunction (tokens) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return function (obj, opts) {
|
return function(obj, opts) {
|
||||||
var path = ''
|
var path = ''
|
||||||
var data = obj || {}
|
var data = obj || {}
|
||||||
var options = opts || {}
|
var options = opts || {}
|
||||||
@ -380,7 +410,7 @@ function tokensToFunction (tokens) {
|
|||||||
* @param {string} str
|
* @param {string} str
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function escapeString (str) {
|
function escapeString(str) {
|
||||||
return str.replace(/([.+*?=^!:${}()[\]|\/\\])/g, '\\$1')
|
return str.replace(/([.+*?=^!:${}()[\]|\/\\])/g, '\\$1')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,6 +420,6 @@ function escapeString (str) {
|
|||||||
* @param {string} group
|
* @param {string} group
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function escapeGroup (group) {
|
function escapeGroup(group) {
|
||||||
return group.replace(/([=!:$\/()])/g, '\\$1')
|
return group.replace(/([=!:$\/()])/g, '\\$1')
|
||||||
}
|
}
|
||||||
|
@ -184,7 +184,7 @@ export default class Builder extends Tapable {
|
|||||||
debug('Generating files...')
|
debug('Generating files...')
|
||||||
// -- Templates --
|
// -- Templates --
|
||||||
let templatesFiles = [
|
let templatesFiles = [
|
||||||
'App.vue',
|
'App.js',
|
||||||
'client.js',
|
'client.js',
|
||||||
'index.js',
|
'index.js',
|
||||||
'middleware.js',
|
'middleware.js',
|
||||||
@ -196,7 +196,7 @@ export default class Builder extends Tapable {
|
|||||||
'components/nuxt-loading.vue',
|
'components/nuxt-loading.vue',
|
||||||
'components/nuxt-child.js',
|
'components/nuxt-child.js',
|
||||||
'components/nuxt-link.js',
|
'components/nuxt-link.js',
|
||||||
'components/nuxt.vue',
|
'components/nuxt.js',
|
||||||
'components/no-ssr.js',
|
'components/no-ssr.js',
|
||||||
'views/app.template.html',
|
'views/app.template.html',
|
||||||
'views/error.html'
|
'views/error.html'
|
||||||
@ -215,7 +215,7 @@ export default class Builder extends Tapable {
|
|||||||
store: this.options.store,
|
store: this.options.store,
|
||||||
css: this.options.css,
|
css: this.options.css,
|
||||||
plugins: this.plugins,
|
plugins: this.plugins,
|
||||||
appPath: './App.vue',
|
appPath: './App.js',
|
||||||
layouts: Object.assign({}, this.options.layouts),
|
layouts: Object.assign({}, this.options.layouts),
|
||||||
loading: typeof this.options.loading === 'string' ? this.relativeToBuild(this.options.srcDir, this.options.loading) : this.options.loading,
|
loading: typeof this.options.loading === 'string' ? this.relativeToBuild(this.options.srcDir, this.options.loading) : this.options.loading,
|
||||||
transition: this.options.transition,
|
transition: this.options.transition,
|
||||||
|
Loading…
Reference in New Issue
Block a user