22 changed files with 3211 additions and 0 deletions
-
1.gitignore
-
199.nuxt/App.js
-
778.nuxt/client.js
-
142.nuxt/components/nuxt-build-indicator.vue
-
122.nuxt/components/nuxt-child.js
-
97.nuxt/components/nuxt-error.vue
-
98.nuxt/components/nuxt-link.client.js
-
16.nuxt/components/nuxt-link.server.js
-
177.nuxt/components/nuxt-loading.vue
-
101.nuxt/components/nuxt.js
-
1.nuxt/empty.js
-
185.nuxt/index.js
-
109.nuxt/loading.html
-
3.nuxt/middleware.js
-
87.nuxt/mixins/fetch.client.js
-
48.nuxt/mixins/fetch.server.js
-
60.nuxt/router.js
-
74.nuxt/router.scrollBehavior.js
-
265.nuxt/server.js
-
616.nuxt/utils.js
-
9.nuxt/views/app.template.html
-
23.nuxt/views/error.html
@ -0,0 +1 @@ |
|||||
|
node_modules |
@ -0,0 +1,199 @@ |
|||||
|
import Vue from 'vue' |
||||
|
|
||||
|
import { |
||||
|
getMatchedComponentsInstances, |
||||
|
getChildrenComponentInstancesUsingFetch, |
||||
|
promisify, |
||||
|
globalHandleError, |
||||
|
sanitizeComponent |
||||
|
} from './utils' |
||||
|
|
||||
|
import NuxtLoading from './components/nuxt-loading.vue' |
||||
|
import NuxtBuildIndicator from './components/nuxt-build-indicator' |
||||
|
|
||||
|
import _6f6c098b from '..\\layouts\\default.vue' |
||||
|
import _2d297eb3 from '..\\layouts\\sign.vue' |
||||
|
|
||||
|
const layouts = { "_default": sanitizeComponent(_6f6c098b),"_sign": sanitizeComponent(_2d297eb3) } |
||||
|
|
||||
|
export default { |
||||
|
render (h, props) { |
||||
|
const loadingEl = h('NuxtLoading', { ref: 'loading' }) |
||||
|
|
||||
|
const layoutEl = h(this.layout || 'nuxt') |
||||
|
const templateEl = h('div', { |
||||
|
domProps: { |
||||
|
id: '__layout' |
||||
|
}, |
||||
|
key: this.layoutName |
||||
|
}, [layoutEl]) |
||||
|
|
||||
|
const transitionEl = h('transition', { |
||||
|
props: { |
||||
|
name: 'layout', |
||||
|
mode: 'out-in' |
||||
|
}, |
||||
|
on: { |
||||
|
beforeEnter (el) { |
||||
|
// Ensure to trigger scroll event after calling scrollBehavior
|
||||
|
window.$nuxt.$nextTick(() => { |
||||
|
window.$nuxt.$emit('triggerScroll') |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
}, [templateEl]) |
||||
|
|
||||
|
return h('div', { |
||||
|
domProps: { |
||||
|
id: '__nuxt' |
||||
|
} |
||||
|
}, [ |
||||
|
loadingEl, |
||||
|
h(NuxtBuildIndicator), |
||||
|
transitionEl |
||||
|
]) |
||||
|
}, |
||||
|
|
||||
|
data: () => ({ |
||||
|
isOnline: true, |
||||
|
|
||||
|
layout: null, |
||||
|
layoutName: '', |
||||
|
|
||||
|
nbFetching: 0 |
||||
|
}), |
||||
|
|
||||
|
beforeCreate () { |
||||
|
Vue.util.defineReactive(this, 'nuxt', this.$options.nuxt) |
||||
|
}, |
||||
|
created () { |
||||
|
// Add this.$nuxt in child instances
|
||||
|
Vue.prototype.$nuxt = this |
||||
|
// add to window so we can listen when ready
|
||||
|
if (process.client) { |
||||
|
window.$nuxt = this |
||||
|
|
||||
|
this.refreshOnlineStatus() |
||||
|
// Setup the listeners
|
||||
|
window.addEventListener('online', this.refreshOnlineStatus) |
||||
|
window.addEventListener('offline', this.refreshOnlineStatus) |
||||
|
} |
||||
|
// Add $nuxt.error()
|
||||
|
this.error = this.nuxt.error |
||||
|
// Add $nuxt.context
|
||||
|
this.context = this.$options.context |
||||
|
}, |
||||
|
|
||||
|
mounted () { |
||||
|
this.$loading = this.$refs.loading |
||||
|
}, |
||||
|
watch: { |
||||
|
'nuxt.err': 'errorChanged' |
||||
|
}, |
||||
|
|
||||
|
computed: { |
||||
|
isOffline () { |
||||
|
return !this.isOnline |
||||
|
}, |
||||
|
|
||||
|
isFetching() { |
||||
|
return this.nbFetching > 0 |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
refreshOnlineStatus () { |
||||
|
if (process.client) { |
||||
|
if (typeof window.navigator.onLine === 'undefined') { |
||||
|
// If the browser doesn't support connection status reports
|
||||
|
// assume that we are online because most apps' only react
|
||||
|
// when they now that the connection has been interrupted
|
||||
|
this.isOnline = true |
||||
|
} else { |
||||
|
this.isOnline = window.navigator.onLine |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
async refresh () { |
||||
|
const pages = getMatchedComponentsInstances(this.$route) |
||||
|
|
||||
|
if (!pages.length) { |
||||
|
return |
||||
|
} |
||||
|
this.$loading.start() |
||||
|
|
||||
|
const promises = pages.map((page) => { |
||||
|
const p = [] |
||||
|
|
||||
|
// Old fetch
|
||||
|
if (page.$options.fetch && page.$options.fetch.length) { |
||||
|
p.push(promisify(page.$options.fetch, this.context)) |
||||
|
} |
||||
|
if (page.$fetch) { |
||||
|
p.push(page.$fetch()) |
||||
|
} else { |
||||
|
// Get all component instance to call $fetch
|
||||
|
for (const component of getChildrenComponentInstancesUsingFetch(page.$vnode.componentInstance)) { |
||||
|
p.push(component.$fetch()) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (page.$options.asyncData) { |
||||
|
p.push( |
||||
|
promisify(page.$options.asyncData, this.context) |
||||
|
.then((newData) => { |
||||
|
for (const key in newData) { |
||||
|
Vue.set(page.$data, key, newData[key]) |
||||
|
} |
||||
|
}) |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
return Promise.all(p) |
||||
|
}) |
||||
|
try { |
||||
|
await Promise.all(promises) |
||||
|
} catch (error) { |
||||
|
this.$loading.fail(error) |
||||
|
globalHandleError(error) |
||||
|
this.error(error) |
||||
|
} |
||||
|
this.$loading.finish() |
||||
|
}, |
||||
|
|
||||
|
errorChanged () { |
||||
|
if (this.nuxt.err && this.$loading) { |
||||
|
if (this.$loading.fail) { |
||||
|
this.$loading.fail(this.nuxt.err) |
||||
|
} |
||||
|
if (this.$loading.finish) { |
||||
|
this.$loading.finish() |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
setLayout (layout) { |
||||
|
if(layout && typeof layout !== 'string') { |
||||
|
throw new Error('[nuxt] Avoid using non-string value as layout property.') |
||||
|
} |
||||
|
|
||||
|
if (!layout || !layouts['_' + layout]) { |
||||
|
layout = 'default' |
||||
|
} |
||||
|
this.layoutName = layout |
||||
|
this.layout = layouts['_' + layout] |
||||
|
return this.layout |
||||
|
}, |
||||
|
loadLayout (layout) { |
||||
|
if (!layout || !layouts['_' + layout]) { |
||||
|
layout = 'default' |
||||
|
} |
||||
|
return Promise.resolve(layouts['_' + layout]) |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
components: { |
||||
|
NuxtLoading |
||||
|
} |
||||
|
} |
@ -0,0 +1,778 @@ |
|||||
|
import Vue from 'vue' |
||||
|
import fetch from 'unfetch' |
||||
|
import middleware from './middleware.js' |
||||
|
import { |
||||
|
applyAsyncData, |
||||
|
promisify, |
||||
|
middlewareSeries, |
||||
|
sanitizeComponent, |
||||
|
resolveRouteComponents, |
||||
|
getMatchedComponents, |
||||
|
getMatchedComponentsInstances, |
||||
|
flatMapComponents, |
||||
|
setContext, |
||||
|
getLocation, |
||||
|
compile, |
||||
|
getQueryDiff, |
||||
|
globalHandleError |
||||
|
} from './utils.js' |
||||
|
import { createApp, NuxtError } from './index.js' |
||||
|
import fetchMixin from './mixins/fetch.client' |
||||
|
import NuxtLink from './components/nuxt-link.client.js' // should be included after ./index.js
|
||||
|
|
||||
|
// Fetch mixin
|
||||
|
if (!Vue.__nuxt__fetch__mixin__) { |
||||
|
Vue.mixin(fetchMixin) |
||||
|
Vue.__nuxt__fetch__mixin__ = true |
||||
|
} |
||||
|
|
||||
|
// Component: <NuxtLink>
|
||||
|
Vue.component(NuxtLink.name, NuxtLink) |
||||
|
Vue.component('NLink', NuxtLink) |
||||
|
|
||||
|
if (!global.fetch) { global.fetch = fetch } |
||||
|
|
||||
|
// Global shared references
|
||||
|
let _lastPaths = [] |
||||
|
let app |
||||
|
let router |
||||
|
|
||||
|
// Try to rehydrate SSR data from window
|
||||
|
const NUXT = window.__NUXT__ || {} |
||||
|
|
||||
|
Object.assign(Vue.config, {"silent":false,"performance":true}) |
||||
|
|
||||
|
const logs = NUXT.logs || [] |
||||
|
if (logs.length > 0) { |
||||
|
const ssrLogSyle = 'background: #2E495E;border-radius: 0.5em;color: white;font-weight: bold;padding: 2px 0.5em;' |
||||
|
console.group && console.group ('%cNuxt SSR', ssrLogSyle) |
||||
|
logs.forEach(logObj => (console[logObj.type] || console.log)(...logObj.args)) |
||||
|
delete NUXT.logs |
||||
|
console.groupEnd && console.groupEnd() |
||||
|
} |
||||
|
|
||||
|
// Setup global Vue error handler
|
||||
|
if (!Vue.config.$nuxt) { |
||||
|
const defaultErrorHandler = Vue.config.errorHandler |
||||
|
Vue.config.errorHandler = (err, vm, info, ...rest) => { |
||||
|
// Call other handler if exist
|
||||
|
let handled = null |
||||
|
if (typeof defaultErrorHandler === 'function') { |
||||
|
handled = defaultErrorHandler(err, vm, info, ...rest) |
||||
|
} |
||||
|
if (handled === true) { |
||||
|
return handled |
||||
|
} |
||||
|
|
||||
|
if (vm && vm.$root) { |
||||
|
const nuxtApp = Object.keys(Vue.config.$nuxt) |
||||
|
.find(nuxtInstance => vm.$root[nuxtInstance]) |
||||
|
|
||||
|
// Show Nuxt Error Page
|
||||
|
if (nuxtApp && vm.$root[nuxtApp].error && info !== 'render function') { |
||||
|
vm.$root[nuxtApp].error(err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (typeof defaultErrorHandler === 'function') { |
||||
|
return handled |
||||
|
} |
||||
|
|
||||
|
// Log to console
|
||||
|
if (process.env.NODE_ENV !== 'production') { |
||||
|
console.error(err) |
||||
|
} else { |
||||
|
console.error(err.message || err) |
||||
|
} |
||||
|
} |
||||
|
Vue.config.$nuxt = {} |
||||
|
} |
||||
|
Vue.config.$nuxt.$nuxt = true |
||||
|
|
||||
|
const errorHandler = Vue.config.errorHandler || console.error |
||||
|
|
||||
|
// Create and mount App
|
||||
|
createApp().then(mountApp).catch(errorHandler) |
||||
|
|
||||
|
function componentOption (component, key, ...args) { |
||||
|
if (!component || !component.options || !component.options[key]) { |
||||
|
return {} |
||||
|
} |
||||
|
const option = component.options[key] |
||||
|
if (typeof option === 'function') { |
||||
|
return option(...args) |
||||
|
} |
||||
|
return option |
||||
|
} |
||||
|
|
||||
|
function mapTransitions (toComponents, to, from) { |
||||
|
const componentTransitions = (component) => { |
||||
|
const transition = componentOption(component, 'transition', to, from) || {} |
||||
|
return (typeof transition === 'string' ? { name: transition } : transition) |
||||
|
} |
||||
|
|
||||
|
const fromComponents = from ? getMatchedComponents(from) : [] |
||||
|
const maxDepth = Math.max(toComponents.length, fromComponents.length) |
||||
|
|
||||
|
const mergedTransitions = [] |
||||
|
for (let i=0; i<maxDepth; i++) { |
||||
|
// Clone original objects to prevent overrides
|
||||
|
const toTransitions = Object.assign({}, componentTransitions(toComponents[i])) |
||||
|
const transitions = Object.assign({}, componentTransitions(fromComponents[i])) |
||||
|
|
||||
|
// Combine transitions & prefer `leave` properties of "from" route
|
||||
|
Object.keys(toTransitions) |
||||
|
.filter(key => typeof toTransitions[key] !== 'undefined' && !key.toLowerCase().includes('leave')) |
||||
|
.forEach((key) => { transitions[key] = toTransitions[key] }) |
||||
|
|
||||
|
mergedTransitions.push(transitions) |
||||
|
} |
||||
|
return mergedTransitions |
||||
|
} |
||||
|
|
||||
|
async function loadAsyncComponents (to, from, next) { |
||||
|
// Check if route changed (this._routeChanged), only if the page is not an error (for validate())
|
||||
|
this._routeChanged = Boolean(app.nuxt.err) || from.name !== to.name |
||||
|
this._paramChanged = !this._routeChanged && from.path !== to.path |
||||
|
this._queryChanged = !this._paramChanged && from.fullPath !== to.fullPath |
||||
|
this._diffQuery = (this._queryChanged ? getQueryDiff(to.query, from.query) : []) |
||||
|
|
||||
|
if ((this._routeChanged || this._paramChanged) && this.$loading.start && !this.$loading.manual) { |
||||
|
this.$loading.start() |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
if (this._queryChanged) { |
||||
|
const Components = await resolveRouteComponents( |
||||
|
to, |
||||
|
(Component, instance) => ({ Component, instance }) |
||||
|
) |
||||
|
// Add a marker on each component that it needs to refresh or not
|
||||
|
const startLoader = Components.some(({ Component, instance }) => { |
||||
|
const watchQuery = Component.options.watchQuery |
||||
|
if (watchQuery === true) { |
||||
|
return true |
||||
|
} |
||||
|
if (Array.isArray(watchQuery)) { |
||||
|
return watchQuery.some(key => this._diffQuery[key]) |
||||
|
} |
||||
|
if (typeof watchQuery === 'function') { |
||||
|
return watchQuery.apply(instance, [to.query, from.query]) |
||||
|
} |
||||
|
return false |
||||
|
}) |
||||
|
if (startLoader && this.$loading.start && !this.$loading.manual) { |
||||
|
this.$loading.start() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Call next()
|
||||
|
next() |
||||
|
} catch (error) { |
||||
|
const err = error || {} |
||||
|
const statusCode = err.statusCode || err.status || (err.response && err.response.status) || 500 |
||||
|
const message = err.message || '' |
||||
|
|
||||
|
// Handle chunk loading errors
|
||||
|
// This may be due to a new deployment or a network problem
|
||||
|
if (/^Loading( CSS)? chunk (\d)+ failed\./.test(message)) { |
||||
|
window.location.reload(true /* skip cache */) |
||||
|
return // prevent error page blinking for user
|
||||
|
} |
||||
|
|
||||
|
this.error({ statusCode, message }) |
||||
|
this.$nuxt.$emit('routeChanged', to, from, err) |
||||
|
next() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function applySSRData (Component, ssrData) { |
||||
|
if (NUXT.serverRendered && ssrData) { |
||||
|
applyAsyncData(Component, ssrData) |
||||
|
} |
||||
|
|
||||
|
Component._Ctor = Component |
||||
|
return Component |
||||
|
} |
||||
|
|
||||
|
// Get matched components
|
||||
|
function resolveComponents (router) { |
||||
|
const path = getLocation(router.options.base, router.options.mode) |
||||
|
|
||||
|
return flatMapComponents(router.match(path), async (Component, _, match, key, index) => { |
||||
|
// If component is not resolved yet, resolve it
|
||||
|
if (typeof Component === 'function' && !Component.options) { |
||||
|
Component = await Component() |
||||
|
} |
||||
|
// Sanitize it and save it
|
||||
|
const _Component = applySSRData(sanitizeComponent(Component), NUXT.data ? NUXT.data[index] : null) |
||||
|
match.components[key] = _Component |
||||
|
return _Component |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
function callMiddleware (Components, context, layout) { |
||||
|
let midd = [] |
||||
|
let unknownMiddleware = false |
||||
|
|
||||
|
// If layout is undefined, only call global middleware
|
||||
|
if (typeof layout !== 'undefined') { |
||||
|
midd = [] // Exclude global middleware if layout defined (already called before)
|
||||
|
layout = sanitizeComponent(layout) |
||||
|
if (layout.options.middleware) { |
||||
|
midd = midd.concat(layout.options.middleware) |
||||
|
} |
||||
|
Components.forEach((Component) => { |
||||
|
if (Component.options.middleware) { |
||||
|
midd = midd.concat(Component.options.middleware) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
midd = midd.map((name) => { |
||||
|
if (typeof name === 'function') { |
||||
|
return name |
||||
|
} |
||||
|
if (typeof middleware[name] !== 'function') { |
||||
|
unknownMiddleware = true |
||||
|
this.error({ statusCode: 500, message: 'Unknown middleware ' + name }) |
||||
|
} |
||||
|
return middleware[name] |
||||
|
}) |
||||
|
|
||||
|
if (unknownMiddleware) { |
||||
|
return |
||||
|
} |
||||
|
return middlewareSeries(midd, context) |
||||
|
} |
||||
|
|
||||
|
async function render (to, from, next) { |
||||
|
if (this._routeChanged === false && this._paramChanged === false && this._queryChanged === false) { |
||||
|
return next() |
||||
|
} |
||||
|
// Handle first render on SPA mode
|
||||
|
if (to === from) { |
||||
|
_lastPaths = [] |
||||
|
} else { |
||||
|
const fromMatches = [] |
||||
|
_lastPaths = getMatchedComponents(from, fromMatches).map((Component, i) => { |
||||
|
return compile(from.matched[fromMatches[i]].path)(from.params) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// nextCalled is true when redirected
|
||||
|
let nextCalled = false |
||||
|
const _next = (path) => { |
||||
|
if (from.path === path.path && this.$loading.finish) { |
||||
|
this.$loading.finish() |
||||
|
} |
||||
|
|
||||
|
if (from.path !== path.path && this.$loading.pause) { |
||||
|
this.$loading.pause() |
||||
|
} |
||||
|
|
||||
|
if (nextCalled) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
nextCalled = true |
||||
|
next(path) |
||||
|
} |
||||
|
|
||||
|
// Update context
|
||||
|
await setContext(app, { |
||||
|
route: to, |
||||
|
from, |
||||
|
next: _next.bind(this) |
||||
|
}) |
||||
|
this._dateLastError = app.nuxt.dateErr |
||||
|
this._hadError = Boolean(app.nuxt.err) |
||||
|
|
||||
|
// Get route's matched components
|
||||
|
const matches = [] |
||||
|
const Components = getMatchedComponents(to, matches) |
||||
|
|
||||
|
// If no Components matched, generate 404
|
||||
|
if (!Components.length) { |
||||
|
// Default layout
|
||||
|
await callMiddleware.call(this, Components, app.context) |
||||
|
if (nextCalled) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// Load layout for error page
|
||||
|
const errorLayout = (NuxtError.options || NuxtError).layout |
||||
|
const layout = await this.loadLayout( |
||||
|
typeof errorLayout === 'function' |
||||
|
? errorLayout.call(NuxtError, app.context) |
||||
|
: errorLayout |
||||
|
) |
||||
|
|
||||
|
await callMiddleware.call(this, Components, app.context, layout) |
||||
|
if (nextCalled) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// Show error page
|
||||
|
app.context.error({ statusCode: 404, message: 'This page could not be found' }) |
||||
|
return next() |
||||
|
} |
||||
|
|
||||
|
// Update ._data and other properties if hot reloaded
|
||||
|
Components.forEach((Component) => { |
||||
|
if (Component._Ctor && Component._Ctor.options) { |
||||
|
Component.options.asyncData = Component._Ctor.options.asyncData |
||||
|
Component.options.fetch = Component._Ctor.options.fetch |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// Apply transitions
|
||||
|
this.setTransitions(mapTransitions(Components, to, from)) |
||||
|
|
||||
|
try { |
||||
|
// Call middleware
|
||||
|
await callMiddleware.call(this, Components, app.context) |
||||
|
if (nextCalled) { |
||||
|
return |
||||
|
} |
||||
|
if (app.context._errored) { |
||||
|
return next() |
||||
|
} |
||||
|
|
||||
|
// Set layout
|
||||
|
let layout = Components[0].options.layout |
||||
|
if (typeof layout === 'function') { |
||||
|
layout = layout(app.context) |
||||
|
} |
||||
|
layout = await this.loadLayout(layout) |
||||
|
|
||||
|
// Call middleware for layout
|
||||
|
await callMiddleware.call(this, Components, app.context, layout) |
||||
|
if (nextCalled) { |
||||
|
return |
||||
|
} |
||||
|
if (app.context._errored) { |
||||
|
return next() |
||||
|
} |
||||
|
|
||||
|
// Call .validate()
|
||||
|
let isValid = true |
||||
|
try { |
||||
|
for (const Component of Components) { |
||||
|
if (typeof Component.options.validate !== 'function') { |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
isValid = await Component.options.validate(app.context) |
||||
|
|
||||
|
if (!isValid) { |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
} catch (validationError) { |
||||
|
// ...If .validate() threw an error
|
||||
|
this.error({ |
||||
|
statusCode: validationError.statusCode || '500', |
||||
|
message: validationError.message |
||||
|
}) |
||||
|
return next() |
||||
|
} |
||||
|
|
||||
|
// ...If .validate() returned false
|
||||
|
if (!isValid) { |
||||
|
this.error({ statusCode: 404, message: 'This page could not be found' }) |
||||
|
return next() |
||||
|
} |
||||
|
|
||||
|
let instances |
||||
|
// Call asyncData & fetch hooks on components matched by the route.
|
||||
|
await Promise.all(Components.map((Component, i) => { |
||||
|
// Check if only children route changed
|
||||
|
Component._path = compile(to.matched[matches[i]].path)(to.params) |
||||
|
Component._dataRefresh = false |
||||
|
const childPathChanged = Component._path !== _lastPaths[i] |
||||
|
// Refresh component (call asyncData & fetch) when:
|
||||
|
// Route path changed part includes current component
|
||||
|
// Or route param changed part includes current component and watchParam is not `false`
|
||||
|
// Or route query is changed and watchQuery returns `true`
|
||||
|
if (this._routeChanged && childPathChanged) { |
||||
|
Component._dataRefresh = true |
||||
|
} else if (this._paramChanged && childPathChanged) { |
||||
|
const watchParam = Component.options.watchParam |
||||
|
Component._dataRefresh = watchParam !== false |
||||
|
} else if (this._queryChanged) { |
||||
|
const watchQuery = Component.options.watchQuery |
||||
|
if (watchQuery === true) { |
||||
|
Component._dataRefresh = true |
||||
|
} else if (Array.isArray(watchQuery)) { |
||||
|
Component._dataRefresh = watchQuery.some(key => this._diffQuery[key]) |
||||
|
} else if (typeof watchQuery === 'function') { |
||||
|
if (!instances) { |
||||
|
instances = getMatchedComponentsInstances(to) |
||||
|
} |
||||
|
Component._dataRefresh = watchQuery.apply(instances[i], [to.query, from.query]) |
||||
|
} |
||||
|
} |
||||
|
if (!this._hadError && this._isMounted && !Component._dataRefresh) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
const promises = [] |
||||
|
|
||||
|
const hasAsyncData = ( |
||||
|
Component.options.asyncData && |
||||
|
typeof Component.options.asyncData === 'function' |
||||
|
) |
||||
|
|
||||
|
const hasFetch = Boolean(Component.options.fetch) && Component.options.fetch.length |
||||
|
|
||||
|
const loadingIncrease = (hasAsyncData && hasFetch) ? 30 : 45 |
||||
|
|
||||
|
// Call asyncData(context)
|
||||
|
if (hasAsyncData) { |
||||
|
const promise = promisify(Component.options.asyncData, app.context) |
||||
|
.then((asyncDataResult) => { |
||||
|
applyAsyncData(Component, asyncDataResult) |
||||
|
|
||||
|
if (this.$loading.increase) { |
||||
|
this.$loading.increase(loadingIncrease) |
||||
|
} |
||||
|
}) |
||||
|
promises.push(promise) |
||||
|
} |
||||
|
|
||||
|
// Check disabled page loading
|
||||
|
this.$loading.manual = Component.options.loading === false |
||||
|
|
||||
|
// Call fetch(context)
|
||||
|
if (hasFetch) { |
||||
|
let p = Component.options.fetch(app.context) |
||||
|
if (!p || (!(p instanceof Promise) && (typeof p.then !== 'function'))) { |
||||
|
p = Promise.resolve(p) |
||||
|
} |
||||
|
p.then((fetchResult) => { |
||||
|
if (this.$loading.increase) { |
||||
|
this.$loading.increase(loadingIncrease) |
||||
|
} |
||||
|
}) |
||||
|
promises.push(p) |
||||
|
} |
||||
|
|
||||
|
return Promise.all(promises) |
||||
|
})) |
||||
|
|
||||
|
// If not redirected
|
||||
|
if (!nextCalled) { |
||||
|
if (this.$loading.finish && !this.$loading.manual) { |
||||
|
this.$loading.finish() |
||||
|
} |
||||
|
|
||||
|
next() |
||||
|
} |
||||
|
} catch (err) { |
||||
|
const error = err || {} |
||||
|
if (error.message === 'ERR_REDIRECT') { |
||||
|
return this.$nuxt.$emit('routeChanged', to, from, error) |
||||
|
} |
||||
|
_lastPaths = [] |
||||
|
|
||||
|
globalHandleError(error) |
||||
|
|
||||
|
// Load error layout
|
||||
|
let layout = (NuxtError.options || NuxtError).layout |
||||
|
if (typeof layout === 'function') { |
||||
|
layout = layout(app.context) |
||||
|
} |
||||
|
await this.loadLayout(layout) |
||||
|
|
||||
|
this.error(error) |
||||
|
this.$nuxt.$emit('routeChanged', to, from, error) |
||||
|
next() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Fix components format in matched, it's due to code-splitting of vue-router
|
||||
|
function normalizeComponents (to, ___) { |
||||
|
flatMapComponents(to, (Component, _, match, key) => { |
||||
|
if (typeof Component === 'object' && !Component.options) { |
||||
|
// Updated via vue-router resolveAsyncComponents()
|
||||
|
Component = Vue.extend(Component) |
||||
|
Component._Ctor = Component |
||||
|
match.components[key] = Component |
||||
|
} |
||||
|
return Component |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
function showNextPage (to) { |
||||
|
// Hide error component if no error
|
||||
|
if (this._hadError && this._dateLastError === this.$options.nuxt.dateErr) { |
||||
|
this.error() |
||||
|
} |
||||
|
|
||||
|
// Set layout
|
||||
|
let layout = this.$options.nuxt.err |
||||
|
? (NuxtError.options || NuxtError).layout |
||||
|
: to.matched[0].components.default.options.layout |
||||
|
|
||||
|
if (typeof layout === 'function') { |
||||
|
layout = layout(app.context) |
||||
|
} |
||||
|
this.setLayout(layout) |
||||
|
} |
||||
|
|
||||
|
// When navigating on a different route but the same component is used, Vue.js
|
||||
|
// Will not update the instance data, so we have to update $data ourselves
|
||||
|
function fixPrepatch (to, ___) { |
||||
|
if (this._routeChanged === false && this._paramChanged === false && this._queryChanged === false) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
const instances = getMatchedComponentsInstances(to) |
||||
|
const Components = getMatchedComponents(to) |
||||
|
|
||||
|
Vue.nextTick(() => { |
||||
|
instances.forEach((instance, i) => { |
||||
|
if (!instance || instance._isDestroyed) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if ( |
||||
|
instance.constructor._dataRefresh && |
||||
|
Components[i] === instance.constructor && |
||||
|
instance.$vnode.data.keepAlive !== true && |
||||
|
typeof instance.constructor.options.data === 'function' |
||||
|
) { |
||||
|
const newData = instance.constructor.options.data.call(instance) |
||||
|
for (const key in newData) { |
||||
|
Vue.set(instance.$data, key, newData[key]) |
||||
|
} |
||||
|
|
||||
|
// Ensure to trigger scroll event after calling scrollBehavior
|
||||
|
window.$nuxt.$nextTick(() => { |
||||
|
window.$nuxt.$emit('triggerScroll') |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
showNextPage.call(this, to) |
||||
|
|
||||
|
// Hot reloading
|
||||
|
setTimeout(() => hotReloadAPI(this), 100) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
function nuxtReady (_app) { |
||||
|
window.onNuxtReadyCbs.forEach((cb) => { |
||||
|
if (typeof cb === 'function') { |
||||
|
cb(_app) |
||||
|
} |
||||
|
}) |
||||
|
// Special JSDOM
|
||||
|
if (typeof window._onNuxtLoaded === 'function') { |
||||
|
window._onNuxtLoaded(_app) |
||||
|
} |
||||
|
// Add router hooks
|
||||
|
router.afterEach((to, from) => { |
||||
|
// Wait for fixPrepatch + $data updates
|
||||
|
Vue.nextTick(() => _app.$nuxt.$emit('routeChanged', to, from)) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const noopData = () => { return {} } |
||||
|
const noopFetch = () => {} |
||||
|
|
||||
|
// Special hot reload with asyncData(context)
|
||||
|
function getNuxtChildComponents ($parent, $components = []) { |
||||
|
$parent.$children.forEach(($child) => { |
||||
|
if ($child.$vnode && $child.$vnode.data.nuxtChild && !$components.find(c =>(c.$options.__file === $child.$options.__file))) { |
||||
|
$components.push($child) |
||||
|
} |
||||
|
if ($child.$children && $child.$children.length) { |
||||
|
getNuxtChildComponents($child, $components) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
return $components |
||||
|
} |
||||
|
|
||||
|
function hotReloadAPI(_app) { |
||||
|
if (!module.hot) return |
||||
|
|
||||
|
let $components = getNuxtChildComponents(_app.$nuxt, []) |
||||
|
|
||||
|
$components.forEach(addHotReload.bind(_app)) |
||||
|
} |
||||
|
|
||||
|
function addHotReload ($component, depth) { |
||||
|
if ($component.$vnode.data._hasHotReload) return |
||||
|
$component.$vnode.data._hasHotReload = true |
||||
|
|
||||
|
var _forceUpdate = $component.$forceUpdate.bind($component.$parent) |
||||
|
|
||||
|
$component.$vnode.context.$forceUpdate = async () => { |
||||
|
let Components = getMatchedComponents(router.currentRoute) |
||||
|
let Component = Components[depth] |
||||
|
if (!Component) { |
||||
|
return _forceUpdate() |
||||
|
} |
||||
|
if (typeof Component === 'object' && !Component.options) { |
||||
|
// Updated via vue-router resolveAsyncComponents()
|
||||
|
Component = Vue.extend(Component) |
||||
|
Component._Ctor = Component |
||||
|
} |
||||
|
this.error() |
||||
|
let promises = [] |
||||
|
const next = function (path) { |
||||
|
this.$loading.finish && this.$loading.finish() |
||||
|
router.push(path) |
||||
|
} |
||||
|
await setContext(app, { |
||||
|
route: router.currentRoute, |
||||
|
isHMR: true, |
||||
|
next: next.bind(this) |
||||
|
}) |
||||
|
const context = app.context |
||||
|
|
||||
|
if (this.$loading.start && !this.$loading.manual) { |
||||
|
this.$loading.start() |
||||
|
} |
||||
|
|
||||
|
callMiddleware.call(this, Components, context) |
||||
|
.then(() => { |
||||
|
// If layout changed
|
||||
|
if (depth !== 0) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
let layout = Component.options.layout || 'default' |
||||
|
if (typeof layout === 'function') { |
||||
|
layout = layout(context) |
||||
|
} |
||||
|
if (this.layoutName === layout) { |
||||
|
return |
||||
|
} |
||||
|
let promise = this.loadLayout(layout) |
||||
|
promise.then(() => { |
||||
|
this.setLayout(layout) |
||||
|
Vue.nextTick(() => hotReloadAPI(this)) |
||||
|
}) |
||||
|
return promise |
||||
|
}) |
||||
|
|
||||
|
.then(() => { |
||||
|
return callMiddleware.call(this, Components, context, this.layout) |
||||
|
}) |
||||
|
|
||||
|
.then(() => { |
||||
|
// Call asyncData(context)
|
||||
|
let pAsyncData = promisify(Component.options.asyncData || noopData, context) |
||||
|
pAsyncData.then((asyncDataResult) => { |
||||
|
applyAsyncData(Component, asyncDataResult) |
||||
|
this.$loading.increase && this.$loading.increase(30) |
||||
|
}) |
||||
|
promises.push(pAsyncData) |
||||
|
|
||||
|
// Call fetch()
|
||||
|
Component.options.fetch = Component.options.fetch || noopFetch |
||||
|
let pFetch = Component.options.fetch.length && Component.options.fetch(context) |
||||
|
if (!pFetch || (!(pFetch instanceof Promise) && (typeof pFetch.then !== 'function'))) { pFetch = Promise.resolve(pFetch) } |
||||
|
pFetch.then(() => this.$loading.increase && this.$loading.increase(30)) |
||||
|
promises.push(pFetch) |
||||
|
|
||||
|
return Promise.all(promises) |
||||
|
}) |
||||
|
.then(() => { |
||||
|
this.$loading.finish && this.$loading.finish() |
||||
|
_forceUpdate() |
||||
|
setTimeout(() => hotReloadAPI(this), 100) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
async function mountApp (__app) { |
||||
|
// Set global variables
|
||||
|
app = __app.app |
||||
|
router = __app.router |
||||
|
|
||||
|
// Create Vue instance
|
||||
|
const _app = new Vue(app) |
||||
|
|
||||
|
// Load layout
|
||||
|
const layout = NUXT.layout || 'default' |
||||
|
await _app.loadLayout(layout) |
||||
|
_app.setLayout(layout) |
||||
|
|
||||
|
// Mounts Vue app to DOM element
|
||||
|
const mount = () => { |
||||
|
_app.$mount('#__nuxt') |
||||
|
|
||||
|
// Add afterEach router hooks
|
||||
|
router.afterEach(normalizeComponents) |
||||
|
router.afterEach(fixPrepatch.bind(_app)) |
||||
|
|
||||
|
// Listen for first Vue update
|
||||
|
Vue.nextTick(() => { |
||||
|
// Call window.{{globals.readyCallback}} callbacks
|
||||
|
nuxtReady(_app) |
||||
|
|
||||
|
// Enable hot reloading
|
||||
|
hotReloadAPI(_app) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// Resolve route components
|
||||
|
const Components = await Promise.all(resolveComponents(router)) |
||||
|
|
||||
|
// Enable transitions
|
||||
|
_app.setTransitions = _app.$options.nuxt.setTransitions.bind(_app) |
||||
|
if (Components.length) { |
||||
|
_app.setTransitions(mapTransitions(Components, router.currentRoute)) |
||||
|
_lastPaths = router.currentRoute.matched.map(route => compile(route.path)(router.currentRoute.params)) |
||||
|
} |
||||
|
|
||||
|
// Initialize error handler
|
||||
|
_app.$loading = {} // To avoid error while _app.$nuxt does not exist
|
||||
|
if (NUXT.error) { |
||||
|
_app.error(NUXT.error) |
||||
|
} |
||||
|
|
||||
|
// Add beforeEach router hooks
|
||||
|
router.beforeEach(loadAsyncComponents.bind(_app)) |
||||
|
router.beforeEach(render.bind(_app)) |
||||
|
|
||||
|
// If page already is server rendered and it was done on the same route path as client side render
|
||||
|
if (NUXT.serverRendered && NUXT.routePath === _app.context.route.path) { |
||||
|
mount() |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// First render on client-side
|
||||
|
const clientFirstMount = () => { |
||||
|
normalizeComponents(router.currentRoute, router.currentRoute) |
||||
|
showNextPage.call(_app, router.currentRoute) |
||||
|
// Don't call fixPrepatch.call(_app, router.currentRoute, router.currentRoute) since it's first render
|
||||
|
mount() |
||||
|
} |
||||
|
|
||||
|
render.call(_app, router.currentRoute, router.currentRoute, (path) => { |
||||
|
// If not redirected
|
||||
|
if (!path) { |
||||
|
clientFirstMount() |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// Add a one-time afterEach hook to
|
||||
|
// mount the app wait for redirect and route gets resolved
|
||||
|
const unregisterHook = router.afterEach((to, from) => { |
||||
|
unregisterHook() |
||||
|
clientFirstMount() |
||||
|
}) |
||||
|
|
||||
|
// Push the path and let route to be resolved
|
||||
|
router.push(path, undefined, (err) => { |
||||
|
if (err) { |
||||
|
errorHandler(err) |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
} |
@ -0,0 +1,142 @@ |
|||||
|
<template> |
||||
|
<transition appear> |
||||
|
<div v-if="building" class="nuxt__build_indicator" :style="indicatorStyle"> |
||||
|
<svg viewBox="0 0 96 72" version="1" xmlns="http://www.w3.org/2000/svg"> |
||||
|
<g fill="none" fill-rule="evenodd"> |
||||
|
<path d="M6 66h23l1-3 21-37L40 6 6 66zM79 66h11L62 17l-5 9 22 37v3zM54 31L35 66h38z" /> |
||||
|
<path d="M29 69v-1-2H6L40 6l11 20 3-6L44 3s-2-3-4-3-3 1-5 3L1 63c0 1-2 3 0 6 0 1 2 2 5 2h28c-3 0-4-1-5-2z" fill="#00C58E" /> |
||||
|
<path d="M95 63L67 14c0-1-2-3-5-3-1 0-3 0-4 3l-4 6 3 6 5-9 28 49H79a5 5 0 0 1 0 3c-2 2-5 2-5 2h16c1 0 4 0 5-2 1-1 2-3 0-6z" fill="#00C58E" /> |
||||
|
<path d="M79 69v-1-2-3L57 26l-3-6-3 6-21 37-1 3a5 5 0 0 0 0 3c1 1 2 2 5 2h40s3 0 5-2zM54 31l19 35H35l19-35z" fill="#FFF" fill-rule="nonzero" /> |
||||
|
</g> |
||||
|
</svg> |
||||
|
{{ animatedProgress }}% |
||||
|
</div> |
||||
|
</transition> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: 'NuxtBuildIndicator', |
||||
|
data () { |
||||
|
return { |
||||
|
building: false, |
||||
|
progress: 0, |
||||
|
animatedProgress: 0, |
||||
|
reconnectAttempts: 0 |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
options: () => ({"position":"bottom-right","backgroundColor":"#2E495E","color":"#00C48D"}), |
||||
|
indicatorStyle () { |
||||
|
const [d1, d2] = this.options.position.split('-') |
||||
|
return { |
||||
|
[d1]: '20px', |
||||
|
[d2]: '20px', |
||||
|
'background-color': this.options.backgroundColor, |
||||
|
color: this.options.color |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
progress (val, oldVal) { |
||||
|
// Average progress may decrease but ignore it! |
||||
|
if (val < oldVal) { |
||||
|
return |
||||
|
} |
||||
|
// Cancel old animation |
||||
|
clearInterval(this._progressAnimation) |
||||
|
// Jump to edge immediately |
||||
|
if (val < 10 || val > 90) { |
||||
|
this.animatedProgress = val |
||||
|
return |
||||
|
} |
||||
|
// Animate to value |
||||
|
this._progressAnimation = setInterval(() => { |
||||
|
const diff = this.progress - this.animatedProgress |
||||
|
if (diff > 0) { |
||||
|
this.animatedProgress++ |
||||
|
} else { |
||||
|
clearInterval(this._progressAnimation) |
||||
|
} |
||||
|
}, 50) |
||||
|
} |
||||
|
}, |
||||
|
mounted () { |
||||
|
if (EventSource === undefined) { |
||||
|
return // Unsupported |
||||
|
} |
||||
|
this.sseConnect() |
||||
|
}, |
||||
|
beforeDestroy () { |
||||
|
this.sseClose() |
||||
|
clearInterval(this._progressAnimation) |
||||
|
}, |
||||
|
methods: { |
||||
|
sseConnect () { |
||||
|
if (this._connecting) { |
||||
|
return |
||||
|
} |
||||
|
this._connecting = true |
||||
|
this.sse = new EventSource('/_loading/sse') |
||||
|
this.sse.addEventListener('message', event => this.onSseMessage(event)) |
||||
|
}, |
||||
|
onSseMessage (message) { |
||||
|
const data = JSON.parse(message.data) |
||||
|
if (!data.states) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.progress = Math.round(data.states.reduce((p, s) => p + s.progress, 0) / data.states.length) |
||||
|
|
||||
|
if (!data.allDone) { |
||||
|
this.building = true |
||||
|
} else { |
||||
|
this.$nextTick(() => { |
||||
|
this.building = false |
||||
|
this.animatedProgress = 0 |
||||
|
this.progress = 0 |
||||
|
clearInterval(this._progressAnimation) |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
sseClose () { |
||||
|
if (this.sse) { |
||||
|
this.sse.close() |
||||
|
delete this.sse |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.nuxt__build_indicator { |
||||
|
box-sizing: border-box; |
||||
|
position: fixed; |
||||
|
font-family: monospace; |
||||
|
padding: 5px 10px; |
||||
|
border-radius: 5px; |
||||
|
box-shadow: 1px 1px 2px 0px rgba(0,0,0,0.2); |
||||
|
width: 88px; |
||||
|
z-index: 2147483647; |
||||
|
font-size: 16px; |
||||
|
line-height: 1.2rem; |
||||
|
} |
||||
|
.v-enter-active, .v-leave-active { |
||||
|
transition-delay: 0.2s; |
||||
|
transition-property: all; |
||||
|
transition-duration: 0.3s; |
||||
|
} |
||||
|
.v-leave-to { |
||||
|
opacity: 0; |
||||
|
transform: translateY(20px); |
||||
|
} |
||||
|
svg { |
||||
|
display: inline-block; |
||||
|
vertical-align: baseline; |
||||
|
width: 1.1em; |
||||
|
position: relative; |
||||
|
top: 1px; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,122 @@ |
|||||
|
|
||||
|
export default { |
||||
|
name: 'NuxtChild', |
||||
|
functional: true, |
||||
|
props: { |
||||
|
nuxtChildKey: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
}, |
||||
|
keepAlive: Boolean, |
||||
|
keepAliveProps: { |
||||
|
type: Object, |
||||
|
default: undefined |
||||
|
} |
||||
|
}, |
||||
|
render (_, { parent, data, props }) { |
||||
|
const h = parent.$createElement |
||||
|
|
||||
|
data.nuxtChild = true |
||||
|
const _parent = parent |
||||
|
const transitions = parent.$nuxt.nuxt.transitions |
||||
|
const defaultTransition = parent.$nuxt.nuxt.defaultTransition |
||||
|
|
||||
|
let depth = 0 |
||||
|
while (parent) { |
||||
|
if (parent.$vnode && parent.$vnode.data.nuxtChild) { |
||||
|
depth++ |
||||
|
} |
||||
|
parent = parent.$parent |
||||
|
} |
||||
|
data.nuxtChildDepth = depth |
||||
|
const transition = transitions[depth] || defaultTransition |
||||
|
const transitionProps = {} |
||||
|
transitionsKeys.forEach((key) => { |
||||
|
if (typeof transition[key] !== 'undefined') { |
||||
|
transitionProps[key] = transition[key] |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const listeners = {} |
||||
|
listenersKeys.forEach((key) => { |
||||
|
if (typeof transition[key] === 'function') { |
||||
|
listeners[key] = transition[key].bind(_parent) |
||||
|
} |
||||
|
}) |
||||
|
if (process.client) { |
||||
|
// Add triggerScroll event on beforeEnter (fix #1376)
|
||||
|
const beforeEnter = listeners.beforeEnter |
||||
|
listeners.beforeEnter = (el) => { |
||||
|
// Ensure to trigger scroll event after calling scrollBehavior
|
||||
|
window.$nuxt.$nextTick(() => { |
||||
|
window.$nuxt.$emit('triggerScroll') |
||||
|
}) |
||||
|
if (beforeEnter) { |
||||
|
return beforeEnter.call(_parent, el) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// make sure that leave is called asynchronous (fix #5703)
|
||||
|
if (transition.css === false) { |
||||
|
const leave = listeners.leave |
||||
|
|
||||
|
// only add leave listener when user didnt provide one
|
||||
|
// or when it misses the done argument
|
||||
|
if (!leave || leave.length < 2) { |
||||
|
listeners.leave = (el, done) => { |
||||
|
if (leave) { |
||||
|
leave.call(_parent, el) |
||||
|
} |
||||
|
|
||||
|
_parent.$nextTick(done) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
let routerView = h('routerView', data) |
||||
|
|
||||
|
if (props.keepAlive) { |
||||
|
routerView = h('keep-alive', { props: props.keepAliveProps }, [routerView]) |
||||
|
} |
||||
|
|
||||
|
return h('transition', { |
||||
|
props: transitionProps, |
||||
|
on: listeners |
||||
|
}, [routerView]) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const transitionsKeys = [ |
||||
|
'name', |
||||
|
'mode', |
||||
|
'appear', |
||||
|
'css', |
||||
|
'type', |
||||
|
'duration', |
||||
|
'enterClass', |
||||
|
'leaveClass', |
||||
|
'appearClass', |
||||
|
'enterActiveClass', |
||||
|
'enterActiveClass', |
||||
|
'leaveActiveClass', |
||||
|
'appearActiveClass', |
||||
|
'enterToClass', |
||||
|
'leaveToClass', |
||||
|
'appearToClass' |
||||
|
] |
||||
|
|
||||
|
const listenersKeys = [ |
||||
|
'beforeEnter', |
||||
|
'enter', |
||||
|
'afterEnter', |
||||
|
'enterCancelled', |
||||
|
'beforeLeave', |
||||
|
'leave', |
||||
|
'afterLeave', |
||||
|
'leaveCancelled', |
||||
|
'beforeAppear', |
||||
|
'appear', |
||||
|
'afterAppear', |
||||
|
'appearCancelled' |
||||
|
] |
@ -0,0 +1,97 @@ |
|||||
|
<template> |
||||
|
<div class="__nuxt-error-page"> |
||||
|
<div class="error"> |
||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="90" height="90" fill="#DBE1EC" viewBox="0 0 48 48"> |
||||
|
<path d="M22 30h4v4h-4zm0-16h4v12h-4zm1.99-10C12.94 4 4 12.95 4 24s8.94 20 19.99 20S44 35.05 44 24 35.04 4 23.99 4zM24 40c-8.84 0-16-7.16-16-16S15.16 8 24 8s16 7.16 16 16-7.16 16-16 16z" /> |
||||
|
</svg> |
||||
|
|
||||
|
<div class="title">{{ message }}</div> |
||||
|
<p v-if="statusCode === 404" class="description"> |
||||
|
<NuxtLink class="error-link" to="/">Back to the home page</NuxtLink> |
||||
|
</p> |
||||
|
|
||||
|
<p class="description" v-else>An error occurred while rendering the page. Check developer tools console for details.</p> |
||||
|
|
||||
|
<div class="logo"> |
||||
|
<a href="https://nuxtjs.org" target="_blank" rel="noopener">Nuxt.js</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: 'NuxtError', |
||||
|
props: { |
||||
|
error: { |
||||
|
type: Object, |
||||
|
default: null |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
statusCode () { |
||||
|
return (this.error && this.error.statusCode) || 500 |
||||
|
}, |
||||
|
message () { |
||||
|
return this.error.message || 'Error' |
||||
|
} |
||||
|
}, |
||||
|
head () { |
||||
|
return { |
||||
|
title: this.message, |
||||
|
meta: [ |
||||
|
{ |
||||
|
name: 'viewport', |
||||
|
content: 'width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no' |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.__nuxt-error-page { |
||||
|
padding: 1rem; |
||||
|
background: #F7F8FB; |
||||
|
color: #47494E; |
||||
|
text-align: center; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
flex-direction: column; |
||||
|
font-family: sans-serif; |
||||
|
font-weight: 100 !important; |
||||
|
-ms-text-size-adjust: 100%; |
||||
|
-webkit-text-size-adjust: 100%; |
||||
|
-webkit-font-smoothing: antialiased; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
} |
||||
|
.__nuxt-error-page .error { |
||||
|
max-width: 450px; |
||||
|
} |
||||
|
.__nuxt-error-page .title { |
||||
|
font-size: 1.5rem; |
||||
|
margin-top: 15px; |
||||
|
color: #47494E; |
||||
|
margin-bottom: 8px; |
||||
|
} |
||||
|
.__nuxt-error-page .description { |
||||
|
color: #7F828B; |
||||
|
line-height: 21px; |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
.__nuxt-error-page a { |
||||
|
color: #7F828B !important; |
||||
|
text-decoration: none; |
||||
|
} |
||||
|
.__nuxt-error-page .logo { |
||||
|
position: fixed; |
||||
|
left: 12px; |
||||
|
bottom: 12px; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,98 @@ |
|||||
|
import Vue from 'vue' |
||||
|
|
||||
|
const requestIdleCallback = window.requestIdleCallback || |
||||
|
function (cb) { |
||||
|
const start = Date.now() |
||||
|
return setTimeout(function () { |
||||
|
cb({ |
||||
|
didTimeout: false, |
||||
|
timeRemaining: () => Math.max(0, 50 - (Date.now() - start)) |
||||
|
}) |
||||
|
}, 1) |
||||
|
} |
||||
|
|
||||
|
const cancelIdleCallback = window.cancelIdleCallback || function (id) { |
||||
|
clearTimeout(id) |
||||
|
} |
||||
|
|
||||
|
const observer = window.IntersectionObserver && new window.IntersectionObserver((entries) => { |
||||
|
entries.forEach(({ intersectionRatio, target: link }) => { |
||||
|
if (intersectionRatio <= 0) { |
||||
|
return |
||||
|
} |
||||
|
link.__prefetch() |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
export default { |
||||
|
name: 'NuxtLink', |
||||
|
extends: Vue.component('RouterLink'), |
||||
|
props: { |
||||
|
prefetch: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
noPrefetch: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
} |
||||
|
}, |
||||
|
mounted () { |
||||
|
if (this.prefetch && !this.noPrefetch) { |
||||
|
this.handleId = requestIdleCallback(this.observe, { timeout: 2e3 }) |
||||
|
} |
||||
|
}, |
||||
|
beforeDestroy () { |
||||
|
cancelIdleCallback(this.handleId) |
||||
|
|
||||
|
if (this.__observed) { |
||||
|
observer.unobserve(this.$el) |
||||
|
delete this.$el.__prefetch |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
observe () { |
||||
|
// If no IntersectionObserver, avoid prefetching
|
||||
|
if (!observer) { |
||||
|
return |
||||
|
} |
||||
|
// Add to observer
|
||||
|
if (this.shouldPrefetch()) { |
||||
|
this.$el.__prefetch = this.prefetchLink.bind(this) |
||||
|
observer.observe(this.$el) |
||||
|
this.__observed = true |
||||
|
} |
||||
|
}, |
||||
|
shouldPrefetch () { |
||||
|
return this.getPrefetchComponents().length > 0 |
||||
|
}, |
||||
|
canPrefetch () { |
||||
|
const conn = navigator.connection |
||||
|
const hasBadConnection = this.$nuxt.isOffline || (conn && ((conn.effectiveType || '').includes('2g') || conn.saveData)) |
||||
|
|
||||
|
return !hasBadConnection |
||||
|
}, |
||||
|
getPrefetchComponents () { |
||||
|
const ref = this.$router.resolve(this.to, this.$route, this.append) |
||||
|
const Components = ref.resolved.matched.map(r => r.components.default) |
||||
|
|
||||
|
return Components.filter(Component => typeof Component === 'function' && !Component.options && !Component.__prefetched) |
||||
|
}, |
||||
|
prefetchLink () { |
||||
|
if (!this.canPrefetch()) { |
||||
|
return |
||||
|
} |
||||
|
// Stop observing this link (in case of internet connection changes)
|
||||
|
observer.unobserve(this.$el) |
||||
|
const Components = this.getPrefetchComponents() |
||||
|
|
||||
|
for (const Component of Components) { |
||||
|
const componentOrPromise = Component() |
||||
|
if (componentOrPromise instanceof Promise) { |
||||
|
componentOrPromise.catch(() => {}) |
||||
|
} |
||||
|
Component.__prefetched = true |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
import Vue from 'vue' |
||||
|
|
||||
|
export default { |
||||
|
name: 'NuxtLink', |
||||
|
extends: Vue.component('RouterLink'), |
||||
|
props: { |
||||
|
prefetch: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
noPrefetch: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,177 @@ |
|||||
|
<script> |
||||
|
export default { |
||||
|
name: 'NuxtLoading', |
||||
|
data () { |
||||
|
return { |
||||
|
percent: 0, |
||||
|
show: false, |
||||
|
canSucceed: true, |
||||
|
reversed: false, |
||||
|
skipTimerCount: 0, |
||||
|
rtl: false, |
||||
|
throttle: 200, |
||||
|
duration: 5000, |
||||
|
continuous: false |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
left () { |
||||
|
if (!this.continuous && !this.rtl) { |
||||
|
return false |
||||
|
} |
||||
|
return this.rtl |
||||
|
? (this.reversed ? '0px' : 'auto') |
||||
|
: (!this.reversed ? '0px' : 'auto') |
||||
|
} |
||||
|
}, |
||||
|
beforeDestroy () { |
||||
|
this.clear() |
||||
|
}, |
||||
|
methods: { |
||||
|
clear () { |
||||
|
clearInterval(this._timer) |
||||
|
clearTimeout(this._throttle) |
||||
|
this._timer = null |
||||
|
}, |
||||
|
start () { |
||||
|
this.clear() |
||||
|
this.percent = 0 |
||||
|
this.reversed = false |
||||
|
this.skipTimerCount = 0 |
||||
|
this.canSucceed = true |
||||
|
|
||||
|
if (this.throttle) { |
||||
|
this._throttle = setTimeout(() => this.startTimer(), this.throttle) |
||||
|
} else { |
||||
|
this.startTimer() |
||||
|
} |
||||
|
return this |
||||
|
}, |
||||
|
set (num) { |
||||
|
this.show = true |
||||
|
this.canSucceed = true |
||||
|
this.percent = Math.min(100, Math.max(0, Math.floor(num))) |
||||
|
return this |
||||
|
}, |
||||
|
get () { |
||||
|
return this.percent |
||||
|
}, |
||||
|
increase (num) { |
||||
|
this.percent = Math.min(100, Math.floor(this.percent + num)) |
||||
|
return this |
||||
|
}, |
||||
|
decrease (num) { |
||||
|
this.percent = Math.max(0, Math.floor(this.percent - num)) |
||||
|
return this |
||||
|
}, |
||||
|
pause () { |
||||
|
clearInterval(this._timer) |
||||
|
return this |
||||
|
}, |
||||
|
resume () { |
||||
|
this.startTimer() |
||||
|
return this |
||||
|
}, |
||||
|
finish () { |
||||
|
this.percent = this.reversed ? 0 : 100 |
||||
|
this.hide() |
||||
|
return this |
||||
|
}, |
||||
|
hide () { |
||||
|
this.clear() |
||||
|
setTimeout(() => { |
||||
|
this.show = false |
||||
|
this.$nextTick(() => { |
||||
|
this.percent = 0 |
||||
|
this.reversed = false |
||||
|
}) |
||||
|
}, 500) |
||||
|
return this |
||||
|
}, |
||||
|
fail (error) { |
||||
|
this.canSucceed = false |
||||
|
return this |
||||
|
}, |
||||
|
startTimer () { |
||||
|
if (!this.show) { |
||||
|
this.show = true |
||||
|
} |
||||
|
if (typeof this._cut === 'undefined') { |
||||
|
this._cut = 10000 / Math.floor(this.duration) |
||||
|
} |
||||
|
|
||||
|
this._timer = setInterval(() => { |
||||
|
/** |
||||
|
* When reversing direction skip one timers |
||||
|
* so 0, 100 are displayed for two iterations |
||||
|
* also disable css width transitioning |
||||
|
* which otherwise interferes and shows |
||||
|
* a jojo effect |
||||
|
*/ |
||||
|
if (this.skipTimerCount > 0) { |
||||
|
this.skipTimerCount-- |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if (this.reversed) { |
||||
|
this.decrease(this._cut) |
||||
|
} else { |
||||
|
this.increase(this._cut) |
||||
|
} |
||||
|
|
||||
|
if (this.continuous) { |
||||
|
if (this.percent >= 100) { |
||||
|
this.skipTimerCount = 1 |
||||
|
|
||||
|
this.reversed = !this.reversed |
||||
|
} else if (this.percent <= 0) { |
||||
|
this.skipTimerCount = 1 |
||||
|
|
||||
|
this.reversed = !this.reversed |
||||
|
} |
||||
|
} |
||||
|
}, 100) |
||||
|
} |
||||
|
}, |
||||
|
render (h) { |
||||
|
let el = h(false) |
||||
|
if (this.show) { |
||||
|
el = h('div', { |
||||
|
staticClass: 'nuxt-progress', |
||||
|
class: { |
||||
|
'nuxt-progress-notransition': this.skipTimerCount > 0, |
||||
|
'nuxt-progress-failed': !this.canSucceed |
||||
|
}, |
||||
|
style: { |
||||
|
width: this.percent + '%', |
||||
|
left: this.left |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
return el |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.nuxt-progress { |
||||
|
position: fixed; |
||||
|
top: 0px; |
||||
|
left: 0px; |
||||
|
right: 0px; |
||||
|
height: 2px; |
||||
|
width: 0%; |
||||
|
opacity: 1; |
||||
|
transition: width 0.1s, opacity 0.4s; |
||||
|
background-color: #3B8070; |
||||
|
z-index: 999999; |
||||
|
} |
||||
|
|
||||
|
.nuxt-progress.nuxt-progress-notransition { |
||||
|
transition: none; |
||||
|
} |
||||
|
|
||||
|
.nuxt-progress-failed { |
||||
|
background-color: red; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,101 @@ |
|||||
|
import Vue from 'vue' |
||||
|
import { compile } from '../utils' |
||||
|
|
||||
|
import NuxtError from './nuxt-error.vue' |
||||
|
|
||||
|
import NuxtChild from './nuxt-child' |
||||
|
|
||||
|
export default { |
||||
|
name: 'Nuxt', |
||||
|
components: { |
||||
|
NuxtChild, |
||||
|
NuxtError |
||||
|
}, |
||||
|
props: { |
||||
|
nuxtChildKey: { |
||||
|
type: String, |
||||
|
default: undefined |
||||
|
}, |
||||
|
keepAlive: Boolean, |
||||
|
keepAliveProps: { |
||||
|
type: Object, |
||||
|
default: undefined |
||||
|
}, |
||||
|
name: { |
||||
|
type: String, |
||||
|
default: 'default' |
||||
|
} |
||||
|
}, |
||||
|
errorCaptured (error) { |
||||
|
// if we receive and error while showing the NuxtError component
|
||||
|
// capture the error and force an immediate update so we re-render
|
||||
|
// without the NuxtError component
|
||||
|
if (this.displayingNuxtError) { |
||||
|
this.errorFromNuxtError = error |
||||
|
this.$forceUpdate() |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
routerViewKey () { |
||||
|
// If nuxtChildKey prop is given or current route has children
|
||||
|
if (typeof this.nuxtChildKey !== 'undefined' || this.$route.matched.length > 1) { |
||||
|
return this.nuxtChildKey || compile(this.$route.matched[0].path)(this.$route.params) |
||||
|
} |
||||
|
|
||||
|
const [matchedRoute] = this.$route.matched |
||||
|
|
||||
|
if (!matchedRoute) { |
||||
|
return this.$route.path |
||||
|
} |
||||
|
|
||||
|
const Component = matchedRoute.components.default |
||||
|
|
||||
|
if (Component && Component.options) { |
||||
|
const { options } = Component |
||||
|
|
||||
|
if (options.key) { |
||||
|
return (typeof options.key === 'function' ? options.key(this.$route) : options.key) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const strict = /\/$/.test(matchedRoute.path) |
||||
|
return strict ? this.$route.path : this.$route.path.replace(/\/$/, '') |
||||
|
} |
||||
|
}, |
||||
|
beforeCreate () { |
||||
|
Vue.util.defineReactive(this, 'nuxt', this.$root.$options.nuxt) |
||||
|
}, |
||||
|
render (h) { |
||||
|
// if there is no error
|
||||
|
if (!this.nuxt.err) { |
||||
|
// Directly return nuxt child
|
||||
|
return h('NuxtChild', { |
||||
|
key: this.routerViewKey, |
||||
|
props: this.$props |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// if an error occured within NuxtError show a simple
|
||||
|
// error message instead to prevent looping
|
||||
|
if (this.errorFromNuxtError) { |
||||
|
this.$nextTick(() => (this.errorFromNuxtError = false)) |
||||
|
|
||||
|
return h('div', {}, [ |
||||
|
h('h2', 'An error occured while showing the error page'), |
||||
|
h('p', 'Unfortunately an error occured and while showing the error page another error occured'), |
||||
|
h('p', `Error details: ${this.errorFromNuxtError.toString()}`), |
||||
|
h('nuxt-link', { props: { to: '/' } }, 'Go back to home') |
||||
|
]) |
||||
|
} |
||||
|
|
||||
|
// track if we are showing the NuxtError component
|
||||
|
this.displayingNuxtError = true |
||||
|
this.$nextTick(() => (this.displayingNuxtError = false)) |
||||
|
|
||||
|
return h(NuxtError, { |
||||
|
props: { |
||||
|
error: this.nuxt.err |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
@ -0,0 +1 @@ |
|||||
|
// This file is intentionally left empty for noop aliases
|
@ -0,0 +1,185 @@ |
|||||
|
import Vue from 'vue' |
||||
|
import Meta from 'vue-meta' |
||||
|
import ClientOnly from 'vue-client-only' |
||||
|
import NoSsr from 'vue-no-ssr' |
||||
|
import { createRouter } from './router.js' |
||||
|
import NuxtChild from './components/nuxt-child.js' |
||||
|
import NuxtError from './components/nuxt-error.vue' |
||||
|
import Nuxt from './components/nuxt.js' |
||||
|
import App from './App.js' |
||||
|
import { setContext, getLocation, getRouteData, normalizeError } from './utils' |
||||
|
|
||||
|
/* Plugins */ |
||||
|
|
||||
|
import nuxt_plugin_nuxtswiperplugin_622602ba from 'nuxt_plugin_nuxtswiperplugin_622602ba' // Source: ..\\plugins\\nuxt-swiper-plugin.js (mode: 'client')
|
||||
|
import nuxt_plugin_elementuiplugin_5181e592 from 'nuxt_plugin_elementuiplugin_5181e592' // Source: ..\\plugins\\element-ui-plugin.js (mode: 'client')
|
||||
|
|
||||
|
// Component: <ClientOnly>
|
||||
|
Vue.component(ClientOnly.name, ClientOnly) |
||||
|
|
||||
|
// TODO: Remove in Nuxt 3: <NoSsr>
|
||||
|
Vue.component(NoSsr.name, { |
||||
|
...NoSsr, |
||||
|
render (h, ctx) { |
||||
|
if (process.client && !NoSsr._warned) { |
||||
|
NoSsr._warned = true |
||||
|
|
||||
|
console.warn('<no-ssr> has been deprecated and will be removed in Nuxt 3, please use <client-only> instead') |
||||
|
} |
||||
|
return NoSsr.render(h, ctx) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// Component: <NuxtChild>
|
||||
|
Vue.component(NuxtChild.name, NuxtChild) |
||||
|
Vue.component('NChild', NuxtChild) |
||||
|
|
||||
|
// Component NuxtLink is imported in server.js or client.js
|
||||
|
|
||||
|
// Component: <Nuxt>
|
||||
|
Vue.component(Nuxt.name, Nuxt) |
||||
|
|
||||
|
Vue.use(Meta, {"keyName":"head","attribute":"data-n-head","ssrAttribute":"data-n-head-ssr","tagIDKeyName":"hid"}) |
||||
|
|
||||
|
const defaultTransition = {"name":"page","mode":"out-in","appear":false,"appearClass":"appear","appearActiveClass":"appear-active","appearToClass":"appear-to"} |
||||
|
|
||||
|
async function createApp (ssrContext) { |
||||
|
const router = await createRouter(ssrContext) |
||||
|
|
||||
|
// Create Root instance
|
||||
|
|
||||
|
// here we inject the router and store to all child components,
|
||||
|
// making them available everywhere as `this.$router` and `this.$store`.
|
||||
|
const app = { |
||||
|
head: {"title":"AI小财神","meta":[{"charset":"utf-8"},{"name":"viewport","content":"width=device-width, initial-scale=1"},{"name":"keywords","content":"AI小财神"},{"name":"description","content":"AI小财神"}],"link":[{"rel":"icon","type":"image\u002Fx-icon","href":"\u002Ffavicon.ico"}],"style":[],"script":[]}, |
||||
|
|
||||
|
router, |
||||
|
nuxt: { |
||||
|
defaultTransition, |
||||
|
transitions: [defaultTransition], |
||||
|
setTransitions (transitions) { |
||||
|
if (!Array.isArray(transitions)) { |
||||
|
transitions = [transitions] |
||||
|
} |
||||
|
transitions = transitions.map((transition) => { |
||||
|
if (!transition) { |
||||
|
transition = defaultTransition |
||||
|
} else if (typeof transition === 'string') { |
||||
|
transition = Object.assign({}, defaultTransition, { name: transition }) |
||||
|
} else { |
||||
|
transition = Object.assign({}, defaultTransition, transition) |
||||
|
} |
||||
|
return transition |
||||
|
}) |
||||
|
this.$options.nuxt.transitions = transitions |
||||
|
return transitions |
||||
|
}, |
||||
|
|
||||
|
err: null, |
||||
|
dateErr: null, |
||||
|
error (err) { |
||||
|
err = err || null |
||||
|
app.context._errored = Boolean(err) |
||||
|
err = err ? normalizeError(err) : null |
||||
|
let nuxt = app.nuxt // to work with @vue/composition-api, see https://github.com/nuxt/nuxt.js/issues/6517#issuecomment-573280207
|
||||
|
if (this) { |
||||
|
nuxt = this.nuxt || this.$options.nuxt |
||||
|
} |
||||
|
nuxt.dateErr = Date.now() |
||||
|
nuxt.err = err |
||||
|
// Used in src/server.js
|
||||
|
if (ssrContext) { |
||||
|
ssrContext.nuxt.error = err |
||||
|
} |
||||
|
return err |
||||
|
} |
||||
|
}, |
||||
|
...App |
||||
|
} |
||||
|
|
||||
|
const next = ssrContext ? ssrContext.next : location => app.router.push(location) |
||||
|
// Resolve route
|
||||
|
let route |
||||
|
if (ssrContext) { |
||||
|
route = router.resolve(ssrContext.url).route |
||||
|
} else { |
||||
|
const path = getLocation(router.options.base, router.options.mode) |
||||
|
route = router.resolve(path).route |
||||
|
} |
||||
|
|
||||
|
// Set context to app.context
|
||||
|
await setContext(app, { |
||||
|
route, |
||||
|
next, |
||||
|
error: app.nuxt.error.bind(app), |
||||
|
payload: ssrContext ? ssrContext.payload : undefined, |
||||
|
req: ssrContext ? ssrContext.req : undefined, |
||||
|
res: ssrContext ? ssrContext.res : undefined, |
||||
|
beforeRenderFns: ssrContext ? ssrContext.beforeRenderFns : undefined, |
||||
|
ssrContext |
||||
|
}) |
||||
|
|
||||
|
const inject = function (key, value) { |
||||
|
if (!key) { |
||||
|
throw new Error('inject(key, value) has no key provided') |
||||
|
} |
||||
|
if (value === undefined) { |
||||
|
throw new Error(`inject('${key}', value) has no value provided`) |
||||
|
} |
||||
|
|
||||
|
key = '$' + key |
||||
|
// Add into app
|
||||
|
app[key] = value |
||||
|
|
||||
|
// 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(() => { |
||||
|
if (!Object.prototype.hasOwnProperty.call(Vue, key)) { |
||||
|
Object.defineProperty(Vue.prototype, key, { |
||||
|
get () { |
||||
|
return this.$root.$options[key] |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// Plugin execution
|
||||
|
|
||||
|
if (process.client && typeof nuxt_plugin_nuxtswiperplugin_622602ba === 'function') { |
||||
|
await nuxt_plugin_nuxtswiperplugin_622602ba(app.context, inject) |
||||
|
} |
||||
|
|
||||
|
if (process.client && typeof nuxt_plugin_elementuiplugin_5181e592 === 'function') { |
||||
|
await nuxt_plugin_elementuiplugin_5181e592(app.context, inject) |
||||
|
} |
||||
|
|
||||
|
// If server-side, wait for async component to be resolved first
|
||||
|
if (process.server && ssrContext && ssrContext.url) { |
||||
|
await new Promise((resolve, reject) => { |
||||
|
router.push(ssrContext.url, resolve, () => { |
||||
|
// navigated to a different route in router guard
|
||||
|
const unregister = router.afterEach(async (to, from, next) => { |
||||
|
ssrContext.url = to.fullPath |
||||
|
app.context.route = await getRouteData(to) |
||||
|
app.context.params = to.params || {} |
||||
|
app.context.query = to.query || {} |
||||
|
unregister() |
||||
|
resolve() |
||||
|
}) |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
app, |
||||
|
router |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export { createApp, NuxtError } |
@ -0,0 +1,109 @@ |
|||||
|
<style> |
||||
|
#nuxt-loading { |
||||
|
visibility: hidden; |
||||
|
opacity: 0; |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
top: 0; |
||||
|
bottom: 0; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
flex-direction: column; |
||||
|
animation: nuxtLoadingIn 10s ease; |
||||
|
-webkit-animation: nuxtLoadingIn 10s ease; |
||||
|
animation-fill-mode: forwards; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
@keyframes nuxtLoadingIn { |
||||
|
0% { |
||||
|
visibility: hidden; |
||||
|
opacity: 0; |
||||
|
} |
||||
|
20% { |
||||
|
visibility: visible; |
||||
|
opacity: 0; |
||||
|
} |
||||
|
100% { |
||||
|
visibility: visible; |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@-webkit-keyframes nuxtLoadingIn { |
||||
|
0% { |
||||
|
visibility: hidden; |
||||
|
opacity: 0; |
||||
|
} |
||||
|
20% { |
||||
|
visibility: visible; |
||||
|
opacity: 0; |
||||
|
} |
||||
|
100% { |
||||
|
visibility: visible; |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#nuxt-loading>div, |
||||
|
#nuxt-loading>div:after { |
||||
|
border-radius: 50%; |
||||
|
width: 5rem; |
||||
|
height: 5rem; |
||||
|
} |
||||
|
|
||||
|
#nuxt-loading>div { |
||||
|
font-size: 10px; |
||||
|
position: relative; |
||||
|
text-indent: -9999em; |
||||
|
border: .5rem solid #F5F5F5; |
||||
|
border-left: .5rem solid #3B8070; |
||||
|
-webkit-transform: translateZ(0); |
||||
|
-ms-transform: translateZ(0); |
||||
|
transform: translateZ(0); |
||||
|
-webkit-animation: nuxtLoading 1.1s infinite linear; |
||||
|
animation: nuxtLoading 1.1s infinite linear; |
||||
|
} |
||||
|
|
||||
|
#nuxt-loading.error>div { |
||||
|
border-left: .5rem solid #ff4500; |
||||
|
animation-duration: 5s; |
||||
|
} |
||||
|
|
||||
|
@-webkit-keyframes nuxtLoading { |
||||
|
0% { |
||||
|
-webkit-transform: rotate(0deg); |
||||
|
transform: rotate(0deg); |
||||
|
} |
||||
|
100% { |
||||
|
-webkit-transform: rotate(360deg); |
||||
|
transform: rotate(360deg); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@keyframes nuxtLoading { |
||||
|
0% { |
||||
|
-webkit-transform: rotate(0deg); |
||||
|
transform: rotate(0deg); |
||||
|
} |
||||
|
100% { |
||||
|
-webkit-transform: rotate(360deg); |
||||
|
transform: rotate(360deg); |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
|
<script> |
||||
|
window.addEventListener('error', function () { |
||||
|
var e = document.getElementById('nuxt-loading'); |
||||
|
if (e) { |
||||
|
e.className += ' error'; |
||||
|
} |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<div id="nuxt-loading" aria-live="polite" role="status"><div>Loading...</div></div> |
||||
|
|
||||
|
<!-- https://projects.lukehaas.me/css-loaders --> |
@ -0,0 +1,3 @@ |
|||||
|
const middleware = {} |
||||
|
|
||||
|
export default middleware |
@ -0,0 +1,87 @@ |
|||||
|
import Vue from 'vue' |
||||
|
import { hasFetch, normalizeError, addLifecycleHook } from '../utils' |
||||
|
|
||||
|
const isSsrHydration = (vm) => vm.$vnode && vm.$vnode.elm && vm.$vnode.elm.dataset && vm.$vnode.elm.dataset.fetchKey |
||||
|
const nuxtState = window.__NUXT__ |
||||
|
|
||||
|
export default { |
||||
|
beforeCreate () { |
||||
|
if (!hasFetch(this)) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this._fetchDelay = typeof this.$options.fetchDelay === 'number' ? this.$options.fetchDelay : 200 |
||||
|
|
||||
|
Vue.util.defineReactive(this, '$fetchState', { |
||||
|
pending: false, |
||||
|
error: null, |
||||
|
timestamp: Date.now() |
||||
|
}) |
||||
|
|
||||
|
this.$fetch = $fetch.bind(this) |
||||
|
addLifecycleHook(this, 'created', created) |
||||
|
addLifecycleHook(this, 'beforeMount', beforeMount) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function beforeMount() { |
||||
|
if (!this._hydrated) { |
||||
|
return this.$fetch() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function created() { |
||||
|
if (!isSsrHydration(this)) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// Hydrate component
|
||||
|
this._hydrated = true |
||||
|
this._fetchKey = +this.$vnode.elm.dataset.fetchKey |
||||
|
const data = nuxtState.fetch[this._fetchKey] |
||||
|
|
||||
|
// If fetch error
|
||||
|
if (data && data._error) { |
||||
|
this.$fetchState.error = data._error |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// Merge data
|
||||
|
for (const key in data) { |
||||
|
Vue.set(this.$data, key, data[key]) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function $fetch() { |
||||
|
if (!this._fetchPromise) { |
||||
|
this._fetchPromise = $_fetch.call(this) |
||||
|
.then(() => { delete this._fetchPromise }) |
||||
|
} |
||||
|
return this._fetchPromise |
||||
|
} |
||||
|
|
||||
|
async function $_fetch() { |
||||
|
this.$nuxt.nbFetching++ |
||||
|
this.$fetchState.pending = true |
||||
|
this.$fetchState.error = null |
||||
|
this._hydrated = false |
||||
|
let error = null |
||||
|
const startTime = Date.now() |
||||
|
|
||||
|
try { |
||||
|
await this.$options.fetch.call(this) |
||||
|
} catch (err) { |
||||
|
error = normalizeError(err) |
||||
|
} |
||||
|
|
||||
|
const delayLeft = this._fetchDelay - (Date.now() - startTime) |
||||
|
if (delayLeft > 0) { |
||||
|
await new Promise(resolve => setTimeout(resolve, delayLeft)) |
||||
|
} |
||||
|
|
||||
|
this.$fetchState.error = error |
||||
|
this.$fetchState.pending = false |
||||
|
this.$fetchState.timestamp = Date.now() |
||||
|
|
||||
|
this.$nextTick(() => this.$nuxt.nbFetching--) |
||||
|
} |
@ -0,0 +1,48 @@ |
|||||
|
import Vue from 'vue' |
||||
|
import { hasFetch, normalizeError, addLifecycleHook } from '../utils' |
||||
|
|
||||
|
async function serverPrefetch() { |
||||
|
if (!this._fetchOnServer) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// Call and await on $fetch
|
||||
|
try { |
||||
|
await this.$options.fetch.call(this) |
||||
|
} catch (err) { |
||||
|
this.$fetchState.error = normalizeError(err) |
||||
|
} |
||||
|
this.$fetchState.pending = false |
||||
|
|
||||
|
// Define an ssrKey for hydration
|
||||
|
this._fetchKey = this.$ssrContext.nuxt.fetch.length |
||||
|
|
||||
|
// Add data-fetch-key on parent element of Component
|
||||
|
const attrs = this.$vnode.data.attrs = this.$vnode.data.attrs || {} |
||||
|
attrs['data-fetch-key'] = this._fetchKey |
||||
|
|
||||
|
// Add to ssrContext for window.__NUXT__.fetch
|
||||
|
this.$ssrContext.nuxt.fetch.push(this.$fetchState.error ? { _error: this.$fetchState.error } : this._data) |
||||
|
} |
||||
|
|
||||
|
export default { |
||||
|
beforeCreate() { |
||||
|
if (!hasFetch(this)) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if (typeof this.$options.fetchOnServer === 'function') { |
||||
|
this._fetchOnServer = this.$options.fetchOnServer.call(this) !== false |
||||
|
} else { |
||||
|
this._fetchOnServer = this.$options.fetchOnServer !== false |
||||
|
} |
||||
|
|
||||
|
Vue.util.defineReactive(this, '$fetchState', { |
||||
|
pending: true, |
||||
|
error: null, |
||||
|
timestamp: Date.now() |
||||
|
}) |
||||
|
|
||||
|
addLifecycleHook(this, 'serverPrefetch', serverPrefetch) |
||||
|
} |
||||
|
} |
@ -0,0 +1,60 @@ |
|||||
|
import Vue from 'vue' |
||||
|
import Router from 'vue-router' |
||||
|
import { interopDefault } from './utils' |
||||
|
import scrollBehavior from './router.scrollBehavior.js' |
||||
|
|
||||
|
const _578f272f = () => interopDefault(import('..\\pages\\club\\index.vue' /* webpackChunkName: "pages_club_index" */)) |
||||
|
const _616e5366 = () => interopDefault(import('..\\pages\\home\\index.vue' /* webpackChunkName: "pages_home_index" */)) |
||||
|
const _0612e07c = () => interopDefault(import('..\\pages\\login.vue' /* webpackChunkName: "pages_login" */)) |
||||
|
const _e5ead240 = () => interopDefault(import('..\\pages\\register.vue' /* webpackChunkName: "pages_register" */)) |
||||
|
const _0f3a46dd = () => interopDefault(import('..\\pages\\ucenter.vue' /* webpackChunkName: "pages_ucenter" */)) |
||||
|
const _3c6b3d65 = () => interopDefault(import('..\\pages\\index.vue' /* webpackChunkName: "pages_index" */)) |
||||
|
|
||||
|
// TODO: remove in Nuxt 3
|
||||
|
const emptyFn = () => {} |
||||
|
const originalPush = Router.prototype.push |
||||
|
Router.prototype.push = function push (location, onComplete = emptyFn, onAbort) { |
||||
|
return originalPush.call(this, location, onComplete, onAbort) |
||||
|
} |
||||
|
|
||||
|
Vue.use(Router) |
||||
|
|
||||
|
export const routerOptions = { |
||||
|
mode: 'history', |
||||
|
base: decodeURI('/'), |
||||
|
linkActiveClass: 'nuxt-link-active', |
||||
|
linkExactActiveClass: 'nuxt-link-exact-active', |
||||
|
scrollBehavior, |
||||
|
|
||||
|
routes: [{ |
||||
|
path: "/club", |
||||
|
component: _578f272f, |
||||
|
name: "club" |
||||
|
}, { |
||||
|
path: "/home", |
||||
|
component: _616e5366, |
||||
|
name: "home" |
||||
|
}, { |
||||
|
path: "/login", |
||||
|
component: _0612e07c, |
||||
|
name: "login" |
||||
|
}, { |
||||
|
path: "/register", |
||||
|
component: _e5ead240, |
||||
|
name: "register" |
||||
|
}, { |
||||
|
path: "/ucenter", |
||||
|
component: _0f3a46dd, |
||||
|
name: "ucenter" |
||||
|
}, { |
||||
|
path: "/", |
||||
|
component: _3c6b3d65, |
||||
|
name: "index" |
||||
|
}], |
||||
|
|
||||
|
fallback: false |
||||
|
} |
||||
|
|
||||
|
export function createRouter () { |
||||
|
return new Router(routerOptions) |
||||
|
} |
@ -0,0 +1,74 @@ |
|||||
|
import { getMatchedComponents } from './utils' |
||||
|
|
||||
|
if (process.client) { |
||||
|
if ('scrollRestoration' in window.history) { |
||||
|
window.history.scrollRestoration = 'manual' |
||||
|
|
||||
|
// reset scrollRestoration to auto when leaving page, allowing page reload
|
||||
|
// and back-navigation from other pages to use the browser to restore the
|
||||
|
// scrolling position.
|
||||
|
window.addEventListener('beforeunload', () => { |
||||
|
window.history.scrollRestoration = 'auto' |
||||
|
}) |
||||
|
|
||||
|
// Setting scrollRestoration to manual again when returning to this page.
|
||||
|
window.addEventListener('load', () => { |
||||
|
window.history.scrollRestoration = 'manual' |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default function (to, from, savedPosition) { |
||||
|
// if the returned position is falsy or an empty object,
|
||||
|
// will retain current scroll position.
|
||||
|
let position = false |
||||
|
|
||||
|
// if no children detected and scrollToTop is not explicitly disabled
|
||||
|
const Pages = getMatchedComponents(to) |
||||
|
if ( |
||||
|
Pages.length < 2 && |
||||
|
Pages.every(Page => Page.options.scrollToTop !== false) |
||||
|
) { |
||||
|
// scroll to the top of the page
|
||||
|
position = { x: 0, y: 0 } |
||||
|
} else if (Pages.some(Page => Page.options.scrollToTop)) { |
||||
|
// if one of the children has scrollToTop option set to true
|
||||
|
position = { x: 0, y: 0 } |
||||
|
} |
||||
|
|
||||
|
// savedPosition is only available for popstate navigations (back button)
|
||||
|
if (savedPosition) { |
||||
|
position = savedPosition |
||||
|
} |
||||
|
|
||||
|
const nuxt = window.$nuxt |
||||
|
|
||||
|
// triggerScroll is only fired when a new component is loaded
|
||||
|
if (to.path === from.path && to.hash !== from.hash) { |
||||
|
nuxt.$nextTick(() => nuxt.$emit('triggerScroll')) |
||||
|
} |
||||
|
|
||||
|
return new Promise((resolve) => { |
||||
|
// wait for the out transition to complete (if necessary)
|
||||
|
nuxt.$once('triggerScroll', () => { |
||||
|
// coords will be used if no selector is provided,
|
||||
|
// or if the selector didn't match any element.
|
||||
|
if (to.hash) { |
||||
|
let hash = to.hash |
||||
|
// CSS.escape() is not supported with IE and Edge.
|
||||
|
if (typeof window.CSS !== 'undefined' && typeof window.CSS.escape !== 'undefined') { |
||||
|
hash = '#' + window.CSS.escape(hash.substr(1)) |
||||
|
} |
||||
|
try { |
||||
|
if (document.querySelector(hash)) { |
||||
|
// scroll to anchor by returning the selector
|
||||
|
position = { selector: hash } |
||||
|
} |
||||
|
} catch (e) { |
||||
|
console.warn('Failed to save scroll position. Please add CSS.escape() polyfill (https://github.com/mathiasbynens/CSS.escape).') |
||||
|
} |
||||
|
} |
||||
|
resolve(position) |
||||
|
}) |
||||
|
}) |
||||
|
} |
@ -0,0 +1,265 @@ |
|||||
|
import { stringify } from 'querystring' |
||||
|
import Vue from 'vue' |
||||
|
import fetch from 'node-fetch' |
||||
|
import middleware from './middleware.js' |
||||
|
import { |
||||
|
applyAsyncData, |
||||
|
middlewareSeries, |
||||
|
sanitizeComponent, |
||||
|
getMatchedComponents, |
||||
|
promisify |
||||
|
} from './utils.js' |
||||
|
import fetchMixin from './mixins/fetch.server' |
||||
|
import { createApp, NuxtError } from './index.js' |
||||
|
import NuxtLink from './components/nuxt-link.server.js' // should be included after ./index.js
|
||||
|
|
||||
|
// Update serverPrefetch strategy
|
||||
|
Vue.config.optionMergeStrategies.serverPrefetch = Vue.config.optionMergeStrategies.created |
||||
|
|
||||
|
// Fetch mixin
|
||||
|
if (!Vue.__nuxt__fetch__mixin__) { |
||||
|
Vue.mixin(fetchMixin) |
||||
|
Vue.__nuxt__fetch__mixin__ = true |
||||
|
} |
||||
|
|
||||
|
// Component: <NuxtLink>
|
||||
|
Vue.component(NuxtLink.name, NuxtLink) |
||||
|
Vue.component('NLink', NuxtLink) |
||||
|
|
||||
|
if (!global.fetch) { global.fetch = fetch } |
||||
|
|
||||
|
const noopApp = () => new Vue({ render: h => h('div') }) |
||||
|
|
||||
|
function urlJoin () { |
||||
|
return Array.prototype.slice.call(arguments).join('/').replace(/\/+/g, '/') |
||||
|
} |
||||
|
|
||||
|
const createNext = ssrContext => (opts) => { |
||||
|
ssrContext.redirected = opts |
||||
|
// If nuxt generate
|
||||
|
if (!ssrContext.res) { |
||||
|
ssrContext.nuxt.serverRendered = false |
||||
|
return |
||||
|
} |
||||
|
opts.query = stringify(opts.query) |
||||
|
opts.path = opts.path + (opts.query ? '?' + opts.query : '') |
||||
|
const routerBase = '/' |
||||
|
if (!opts.path.startsWith('http') && (routerBase !== '/' && !opts.path.startsWith(routerBase))) { |
||||
|
opts.path = urlJoin(routerBase, opts.path) |
||||
|
} |
||||
|
// Avoid loop redirect
|
||||
|
if (opts.path === ssrContext.url) { |
||||
|
ssrContext.redirected = false |
||||
|
return |
||||
|
} |
||||
|
ssrContext.res.writeHead(opts.status, { |
||||
|
Location: opts.path |
||||
|
}) |
||||
|
ssrContext.res.end() |
||||
|
} |
||||
|
|
||||
|
// This exported function will be called by `bundleRenderer`.
|
||||
|
// This is where we perform data-prefetching to determine the
|
||||
|
// state of our application before actually rendering it.
|
||||
|
// Since data fetching is async, this function is expected to
|
||||
|
// return a Promise that resolves to the app instance.
|
||||
|
export default async (ssrContext) => { |
||||
|
// Create ssrContext.next for simulate next() of beforeEach() when wanted to redirect
|
||||
|
ssrContext.redirected = false |
||||
|
ssrContext.next = createNext(ssrContext) |
||||
|
// Used for beforeNuxtRender({ Components, nuxtState })
|
||||
|
ssrContext.beforeRenderFns = [] |
||||
|
// Nuxt object (window{{globals.context}}, defaults to window.__NUXT__)
|
||||
|
ssrContext.nuxt = { layout: 'default', data: [], fetch: [], error: null, serverRendered: true, routePath: '' } |
||||
|
// Create the app definition and the instance (created for each request)
|
||||
|
const { app, router } = await createApp(ssrContext) |
||||
|
const _app = new Vue(app) |
||||
|
// Add ssr route path to nuxt context so we can account for page navigation between ssr and csr
|
||||
|
ssrContext.nuxt.routePath = app.context.route.path |
||||
|
|
||||
|
// 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 = {} |
||||
|
|
||||
|
const beforeRender = async () => { |
||||
|
// Call beforeNuxtRender() methods
|
||||
|
await Promise.all(ssrContext.beforeRenderFns.map(fn => promisify(fn, { Components, nuxtState: ssrContext.nuxt }))) |
||||
|
} |
||||
|
|
||||
|
const renderErrorPage = async () => { |
||||
|
// Load layout for error page
|
||||
|
const layout = (NuxtError.options || NuxtError).layout |
||||
|
const errLayout = typeof layout === 'function' ? layout.call(NuxtError, app.context) : layout |
||||
|
ssrContext.nuxt.layout = errLayout || 'default' |
||||
|
await _app.loadLayout(errLayout) |
||||
|
_app.setLayout(errLayout) |
||||
|
|
||||
|
await beforeRender() |
||||
|
return _app |
||||
|
} |
||||
|
const render404Page = () => { |
||||
|
app.context.error({ statusCode: 404, path: ssrContext.url, message: 'This page could not be found' }) |
||||
|
return renderErrorPage() |
||||
|
} |
||||
|
|
||||
|
const s = Date.now() |
||||
|
|
||||
|
// Components are already resolved by setContext -> getRouteData (app/utils.js)
|
||||
|
const Components = getMatchedComponents(router.match(ssrContext.url)) |
||||
|
|
||||
|
/* |
||||
|
** Call global middleware (nuxt.config.js) |
||||
|
*/ |
||||
|
let midd = [] |
||||
|
midd = midd.map((name) => { |
||||
|
if (typeof name === 'function') { |
||||
|
return name |
||||
|
} |
||||
|
if (typeof middleware[name] !== 'function') { |
||||
|
app.context.error({ statusCode: 500, message: 'Unknown 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 renderErrorPage() |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
** Set layout |
||||
|
*/ |
||||
|
let layout = Components.length ? Components[0].options.layout : NuxtError.layout |
||||
|
if (typeof layout === 'function') { |
||||
|
layout = layout(app.context) |
||||
|
} |
||||
|
await _app.loadLayout(layout) |
||||
|
if (ssrContext.nuxt.error) { |
||||
|
return renderErrorPage() |
||||
|
} |
||||
|
layout = _app.setLayout(layout) |
||||
|
ssrContext.nuxt.layout = _app.layoutName |
||||
|
|
||||
|
/* |
||||
|
** Call middleware (layout + pages) |
||||
|
*/ |
||||
|
midd = [] |
||||
|
|
||||
|
layout = sanitizeComponent(layout) |
||||
|
if (layout.options.middleware) { |
||||
|
midd = midd.concat(layout.options.middleware) |
||||
|
} |
||||
|
|
||||
|
Components.forEach((Component) => { |
||||
|
if (Component.options.middleware) { |
||||
|
midd = midd.concat(Component.options.middleware) |
||||
|
} |
||||
|
}) |
||||
|
midd = midd.map((name) => { |
||||
|
if (typeof name === 'function') { |
||||
|
return name |
||||
|
} |
||||
|
if (typeof middleware[name] !== 'function') { |
||||
|
app.context.error({ statusCode: 500, message: 'Unknown 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 renderErrorPage() |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
** Call .validate() |
||||
|
*/ |
||||
|
let isValid = true |
||||
|
try { |
||||
|
for (const Component of Components) { |
||||
|
if (typeof Component.options.validate !== 'function') { |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
isValid = await Component.options.validate(app.context) |
||||
|
|
||||
|
if (!isValid) { |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
} catch (validationError) { |
||||
|
// ...If .validate() threw an error
|
||||
|
app.context.error({ |
||||
|
statusCode: validationError.statusCode || '500', |
||||
|
message: validationError.message |
||||
|
}) |
||||
|
return renderErrorPage() |
||||
|
} |
||||
|
|
||||
|
// ...If .validate() returned false
|
||||
|
if (!isValid) { |
||||
|
// Don't server-render the page in generate mode
|
||||
|
if (ssrContext._generate) { |
||||
|
ssrContext.nuxt.serverRendered = false |
||||
|
} |
||||
|
// Render a 404 error page
|
||||
|
return render404Page() |
||||
|
} |
||||
|
|
||||
|
// If no Components found, returns 404
|
||||
|
if (!Components.length) { |
||||
|
return render404Page() |
||||
|
} |
||||
|
|
||||
|
// Call asyncData & fetch hooks on components matched by the route.
|
||||
|
const asyncDatas = await Promise.all(Components.map((Component) => { |
||||
|
const promises = [] |
||||
|
|
||||
|
// Call asyncData(context)
|
||||
|
if (Component.options.asyncData && typeof Component.options.asyncData === 'function') { |
||||
|
const promise = promisify(Component.options.asyncData, app.context) |
||||
|
promise.then((asyncDataResult) => { |
||||
|
ssrContext.asyncData[Component.cid] = asyncDataResult |
||||
|
applyAsyncData(Component) |
||||
|
return asyncDataResult |
||||
|
}) |
||||
|
promises.push(promise) |
||||
|
} else { |
||||
|
promises.push(null) |
||||
|
} |
||||
|
|
||||
|
// Call fetch(context)
|
||||
|
if (Component.options.fetch && Component.options.fetch.length) { |
||||
|
promises.push(Component.options.fetch(app.context)) |
||||
|
} else { |
||||
|
promises.push(null) |
||||
|
} |
||||
|
|
||||
|
return Promise.all(promises) |
||||
|
})) |
||||
|
|
||||
|
if (process.env.DEBUG && asyncDatas.length) console.debug('Data fetching ' + ssrContext.url + ': ' + (Date.now() - s) + 'ms') |
||||
|
|
||||
|
// datas are the first row of each
|
||||
|
ssrContext.nuxt.data = asyncDatas.map(r => r[0] || {}) |
||||
|
|
||||
|
// ...If there is a redirect or an error, stop the process
|
||||
|
if (ssrContext.redirected) { |
||||
|
return noopApp() |
||||
|
} |
||||
|
if (ssrContext.nuxt.error) { |
||||
|
return renderErrorPage() |
||||
|
} |
||||
|
|
||||
|
// Call beforeNuxtRender methods & add store state
|
||||
|
await beforeRender() |
||||
|
|
||||
|
return _app |
||||
|
} |
@ -0,0 +1,616 @@ |
|||||
|
import Vue from 'vue' |
||||
|
|
||||
|
// window.{{globals.loadedCallback}} hook
|
||||
|
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
|
||||
|
if (process.client) { |
||||
|
window.onNuxtReadyCbs = [] |
||||
|
window.onNuxtReady = (cb) => { |
||||
|
window.onNuxtReadyCbs.push(cb) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function empty () {} |
||||
|
|
||||
|
export function globalHandleError (error) { |
||||
|
if (Vue.config.errorHandler) { |
||||
|
Vue.config.errorHandler(error) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function interopDefault (promise) { |
||||
|
return promise.then(m => m.default || m) |
||||
|
} |
||||
|
|
||||
|
export function hasFetch(vm) { |
||||
|
return vm.$options && typeof vm.$options.fetch === 'function' && !vm.$options.fetch.length |
||||
|
} |
||||
|
export function getChildrenComponentInstancesUsingFetch(vm, instances = []) { |
||||
|
const children = vm.$children || [] |
||||
|
for (const child of children) { |
||||
|
if (child.$fetch) { |
||||
|
instances.push(child) |
||||
|
continue; // Don't get the children since it will reload the template
|
||||
|
} |
||||
|
if (child.$children) { |
||||
|
getChildrenComponentInstancesUsingFetch(child, instances) |
||||
|
} |
||||
|
} |
||||
|
return instances |
||||
|
} |
||||
|
|
||||
|
export function applyAsyncData (Component, asyncData) { |
||||
|
if ( |
||||
|
// For SSR, we once all this function without second param to just apply asyncData
|
||||
|
// Prevent doing this for each SSR request
|
||||
|
!asyncData && Component.options.__hasNuxtData |
||||
|
) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
const ComponentData = Component.options._originDataFn || Component.options.data || function () { return {} } |
||||
|
Component.options._originDataFn = ComponentData |
||||
|
|
||||
|
Component.options.data = function () { |
||||
|
const data = ComponentData.call(this, this) |
||||
|
if (this.$ssrContext) { |
||||
|
asyncData = this.$ssrContext.asyncData[Component.cid] |
||||
|
} |
||||
|
return { ...data, ...asyncData } |
||||
|
} |
||||
|
|
||||
|
Component.options.__hasNuxtData = true |
||||
|
|
||||
|
if (Component._Ctor && Component._Ctor.options) { |
||||
|
Component._Ctor.options.data = Component.options.data |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function sanitizeComponent (Component) { |
||||
|
// If Component already sanitized
|
||||
|
if (Component.options && Component._Ctor === Component) { |
||||
|
return Component |
||||
|
} |
||||
|
if (!Component.options) { |
||||
|
Component = Vue.extend(Component) // fix issue #6
|
||||
|
Component._Ctor = Component |
||||
|
} else { |
||||
|
Component._Ctor = Component |
||||
|
Component.extendOptions = Component.options |
||||
|
} |
||||
|
// If no component name defined, set file path as name, (also fixes #5703)
|
||||
|
if (!Component.options.name && Component.options.__file) { |
||||
|
Component.options.name = Component.options.__file |
||||
|
} |
||||
|
return Component |
||||
|
} |
||||
|
|
||||
|
export function getMatchedComponents (route, matches = false, prop = 'components') { |
||||
|
return Array.prototype.concat.apply([], route.matched.map((m, index) => { |
||||
|
return Object.keys(m[prop]).map((key) => { |
||||
|
matches && matches.push(index) |
||||
|
return m[prop][key] |
||||
|
}) |
||||
|
})) |
||||
|
} |
||||
|
|
||||
|
export function getMatchedComponentsInstances (route, matches = false) { |
||||
|
return getMatchedComponents(route, matches, 'instances') |
||||
|
} |
||||
|
|
||||
|
export function flatMapComponents (route, fn) { |
||||
|
return Array.prototype.concat.apply([], route.matched.map((m, index) => { |
||||
|
return Object.keys(m.components).reduce((promises, key) => { |
||||
|
if (m.components[key]) { |
||||
|
promises.push(fn(m.components[key], m.instances[key], m, key, index)) |
||||
|
} else { |
||||
|
delete m.components[key] |
||||
|
} |
||||
|
return promises |
||||
|
}, []) |
||||
|
})) |
||||
|
} |
||||
|
|
||||
|
export function resolveRouteComponents (route, fn) { |
||||
|
return Promise.all( |
||||
|
flatMapComponents(route, async (Component, instance, match, key) => { |
||||
|
// If component is a function, resolve it
|
||||
|
if (typeof Component === 'function' && !Component.options) { |
||||
|
Component = await Component() |
||||
|
} |
||||
|
match.components[key] = Component = sanitizeComponent(Component) |
||||
|
return typeof fn === 'function' ? fn(Component, instance, match, key) : Component |
||||
|
}) |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export async function getRouteData (route) { |
||||
|
if (!route) { |
||||
|
return |
||||
|
} |
||||
|
// 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, index) => { |
||||
|
return { ...Component.options.meta, ...(route.matched[index] || {}).meta } |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export async function setContext (app, context) { |
||||
|
// If context not defined, create it
|
||||
|
if (!app.context) { |
||||
|
app.context = { |
||||
|
isStatic: process.static, |
||||
|
isDev: true, |
||||
|
isHMR: false, |
||||
|
app, |
||||
|
|
||||
|
payload: context.payload, |
||||
|
error: context.error, |
||||
|
base: '/', |
||||
|
env: {} |
||||
|
} |
||||
|
// Only set once
|
||||
|
if (context.req) { |
||||
|
app.context.req = context.req |
||||
|
} |
||||
|
if (context.res) { |
||||
|
app.context.res = context.res |
||||
|
} |
||||
|
if (context.ssrContext) { |
||||
|
app.context.ssrContext = context.ssrContext |
||||
|
} |
||||
|
app.context.redirect = (status, path, query) => { |
||||
|
if (!status) { |
||||
|
return |
||||
|
} |
||||
|
app.context._redirected = true |
||||
|
// if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
|
||||
|
let pathType = typeof path |
||||
|
if (typeof status !== 'number' && (pathType === 'undefined' || pathType === 'object')) { |
||||
|
query = path || {} |
||||
|
path = status |
||||
|
pathType = typeof path |
||||
|
status = 302 |
||||
|
} |
||||
|
if (pathType === 'object') { |
||||
|
path = app.router.resolve(path).route.fullPath |
||||
|
} |
||||
|
// "/absolute/route", "./relative/route" or "../relative/route"
|
||||
|
if (/(^[.]{1,2}\/)|(^\/(?!\/))/.test(path)) { |
||||
|
app.context.next({ |
||||
|
path, |
||||
|
query, |
||||
|
status |
||||
|
}) |
||||
|
} else { |
||||
|
path = formatUrl(path, query) |
||||
|
if (process.server) { |
||||
|
app.context.next({ |
||||
|
path, |
||||
|
status |
||||
|
}) |
||||
|
} |
||||
|
if (process.client) { |
||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/Location/replace
|
||||
|
window.location.replace(path) |
||||
|
|
||||
|
// Throw a redirect error
|
||||
|
throw new Error('ERR_REDIRECT') |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if (process.server) { |
||||
|
app.context.beforeNuxtRender = fn => context.beforeRenderFns.push(fn) |
||||
|
} |
||||
|
if (process.client) { |
||||
|
app.context.nuxtState = window.__NUXT__ |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Dynamic keys
|
||||
|
const [currentRouteData, fromRouteData] = await Promise.all([ |
||||
|
getRouteData(context.route), |
||||
|
getRouteData(context.from) |
||||
|
]) |
||||
|
|
||||
|
if (context.route) { |
||||
|
app.context.route = currentRouteData |
||||
|
} |
||||
|
|
||||
|
if (context.from) { |
||||
|
app.context.from = fromRouteData |
||||
|
} |
||||
|
|
||||
|
app.context.next = context.next |
||||
|
app.context._redirected = false |
||||
|
app.context._errored = false |
||||
|
app.context.isHMR = Boolean(context.isHMR) |
||||
|
app.context.params = app.context.route.params || {} |
||||
|
app.context.query = app.context.route.query || {} |
||||
|
} |
||||
|
|
||||
|
export function middlewareSeries (promises, appContext) { |
||||
|
if (!promises.length || appContext._redirected || appContext._errored) { |
||||
|
return Promise.resolve() |
||||
|
} |
||||
|
return promisify(promises[0], appContext) |
||||
|
.then(() => { |
||||
|
return middlewareSeries(promises.slice(1), appContext) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
export function promisify (fn, context) { |
||||
|
let promise |
||||
|
if (fn.length === 2) { |
||||
|
console.warn('Callback-based asyncData, fetch or middleware calls are deprecated. ' + |
||||
|
'Please switch to promises or async/await syntax') |
||||
|
|
||||
|
// fn(context, callback)
|
||||
|
promise = new Promise((resolve) => { |
||||
|
fn(context, function (err, data) { |
||||
|
if (err) { |
||||
|
context.error(err) |
||||
|
} |
||||
|
data = data || {} |
||||
|
resolve(data) |
||||
|
}) |
||||
|
}) |
||||
|
} else { |
||||
|
promise = fn(context) |
||||
|
} |
||||
|
|
||||
|
if (promise && promise instanceof Promise && typeof promise.then === 'function') { |
||||
|
return promise |
||||
|
} |
||||
|
return Promise.resolve(promise) |
||||
|
} |
||||
|
|
||||
|
// Imported from vue-router
|
||||
|
export function getLocation (base, mode) { |
||||
|
let path = decodeURI(window.location.pathname) |
||||
|
if (mode === 'hash') { |
||||
|
return window.location.hash.replace(/^#\//, '') |
||||
|
} |
||||
|
if (base && path.indexOf(base) === 0) { |
||||
|
path = path.slice(base.length) |
||||
|
} |
||||
|
return (path || '/') + window.location.search + window.location.hash |
||||
|
} |
||||
|
|
||||
|
// Imported from path-to-regexp
|
||||
|
|
||||
|
/** |
||||
|
* Compile a string to a template function for the path. |
||||
|
* |
||||
|
* @param {string} str |
||||
|
* @param {Object=} options |
||||
|
* @return {!function(Object=, Object=)} |
||||
|
*/ |
||||
|
export function compile (str, options) { |
||||
|
return tokensToFunction(parse(str, options), options) |
||||
|
} |
||||
|
|
||||
|
export function getQueryDiff (toQuery, fromQuery) { |
||||
|
const diff = {} |
||||
|
const queries = { ...toQuery, ...fromQuery } |
||||
|
for (const k in queries) { |
||||
|
if (String(toQuery[k]) !== String(fromQuery[k])) { |
||||
|
diff[k] = true |
||||
|
} |
||||
|
} |
||||
|
return diff |
||||
|
} |
||||
|
|
||||
|
export function normalizeError (err) { |
||||
|
let message |
||||
|
if (!(err.message || typeof err === 'string')) { |
||||
|
try { |
||||
|
message = JSON.stringify(err, null, 2) |
||||
|
} catch (e) { |
||||
|
message = `[${err.constructor.name}]` |
||||
|
} |
||||
|
} else { |
||||
|
message = err.message || err |
||||
|
} |
||||
|
return { |
||||
|
...err, |
||||
|
message, |
||||
|
statusCode: (err.statusCode || err.status || (err.response && err.response.status) || 500) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* The main path matching regexp utility. |
||||
|
* |
||||
|
* @type {RegExp} |
||||
|
*/ |
||||
|
const PATH_REGEXP = new RegExp([ |
||||
|
// Match escaped characters that would otherwise appear in future matches.
|
||||
|
// This allows the user to escape special characters that won't transform.
|
||||
|
'(\\\\.)', |
||||
|
// Match Express-style parameters and un-named parameters with a prefix
|
||||
|
// and optional suffixes. Matches appear as:
|
||||
|
//
|
||||
|
// "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined]
|
||||
|
// "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined]
|
||||
|
// "/*" => ["/", undefined, undefined, undefined, undefined, "*"]
|
||||
|
'([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))' |
||||
|
].join('|'), 'g') |
||||
|
|
||||
|
/** |
||||
|
* Parse a string for the raw tokens. |
||||
|
* |
||||
|
* @param {string} str |
||||
|
* @param {Object=} options |
||||
|
* @return {!Array} |
||||
|
*/ |
||||
|
function parse (str, options) { |
||||
|
const tokens = [] |
||||
|
let key = 0 |
||||
|
let index = 0 |
||||
|
let path = '' |
||||
|
const defaultDelimiter = (options && options.delimiter) || '/' |
||||
|
let res |
||||
|
|
||||
|
while ((res = PATH_REGEXP.exec(str)) != null) { |
||||
|
const m = res[0] |
||||
|
const escaped = res[1] |
||||
|
const offset = res.index |
||||
|
path += str.slice(index, offset) |
||||
|
index = offset + m.length |
||||
|
|
||||
|
// Ignore already escaped sequences.
|
||||
|
if (escaped) { |
||||
|
path += escaped[1] |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
const next = str[index] |
||||
|
const prefix = res[2] |
||||
|
const name = res[3] |
||||
|
const capture = res[4] |
||||
|
const group = res[5] |
||||
|
const modifier = res[6] |
||||
|
const asterisk = res[7] |
||||
|
|
||||
|
// Push the current path onto the tokens.
|
||||
|
if (path) { |
||||
|
tokens.push(path) |
||||
|
path = '' |
||||
|
} |
||||
|
|
||||
|
const partial = prefix != null && next != null && next !== prefix |
||||
|
const repeat = modifier === '+' || modifier === '*' |
||||
|
const optional = modifier === '?' || modifier === '*' |
||||
|
const delimiter = res[2] || defaultDelimiter |
||||
|
const pattern = capture || group |
||||
|
|
||||
|
tokens.push({ |
||||
|
name: name || key++, |
||||
|
prefix: prefix || '', |
||||
|
delimiter, |
||||
|
optional, |
||||
|
repeat, |
||||
|
partial, |
||||
|
asterisk: Boolean(asterisk), |
||||
|
pattern: pattern ? escapeGroup(pattern) : (asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?') |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// Match any characters still remaining.
|
||||
|
if (index < str.length) { |
||||
|
path += str.substr(index) |
||||
|
} |
||||
|
|
||||
|
// If the path exists, push it onto the end.
|
||||
|
if (path) { |
||||
|
tokens.push(path) |
||||
|
} |
||||
|
|
||||
|
return tokens |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Prettier encoding of URI path segments. |
||||
|
* |
||||
|
* @param {string} |
||||
|
* @return {string} |
||||
|
*/ |
||||
|
function encodeURIComponentPretty (str, slashAllowed) { |
||||
|
const re = slashAllowed ? /[?#]/g : /[/?#]/g |
||||
|
return encodeURI(str).replace(re, (c) => { |
||||
|
return '%' + c.charCodeAt(0).toString(16).toUpperCase() |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Encode the asterisk parameter. Similar to `pretty`, but allows slashes. |
||||
|
* |
||||
|
* @param {string} |
||||
|
* @return {string} |
||||
|
*/ |
||||
|
function encodeAsterisk (str) { |
||||
|
return encodeURIComponentPretty(str, true) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Escape a regular expression string. |
||||
|
* |
||||
|
* @param {string} str |
||||
|
* @return {string} |
||||
|
*/ |
||||
|
function escapeString (str) { |
||||
|
return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1') |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Escape the capturing group by escaping special characters and meaning. |
||||
|
* |
||||
|
* @param {string} group |
||||
|
* @return {string} |
||||
|
*/ |
||||
|
function escapeGroup (group) { |
||||
|
return group.replace(/([=!:$/()])/g, '\\$1') |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Expose a method for transforming tokens into the path function. |
||||
|
*/ |
||||
|
function tokensToFunction (tokens, options) { |
||||
|
// Compile all the tokens into regexps.
|
||||
|
const matches = new Array(tokens.length) |
||||
|
|
||||
|
// Compile all the patterns before compilation.
|
||||
|
for (let i = 0; i < tokens.length; i++) { |
||||
|
if (typeof tokens[i] === 'object') { |
||||
|
matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$', flags(options)) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return function (obj, opts) { |
||||
|
let path = '' |
||||
|
const data = obj || {} |
||||
|
const options = opts || {} |
||||
|
const encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent |
||||
|
|
||||
|
for (let i = 0; i < tokens.length; i++) { |
||||
|
const token = tokens[i] |
||||
|
|
||||
|
if (typeof token === 'string') { |
||||
|
path += token |
||||
|
|
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
const value = data[token.name || 'pathMatch'] |
||||
|
let segment |
||||
|
|
||||
|
if (value == null) { |
||||
|
if (token.optional) { |
||||
|
// Prepend partial segment prefixes.
|
||||
|
if (token.partial) { |
||||
|
path += token.prefix |
||||
|
} |
||||
|
|
||||
|
continue |
||||
|
} else { |
||||
|
throw new TypeError('Expected "' + token.name + '" to be defined') |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Array.isArray(value)) { |
||||
|
if (!token.repeat) { |
||||
|
throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`') |
||||
|
} |
||||
|
|
||||
|
if (value.length === 0) { |
||||
|
if (token.optional) { |
||||
|
continue |
||||
|
} else { |
||||
|
throw new TypeError('Expected "' + token.name + '" to not be empty') |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
for (let j = 0; j < value.length; j++) { |
||||
|
segment = encode(value[j]) |
||||
|
|
||||
|
if (!matches[i].test(segment)) { |
||||
|
throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`') |
||||
|
} |
||||
|
|
||||
|
path += (j === 0 ? token.prefix : token.delimiter) + segment |
||||
|
} |
||||
|
|
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
segment = token.asterisk ? encodeAsterisk(value) : encode(value) |
||||
|
|
||||
|
if (!matches[i].test(segment)) { |
||||
|
throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"') |
||||
|
} |
||||
|
|
||||
|
path += token.prefix + segment |
||||
|
} |
||||
|
|
||||
|
return path |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get the flags for a regexp from the options. |
||||
|
* |
||||
|
* @param {Object} options |
||||
|
* @return {string} |
||||
|
*/ |
||||
|
function flags (options) { |
||||
|
return options && options.sensitive ? '' : 'i' |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Format given url, append query to url query string |
||||
|
* |
||||
|
* @param {string} url |
||||
|
* @param {string} query |
||||
|
* @return {string} |
||||
|
*/ |
||||
|
function formatUrl (url, query) { |
||||
|
let protocol |
||||
|
const index = url.indexOf('://') |
||||
|
if (index !== -1) { |
||||
|
protocol = url.substring(0, index) |
||||
|
url = url.substring(index + 3) |
||||
|
} else if (url.startsWith('//')) { |
||||
|
url = url.substring(2) |
||||
|
} |
||||
|
|
||||
|
let parts = url.split('/') |
||||
|
let result = (protocol ? protocol + '://' : '//') + parts.shift() |
||||
|
|
||||
|
let path = parts.filter(Boolean).join('/') |
||||
|
let hash |
||||
|
parts = path.split('#') |
||||
|
if (parts.length === 2) { |
||||
|
[path, hash] = parts |
||||
|
} |
||||
|
|
||||
|
result += path ? '/' + path : '' |
||||
|
|
||||
|
if (query && JSON.stringify(query) !== '{}') { |
||||
|
result += (url.split('?').length === 2 ? '&' : '?') + formatQuery(query) |
||||
|
} |
||||
|
result += hash ? '#' + hash : '' |
||||
|
|
||||
|
return result |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Transform data object to query string |
||||
|
* |
||||
|
* @param {object} query |
||||
|
* @return {string} |
||||
|
*/ |
||||
|
function formatQuery (query) { |
||||
|
return Object.keys(query).sort().map((key) => { |
||||
|
const val = query[key] |
||||
|
if (val == null) { |
||||
|
return '' |
||||
|
} |
||||
|
if (Array.isArray(val)) { |
||||
|
return val.slice().map(val2 => [key, '=', val2].join('')).join('&') |
||||
|
} |
||||
|
return key + '=' + val |
||||
|
}).filter(Boolean).join('&') |
||||
|
} |
||||
|
|
||||
|
export function addLifecycleHook(vm, hook, fn) { |
||||
|
if (!vm.$options[hook]) { |
||||
|
vm.$options[hook] = [] |
||||
|
} |
||||
|
if (!vm.$options[hook].includes(fn)) { |
||||
|
vm.$options[hook].push(fn) |
||||
|
} |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html {{ HTML_ATTRS }}> |
||||
|
<head {{ HEAD_ATTRS }}> |
||||
|
{{ HEAD }} |
||||
|
</head> |
||||
|
<body {{ BODY_ATTRS }}> |
||||
|
{{ APP }} |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,23 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html> |
||||
|
<head> |
||||
|
<title>Server error</title> |
||||
|
<meta charset="utf-8"> |
||||
|
<meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" name=viewport> |
||||
|
<style> |
||||
|
.__nuxt-error-page{padding: 1rem;background:#f7f8fb;color:#47494e;text-align:center;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;font-family:sans-serif;font-weight:100!important;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased;position:absolute;top:0;left:0;right:0;bottom:0}.__nuxt-error-page .error{max-width:450px}.__nuxt-error-page .title{font-size:24px;font-size:1.5rem;margin-top:15px;color:#47494e;margin-bottom:8px}.__nuxt-error-page .description{color:#7f828b;line-height:21px;margin-bottom:10px}.__nuxt-error-page a{color:#7f828b!important;text-decoration:none}.__nuxt-error-page .logo{position:fixed;left:12px;bottom:12px} |
||||
|
</style> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div class="__nuxt-error-page"> |
||||
|
<div class="error"> |
||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="90" height="90" fill="#DBE1EC" viewBox="0 0 48 48"><path d="M22 30h4v4h-4zm0-16h4v12h-4zm1.99-10C12.94 4 4 12.95 4 24s8.94 20 19.99 20S44 35.05 44 24 35.04 4 23.99 4zM24 40c-8.84 0-16-7.16-16-16S15.16 8 24 8s16 7.16 16 16-7.16 16-16 16z"/></svg> |
||||
|
<div class="title">Server error</div> |
||||
|
<div class="description">{{ message }}</div> |
||||
|
</div> |
||||
|
<div class="logo"> |
||||
|
<a href="https://nuxtjs.org" target="_blank" rel="noopener">Nuxt.js</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</body> |
||||
|
</html> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue