28 changed files with 3603 additions and 0 deletions
@ -0,0 +1,183 @@
@@ -0,0 +1,183 @@
|
||||
import Vue from 'vue' |
||||
import { decode, parsePath, withoutBase, withoutTrailingSlash, normalizeURL } from 'ufo' |
||||
|
||||
import { getMatchedComponentsInstances, getChildrenComponentInstancesUsingFetch, promisify, globalHandleError, urlJoin, sanitizeComponent } from './utils' |
||||
import NuxtError from '../src/layouts/error.vue' |
||||
|
||||
import '../node_modules/element-ui/lib/theme-chalk/index.css' |
||||
|
||||
import '../node_modules/video.js/dist/video-js.css' |
||||
|
||||
import '../node_modules/vue-video-player/src/custom-theme.css' |
||||
|
||||
import _6dce6561 from '../src/layouts/common.vue' |
||||
import _6ef953c7 from '../src/layouts/detail.vue' |
||||
import _6f6c098b from './layouts/default.vue' |
||||
|
||||
const layouts = { "_common": sanitizeComponent(_6dce6561),"_detail": sanitizeComponent(_6ef953c7),"_default": sanitizeComponent(_6f6c098b) } |
||||
|
||||
export default { |
||||
render (h, props) { |
||||
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' |
||||
} |
||||
}, [ |
||||
|
||||
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
|
||||
this.$root.$options.$nuxt = this |
||||
|
||||
if (process.client) { |
||||
// add to window so we can listen when ready
|
||||
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 |
||||
}, |
||||
|
||||
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 |
||||
} |
||||
|
||||
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) { |
||||
globalHandleError(error) |
||||
this.error(error) |
||||
} |
||||
}, |
||||
errorChanged () { |
||||
if (this.nuxt.err) { |
||||
let errorLayout = (NuxtError.options || NuxtError).layout; |
||||
|
||||
if (typeof errorLayout === 'function') { |
||||
errorLayout = errorLayout(this.context) |
||||
} |
||||
|
||||
this.setLayout(errorLayout) |
||||
} |
||||
}, |
||||
|
||||
setLayout (layout) { |
||||
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]) |
||||
}, |
||||
}, |
||||
} |
@ -0,0 +1,205 @@
@@ -0,0 +1,205 @@
|
||||
import Axios from 'axios' |
||||
import defu from 'defu' |
||||
|
||||
// Axios.prototype cannot be modified
|
||||
const axiosExtra = { |
||||
setBaseURL (baseURL) { |
||||
this.defaults.baseURL = baseURL |
||||
}, |
||||
setHeader (name, value, scopes = 'common') { |
||||
for (const scope of Array.isArray(scopes) ? scopes : [ scopes ]) { |
||||
if (!value) { |
||||
delete this.defaults.headers[scope][name]; |
||||
continue |
||||
} |
||||
this.defaults.headers[scope][name] = value |
||||
} |
||||
}, |
||||
setToken (token, type, scopes = 'common') { |
||||
const value = !token ? null : (type ? type + ' ' : '') + token |
||||
this.setHeader('Authorization', value, scopes) |
||||
}, |
||||
onRequest(fn) { |
||||
this.interceptors.request.use(config => fn(config) || config) |
||||
}, |
||||
onResponse(fn) { |
||||
this.interceptors.response.use(response => fn(response) || response) |
||||
}, |
||||
onRequestError(fn) { |
||||
this.interceptors.request.use(undefined, error => fn(error) || Promise.reject(error)) |
||||
}, |
||||
onResponseError(fn) { |
||||
this.interceptors.response.use(undefined, error => fn(error) || Promise.reject(error)) |
||||
}, |
||||
onError(fn) { |
||||
this.onRequestError(fn) |
||||
this.onResponseError(fn) |
||||
}, |
||||
create(options) { |
||||
return createAxiosInstance(defu(options, this.defaults)) |
||||
} |
||||
} |
||||
|
||||
// Request helpers ($get, $post, ...)
|
||||
for (const method of ['request', 'delete', 'get', 'head', 'options', 'post', 'put', 'patch']) { |
||||
axiosExtra['$' + method] = function () { return this[method].apply(this, arguments).then(res => res && res.data) } |
||||
} |
||||
|
||||
const extendAxiosInstance = axios => { |
||||
for (const key in axiosExtra) { |
||||
axios[key] = axiosExtra[key].bind(axios) |
||||
} |
||||
} |
||||
|
||||
const createAxiosInstance = axiosOptions => { |
||||
// Create new axios instance
|
||||
const axios = Axios.create(axiosOptions) |
||||
axios.CancelToken = Axios.CancelToken |
||||
axios.isCancel = Axios.isCancel |
||||
|
||||
// Extend axios proto
|
||||
extendAxiosInstance(axios) |
||||
|
||||
// Intercept to apply default headers
|
||||
axios.onRequest((config) => { |
||||
config.headers = { ...axios.defaults.headers.common, ...config.headers } |
||||
}) |
||||
|
||||
// Setup interceptors
|
||||
|
||||
setupCredentialsInterceptor(axios) |
||||
setupProgress(axios) |
||||
|
||||
return axios |
||||
} |
||||
|
||||
const setupCredentialsInterceptor = axios => { |
||||
// Send credentials only to relative and API Backend requests
|
||||
axios.onRequest(config => { |
||||
if (config.withCredentials === undefined) { |
||||
if (!/^https?:\/\//i.test(config.url) || config.url.indexOf(config.baseURL) === 0) { |
||||
config.withCredentials = true |
||||
} |
||||
} |
||||
}) |
||||
} |
||||
|
||||
const setupProgress = (axios) => { |
||||
if (process.server) { |
||||
return |
||||
} |
||||
|
||||
// A noop loading inteterface for when $nuxt is not yet ready
|
||||
const noopLoading = { |
||||
finish: () => { }, |
||||
start: () => { }, |
||||
fail: () => { }, |
||||
set: () => { } |
||||
} |
||||
|
||||
const $loading = () => { |
||||
const $nuxt = typeof window !== 'undefined' && window['$nuxt'] |
||||
return ($nuxt && $nuxt.$loading && $nuxt.$loading.set) ? $nuxt.$loading : noopLoading |
||||
} |
||||
|
||||
let currentRequests = 0 |
||||
|
||||
axios.onRequest(config => { |
||||
if (config && config.progress === false) { |
||||
return |
||||
} |
||||
|
||||
currentRequests++ |
||||
}) |
||||
|
||||
axios.onResponse(response => { |
||||
if (response && response.config && response.config.progress === false) { |
||||
return |
||||
} |
||||
|
||||
currentRequests-- |
||||
if (currentRequests <= 0) { |
||||
currentRequests = 0 |
||||
$loading().finish() |
||||
} |
||||
}) |
||||
|
||||
axios.onError(error => { |
||||
if (error && error.config && error.config.progress === false) { |
||||
return |
||||
} |
||||
|
||||
currentRequests-- |
||||
|
||||
if (Axios.isCancel(error)) { |
||||
if (currentRequests <= 0) { |
||||
currentRequests = 0 |
||||
$loading().finish() |
||||
} |
||||
return |
||||
} |
||||
|
||||
$loading().fail() |
||||
$loading().finish() |
||||
}) |
||||
|
||||
const onProgress = e => { |
||||
if (!currentRequests || !e.total) { |
||||
return |
||||
} |
||||
const progress = ((e.loaded * 100) / (e.total * currentRequests)) |
||||
$loading().set(Math.min(100, progress)) |
||||
} |
||||
|
||||
axios.defaults.onUploadProgress = onProgress |
||||
axios.defaults.onDownloadProgress = onProgress |
||||
} |
||||
|
||||
export default (ctx, inject) => { |
||||
// runtimeConfig
|
||||
const runtimeConfig = ctx.$config && ctx.$config.axios || {} |
||||
// baseURL
|
||||
const baseURL = process.browser |
||||
? (runtimeConfig.browserBaseURL || runtimeConfig.browserBaseUrl || runtimeConfig.baseURL || runtimeConfig.baseUrl || 'http://localhost:3000/web') |
||||
: (runtimeConfig.baseURL || runtimeConfig.baseUrl || process.env._AXIOS_BASE_URL_ || 'http://localhost:3000/web') |
||||
|
||||
// Create fresh objects for all default header scopes
|
||||
// Axios creates only one which is shared across SSR requests!
|
||||
// https://github.com/mzabriskie/axios/blob/master/lib/defaults.js
|
||||
const headers = { |
||||
"common": { |
||||
"Accept": "application/json, text/plain, */*" |
||||
}, |
||||
"delete": {}, |
||||
"get": {}, |
||||
"head": {}, |
||||
"post": {}, |
||||
"put": {}, |
||||
"patch": {} |
||||
} |
||||
|
||||
const axiosOptions = { |
||||
baseURL, |
||||
headers |
||||
} |
||||
|
||||
// Proxy SSR request headers headers
|
||||
if (process.server && ctx.req && ctx.req.headers) { |
||||
const reqHeaders = { ...ctx.req.headers } |
||||
for (const h of ["accept","cf-connecting-ip","cf-ray","content-length","content-md5","content-type","host","x-forwarded-host","x-forwarded-port","x-forwarded-proto"]) { |
||||
delete reqHeaders[h] |
||||
} |
||||
axiosOptions.headers.common = { ...reqHeaders, ...axiosOptions.headers.common } |
||||
} |
||||
|
||||
if (process.server) { |
||||
// Don't accept brotli encoding because Node can't parse it
|
||||
axiosOptions.headers.common['accept-encoding'] = 'gzip, deflate' |
||||
} |
||||
|
||||
const axios = createAxiosInstance(axiosOptions) |
||||
|
||||
// Inject axios to the context as $axios
|
||||
ctx.$axios = axios |
||||
inject('axios', axios) |
||||
} |
@ -0,0 +1,617 @@
@@ -0,0 +1,617 @@
|
||||
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, |
||||
isSamePath, |
||||
urlJoin |
||||
} 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 |
||||
let store |
||||
|
||||
// Try to rehydrate SSR data from window
|
||||
const NUXT = window.__NUXT__ || {} |
||||
|
||||
const $config = NUXT.config || {} |
||||
if ($config._app) { |
||||
__webpack_public_path__ = urlJoin($config._app.cdnURL, $config._app.assetsPath) |
||||
} |
||||
|
||||
Object.assign(Vue.config, {"silent":true,"performance":false}) |
||||
|
||||
const errorHandler = Vue.config.errorHandler || console.error |
||||
|
||||
// Create and mount App
|
||||
createApp(null, NUXT.config).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) : []) |
||||
|
||||
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 |
||||
}) |
||||
} |
||||
// 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 (route) { |
||||
return flatMapComponents(route, 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 = ["device"] |
||||
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
|
||||
let spaFallback = false |
||||
if (to === from) { |
||||
_lastPaths = [] |
||||
spaFallback = true |
||||
} 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 (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(async (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 |
||||
|
||||
// Call asyncData(context)
|
||||
if (hasAsyncData) { |
||||
const promise = promisify(Component.options.asyncData, app.context) |
||||
|
||||
promise.then((asyncDataResult) => { |
||||
applyAsyncData(Component, asyncDataResult) |
||||
}) |
||||
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) => { |
||||
}) |
||||
promises.push(p) |
||||
} |
||||
|
||||
return Promise.all(promises) |
||||
})) |
||||
|
||||
// If not redirected
|
||||
if (!nextCalled) { |
||||
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 setLayoutForNextPage (to) { |
||||
// Set layout
|
||||
let hasError = Boolean(this.$options.nuxt.err) |
||||
if (this._hadError && this._dateLastError === this.$options.nuxt.dateErr) { |
||||
hasError = false |
||||
} |
||||
let layout = hasError |
||||
? (NuxtError.options || NuxtError).layout |
||||
: to.matched[0].components.default.options.layout |
||||
|
||||
if (typeof layout === 'function') { |
||||
layout = layout(app.context) |
||||
} |
||||
|
||||
this.setLayout(layout) |
||||
} |
||||
|
||||
function checkForErrors (app) { |
||||
// Hide error component if no error
|
||||
if (app._hadError && app._dateLastError === app.$options.nuxt.dateErr) { |
||||
app.error() |
||||
} |
||||
} |
||||
|
||||
// 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) |
||||
|
||||
let triggerScroll = false |
||||
|
||||
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]) |
||||
} |
||||
|
||||
triggerScroll = true |
||||
} |
||||
}) |
||||
|
||||
if (triggerScroll) { |
||||
// Ensure to trigger scroll event after calling scrollBehavior
|
||||
window.$nuxt.$nextTick(() => { |
||||
window.$nuxt.$emit('triggerScroll') |
||||
}) |
||||
} |
||||
|
||||
checkForErrors(this) |
||||
}) |
||||
} |
||||
|
||||
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)) |
||||
}) |
||||
} |
||||
|
||||
async function mountApp (__app) { |
||||
// Set global variables
|
||||
app = __app.app |
||||
router = __app.router |
||||
store = __app.store |
||||
|
||||
// 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(setLayoutForNextPage.bind(_app)) |
||||
|
||||
router.afterEach(fixPrepatch.bind(_app)) |
||||
|
||||
// Listen for first Vue update
|
||||
Vue.nextTick(() => { |
||||
// Call window.{{globals.readyCallback}} callbacks
|
||||
nuxtReady(_app) |
||||
}) |
||||
} |
||||
|
||||
// Resolve route components
|
||||
const Components = await Promise.all(resolveComponents(app.context.route)) |
||||
|
||||
// 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)) |
||||
|
||||
// Fix in static: remove trailing slash to force hydration
|
||||
// Full static, if server-rendered: hydrate, to allow custom redirect to generated page
|
||||
|
||||
// Fix in static: remove trailing slash to force hydration
|
||||
if (NUXT.serverRendered && isSamePath(NUXT.routePath, _app.context.route.path)) { |
||||
return mount() |
||||
} |
||||
|
||||
// First render on client-side
|
||||
const clientFirstMount = () => { |
||||
normalizeComponents(router.currentRoute, router.currentRoute) |
||||
setLayoutForNextPage.call(_app, router.currentRoute) |
||||
checkForErrors(_app) |
||||
// Don't call fixPrepatch.call(_app, router.currentRoute, router.currentRoute) since it's first render
|
||||
mount() |
||||
} |
||||
|
||||
// fix: force next tick to avoid having same timestamp when an error happen on spa fallback
|
||||
await new Promise(resolve => setTimeout(resolve, 0)) |
||||
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,42 @@
@@ -0,0 +1,42 @@
|
||||
export const Barrage = () => import('../../src/components/Barrage.vue' /* webpackChunkName: "components/barrage" */).then(c => wrapFunctional(c.default || c)) |
||||
export const BottomRightFixed = () => import('../../src/components/BottomRightFixed.vue' /* webpackChunkName: "components/bottom-right-fixed" */).then(c => wrapFunctional(c.default || c)) |
||||
export const CommonHeader = () => import('../../src/components/CommonHeader.vue' /* webpackChunkName: "components/common-header" */).then(c => wrapFunctional(c.default || c)) |
||||
export const DoctorItem = () => import('../../src/components/DoctorItem.vue' /* webpackChunkName: "components/doctor-item" */).then(c => wrapFunctional(c.default || c)) |
||||
export const DoctorVideoItem = () => import('../../src/components/DoctorVideoItem.vue' /* webpackChunkName: "components/doctor-video-item" */).then(c => wrapFunctional(c.default || c)) |
||||
export const MobileHeader = () => import('../../src/components/MobileHeader.vue' /* webpackChunkName: "components/mobile-header" */).then(c => wrapFunctional(c.default || c)) |
||||
export const NotLogin = () => import('../../src/components/NotLogin.vue' /* webpackChunkName: "components/not-login" */).then(c => wrapFunctional(c.default || c)) |
||||
export const UserInfoCommon = () => import('../../src/components/UserInfoCommon.vue' /* webpackChunkName: "components/user-info-common" */).then(c => wrapFunctional(c.default || c)) |
||||
export const VideoArticleListItem = () => import('../../src/components/VideoArticleListItem.vue' /* webpackChunkName: "components/video-article-list-item" */).then(c => wrapFunctional(c.default || c)) |
||||
export const WebFooter = () => import('../../src/components/WebFooter.vue' /* webpackChunkName: "components/web-footer" */).then(c => wrapFunctional(c.default || c)) |
||||
export const WebHeader = () => import('../../src/components/WebHeader.vue' /* webpackChunkName: "components/web-header" */).then(c => wrapFunctional(c.default || c)) |
||||
|
||||
// nuxt/nuxt.js#8607
|
||||
function wrapFunctional(options) { |
||||
if (!options || !options.functional) { |
||||
return options |
||||
} |
||||
|
||||
const propKeys = Array.isArray(options.props) ? options.props : Object.keys(options.props || {}) |
||||
|
||||
return { |
||||
render(h) { |
||||
const attrs = {} |
||||
const props = {} |
||||
|
||||
for (const key in this.$attrs) { |
||||
if (propKeys.includes(key)) { |
||||
props[key] = this.$attrs[key] |
||||
} else { |
||||
attrs[key] = this.$attrs[key] |
||||
} |
||||
} |
||||
|
||||
return h(options, { |
||||
on: this.$listeners, |
||||
attrs, |
||||
props, |
||||
scopedSlots: this.$scopedSlots, |
||||
}, this.$slots.default) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,122 @@
@@ -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,96 @@
@@ -0,0 +1,96 @@
|
||||
<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"> |
||||
<a v-if="typeof $route === 'undefined'" class="error-link" href="/"></a> |
||||
<NuxtLink v-else class="error-link" to="/">Back to the home page</NuxtLink> |
||||
</p> |
||||
|
||||
<div class="logo"> |
||||
<a href="https://nuxtjs.org" target="_blank" rel="noopener">Nuxt</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' |
||||
} |
||||
] |
||||
} |
||||
} |
||||
} |
||||
</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 @@
@@ -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 || !link.__prefetch) { |
||||
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 @@
@@ -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,101 @@
@@ -0,0 +1,101 @@
|
||||
import Vue from 'vue' |
||||
import { compile } from '../utils' |
||||
|
||||
import NuxtError from '../../src/layouts/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 occurred 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 occurred while showing the error page'), |
||||
h('p', 'Unfortunately an error occurred and while showing the error page another error occurred'), |
||||
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,7 @@
@@ -0,0 +1,7 @@
|
||||
import Vue from 'vue' |
||||
import * as components from './index' |
||||
|
||||
for (const name in components) { |
||||
Vue.component(name, components[name]) |
||||
Vue.component('Lazy' + name, components[name]) |
||||
} |
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
# Discovered Components |
||||
|
||||
This is an auto-generated list of components discovered by [nuxt/components](https://github.com/nuxt/components). |
||||
|
||||
You can directly use them in pages and other components without the need to import them. |
||||
|
||||
**Tip:** If a component is conditionally rendered with `v-if` and is big, it is better to use `Lazy` or `lazy-` prefix to lazy load. |
||||
|
||||
- `<Barrage>` | `<barrage>` (components/Barrage.vue) |
||||
- `<BottomRightFixed>` | `<bottom-right-fixed>` (components/BottomRightFixed.vue) |
||||
- `<CommonHeader>` | `<common-header>` (components/CommonHeader.vue) |
||||
- `<DoctorItem>` | `<doctor-item>` (components/DoctorItem.vue) |
||||
- `<DoctorVideoItem>` | `<doctor-video-item>` (components/DoctorVideoItem.vue) |
||||
- `<MobileHeader>` | `<mobile-header>` (components/MobileHeader.vue) |
||||
- `<NotLogin>` | `<not-login>` (components/NotLogin.vue) |
||||
- `<UserInfoCommon>` | `<user-info-common>` (components/UserInfoCommon.vue) |
||||
- `<VideoArticleListItem>` | `<video-article-list-item>` (components/VideoArticleListItem.vue) |
||||
- `<WebFooter>` | `<web-footer>` (components/WebFooter.vue) |
||||
- `<WebHeader>` | `<web-header>` (components/WebHeader.vue) |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
// This file is intentionally left empty for noop aliases
|
@ -0,0 +1,304 @@
@@ -0,0 +1,304 @@
|
||||
import Vue from 'vue' |
||||
import Vuex from 'vuex' |
||||
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 '../src/layouts/error.vue' |
||||
import Nuxt from './components/nuxt.js' |
||||
import App from './App.js' |
||||
import { setContext, getLocation, getRouteData, normalizeError } from './utils' |
||||
import { createStore } from './store.js' |
||||
|
||||
/* Plugins */ |
||||
|
||||
import nuxt_plugin_plugin_1ebeed53 from 'nuxt_plugin_plugin_1ebeed53' // Source: ./components/plugin.js (mode: 'all')
|
||||
import nuxt_plugin_axios_3f743ce9 from 'nuxt_plugin_axios_3f743ce9' // Source: ./axios.js (mode: 'all')
|
||||
import nuxt_plugin_elementui_d905880e from 'nuxt_plugin_elementui_d905880e' // Source: ../src/plugins/element-ui (mode: 'all')
|
||||
import nuxt_plugin_axios_2228ef02 from 'nuxt_plugin_axios_2228ef02' // Source: ../src/plugins/axios (mode: 'all')
|
||||
import nuxt_plugin_filters_2abc1387 from 'nuxt_plugin_filters_2abc1387' // Source: ../src/plugins/filters (mode: 'all')
|
||||
import nuxt_plugin_vueqr_234f70b3 from 'nuxt_plugin_vueqr_234f70b3' // Source: ../src/plugins/vueqr (mode: 'all')
|
||||
import nuxt_plugin_video_2349f707 from 'nuxt_plugin_video_2349f707' // Source: ../src/plugins/video (mode: 'client')
|
||||
import nuxt_plugin_swiper_68e7f06e from 'nuxt_plugin_swiper_68e7f06e' // Source: ../src/plugins/swiper.js (mode: 'client')
|
||||
import nuxt_plugin_wxshare_5d8a49ee from 'nuxt_plugin_wxshare_5d8a49ee' // Source: ../src/plugins/wx-share.js (mode: 'client')
|
||||
import nuxt_plugin_baidu_7b6ad772 from 'nuxt_plugin_baidu_7b6ad772' // Source: ../src/plugins/baidu.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) |
||||
|
||||
Object.defineProperty(Vue.prototype, '$nuxt', { |
||||
get() { |
||||
const globalNuxt = this.$root.$options.$nuxt |
||||
if (process.client && !globalNuxt && typeof window !== 'undefined') { |
||||
return window.$nuxt |
||||
} |
||||
return globalNuxt |
||||
}, |
||||
configurable: true |
||||
}) |
||||
|
||||
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"} |
||||
|
||||
const originalRegisterModule = Vuex.Store.prototype.registerModule |
||||
|
||||
function registerModule (path, rawModule, options = {}) { |
||||
const preserveState = process.client && ( |
||||
Array.isArray(path) |
||||
? !!path.reduce((namespacedState, path) => namespacedState && namespacedState[path], this.state) |
||||
: path in this.state |
||||
) |
||||
return originalRegisterModule.call(this, path, rawModule, { preserveState, ...options }) |
||||
} |
||||
|
||||
async function createApp(ssrContext, config = {}) { |
||||
const router = await createRouter(ssrContext, config) |
||||
|
||||
const store = createStore(ssrContext) |
||||
// Add this.$router into store actions/mutations
|
||||
store.$router = router |
||||
|
||||
// Fix SSR caveat https://github.com/nuxt/nuxt.js/issues/3757#issuecomment-414689141
|
||||
store.registerModule = registerModule |
||||
|
||||
// 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":"糖尿病网 - 服务糖尿病患者的知识社区","htmlAttrs":{"lang":"en"},"meta":[{"charset":"utf-8"},{"name":"viewport","content":"initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,width=device-width,user-scalable=no,viewport-fit=cover"},{"hid":"description","name":"description","content":"糖尿病网,服务糖尿病患者的知识平台!以帮助糖尿病患者科学管理身体为目标,提供最新鲜、专业的健康管理资讯。"},{"hid":"keywords","name":"keywords","content":"糖尿病,健康管理,控糖,血糖,血糖管理,科普,知识社区,专业资讯,干货,糖友,疾病,公益,有用,深度,饮食知识"},{"name":"format-detection","content":"telephone=no"}],"link":[{"rel":"icon","type":"image\u002Fx-icon","href":"\u002Ffavicon.ico"}],"script":[{"src":"https:\u002F\u002Fhm.baidu.com\u002Fhm.js?c17780012e32ae356918a39fe159755e"}],"style":[]}, |
||||
|
||||
store, |
||||
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 |
||||
} |
||||
|
||||
// Make app available into store via this.app
|
||||
store.app = 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, { |
||||
store, |
||||
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 |
||||
}) |
||||
|
||||
function inject(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 |
||||
// Add into context
|
||||
if (!app.context[key]) { |
||||
app.context[key] = value |
||||
} |
||||
|
||||
// Add into store
|
||||
store[key] = app[key] |
||||
|
||||
// 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.prototype, key)) { |
||||
Object.defineProperty(Vue.prototype, key, { |
||||
get () { |
||||
return this.$root.$options[key] |
||||
} |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
// Inject runtime config as $config
|
||||
inject('config', config) |
||||
|
||||
if (process.client) { |
||||
// Replace store state before plugins execution
|
||||
if (window.__NUXT__ && window.__NUXT__.state) { |
||||
store.replaceState(window.__NUXT__.state) |
||||
} |
||||
} |
||||
|
||||
// Add enablePreview(previewData = {}) in context for plugins
|
||||
if (process.static && process.client) { |
||||
app.context.enablePreview = function (previewData = {}) { |
||||
app.previewData = Object.assign({}, previewData) |
||||
inject('preview', previewData) |
||||
} |
||||
} |
||||
// Plugin execution
|
||||
|
||||
if (typeof nuxt_plugin_plugin_1ebeed53 === 'function') { |
||||
await nuxt_plugin_plugin_1ebeed53(app.context, inject) |
||||
} |
||||
|
||||
if (typeof nuxt_plugin_axios_3f743ce9 === 'function') { |
||||
await nuxt_plugin_axios_3f743ce9(app.context, inject) |
||||
} |
||||
|
||||
if (typeof nuxt_plugin_elementui_d905880e === 'function') { |
||||
await nuxt_plugin_elementui_d905880e(app.context, inject) |
||||
} |
||||
|
||||
if (typeof nuxt_plugin_axios_2228ef02 === 'function') { |
||||
await nuxt_plugin_axios_2228ef02(app.context, inject) |
||||
} |
||||
|
||||
if (typeof nuxt_plugin_filters_2abc1387 === 'function') { |
||||
await nuxt_plugin_filters_2abc1387(app.context, inject) |
||||
} |
||||
|
||||
if (typeof nuxt_plugin_vueqr_234f70b3 === 'function') { |
||||
await nuxt_plugin_vueqr_234f70b3(app.context, inject) |
||||
} |
||||
|
||||
if (process.client && typeof nuxt_plugin_video_2349f707 === 'function') { |
||||
await nuxt_plugin_video_2349f707(app.context, inject) |
||||
} |
||||
|
||||
if (process.client && typeof nuxt_plugin_swiper_68e7f06e === 'function') { |
||||
await nuxt_plugin_swiper_68e7f06e(app.context, inject) |
||||
} |
||||
|
||||
if (process.client && typeof nuxt_plugin_wxshare_5d8a49ee === 'function') { |
||||
await nuxt_plugin_wxshare_5d8a49ee(app.context, inject) |
||||
} |
||||
|
||||
if (process.client && typeof nuxt_plugin_baidu_7b6ad772 === 'function') { |
||||
await nuxt_plugin_baidu_7b6ad772(app.context, inject) |
||||
} |
||||
|
||||
// Lock enablePreview in context
|
||||
if (process.static && process.client) { |
||||
app.context.enablePreview = function () { |
||||
console.warn('You cannot call enablePreview() outside a plugin.') |
||||
} |
||||
} |
||||
|
||||
// Wait for async component to be resolved first
|
||||
await new Promise((resolve, reject) => { |
||||
// Ignore 404s rather than blindly replacing URL in browser
|
||||
if (process.client) { |
||||
const { route } = router.resolve(app.context.route.fullPath) |
||||
if (!route.matched.length) { |
||||
return resolve() |
||||
} |
||||
} |
||||
router.replace(app.context.route.fullPath, resolve, (err) => { |
||||
// https://github.com/vuejs/vue-router/blob/v3.4.3/src/util/errors.js
|
||||
if (!err._isRouter) return reject(err) |
||||
if (err.type !== 2 /* NavigationFailureType.redirected */) return resolve() |
||||
|
||||
// navigated to a different route in router guard
|
||||
const unregister = router.afterEach(async (to, from) => { |
||||
if (process.server && ssrContext && ssrContext.url) { |
||||
ssrContext.url = to.fullPath |
||||
} |
||||
app.context.route = await getRouteData(to) |
||||
app.context.params = to.params || {} |
||||
app.context.query = to.query || {} |
||||
unregister() |
||||
resolve() |
||||
}) |
||||
}) |
||||
}) |
||||
|
||||
return { |
||||
store, |
||||
app, |
||||
router |
||||
} |
||||
} |
||||
|
||||
export { createApp, NuxtError } |
@ -0,0 +1,82 @@
@@ -0,0 +1,82 @@
|
||||
const chunks = {} // chunkId => exports
|
||||
const chunksInstalling = {} // chunkId => Promise
|
||||
const failedChunks = {} |
||||
|
||||
function importChunk(chunkId, src) { |
||||
// Already installed
|
||||
if (chunks[chunkId]) { |
||||
return Promise.resolve(chunks[chunkId]) |
||||
} |
||||
|
||||
// Failed loading
|
||||
if (failedChunks[chunkId]) { |
||||
return Promise.reject(failedChunks[chunkId]) |
||||
} |
||||
|
||||
// Installing
|
||||
if (chunksInstalling[chunkId]) { |
||||
return chunksInstalling[chunkId] |
||||
} |
||||
|
||||
// Set a promise in chunk cache
|
||||
let resolve, reject |
||||
const promise = chunksInstalling[chunkId] = new Promise((_resolve, _reject) => { |
||||
resolve = _resolve |
||||
reject = _reject |
||||
}) |
||||
|
||||
// Clear chunk data from cache
|
||||
delete chunks[chunkId] |
||||
|
||||
// Start chunk loading
|
||||
const script = document.createElement('script') |
||||
script.charset = 'utf-8' |
||||
script.timeout = 120 |
||||
script.src = src |
||||
let timeout |
||||
|
||||
// Create error before stack unwound to get useful stacktrace later
|
||||
const error = new Error() |
||||
|
||||
// Complete handlers
|
||||
const onScriptComplete = script.onerror = script.onload = (event) => { |
||||
// Cleanups
|
||||
clearTimeout(timeout) |
||||
delete chunksInstalling[chunkId] |
||||
|
||||
// Avoid mem leaks in IE
|
||||
script.onerror = script.onload = null |
||||
|
||||
// Verify chunk is loaded
|
||||
if (chunks[chunkId]) { |
||||
return resolve(chunks[chunkId]) |
||||
} |
||||
|
||||
// Something bad happened
|
||||
const errorType = event && (event.type === 'load' ? 'missing' : event.type) |
||||
const realSrc = event && event.target && event.target.src |
||||
error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')' |
||||
error.name = 'ChunkLoadError' |
||||
error.type = errorType |
||||
error.request = realSrc |
||||
failedChunks[chunkId] = error |
||||
reject(error) |
||||
} |
||||
|
||||
// Timeout
|
||||
timeout = setTimeout(() => { |
||||
onScriptComplete({ type: 'timeout', target: script }) |
||||
}, 120000) |
||||
|
||||
// Append script
|
||||
document.head.appendChild(script) |
||||
|
||||
// Return promise
|
||||
return promise |
||||
} |
||||
|
||||
export function installJsonp() { |
||||
window.__NUXT_JSONP__ = function (chunkId, exports) { chunks[chunkId] = exports } |
||||
window.__NUXT_JSONP_CACHE__ = chunks |
||||
window.__NUXT_IMPORT__ = importChunk |
||||
} |
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
<template> |
||||
<Nuxt /> |
||||
</template> |
@ -0,0 +1,108 @@
@@ -0,0 +1,108 @@
|
||||
<style> |
||||
#nuxt-loading { |
||||
background: white; |
||||
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 #D3D3D3; |
||||
-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> |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
const middleware = {} |
||||
|
||||
middleware['authenticated'] = require('../src/middleware/authenticated.js') |
||||
middleware['authenticated'] = middleware['authenticated'].default || middleware['authenticated'] |
||||
|
||||
middleware['device'] = require('../src/middleware/device.js') |
||||
middleware['device'] = middleware['device'].default || middleware['device'] |
||||
|
||||
export default middleware |
@ -0,0 +1,90 @@
@@ -0,0 +1,90 @@
|
||||
import Vue from 'vue' |
||||
import { hasFetch, normalizeError, addLifecycleHook, createGetCounter } 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) { |
||||
if (process.dev) { |
||||
console.error('Error in fetch():', 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,65 @@
@@ -0,0 +1,65 @@
|
||||
import Vue from 'vue' |
||||
import { hasFetch, normalizeError, addLifecycleHook, purifyData, createGetCounter } from '../utils' |
||||
|
||||
async function serverPrefetch() { |
||||
if (!this._fetchOnServer) { |
||||
return |
||||
} |
||||
|
||||
// Call and await on $fetch
|
||||
try { |
||||
await this.$options.fetch.call(this) |
||||
} catch (err) { |
||||
if (process.dev) { |
||||
console.error('Error in fetch():', err) |
||||
} |
||||
this.$fetchState.error = normalizeError(err) |
||||
} |
||||
this.$fetchState.pending = false |
||||
|
||||
// Define an ssrKey for hydration
|
||||
this._fetchKey = this._fetchKey || this.$ssrContext.fetchCounters['']++ |
||||
|
||||
// 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[this._fetchKey] = |
||||
this.$fetchState.error ? { _error: this.$fetchState.error } : purifyData(this._data) |
||||
} |
||||
|
||||
export default { |
||||
created() { |
||||
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 |
||||
} |
||||
|
||||
const defaultKey = this.$options._scopeId || this.$options.name || '' |
||||
const getCounter = createGetCounter(this.$ssrContext.fetchCounters, defaultKey) |
||||
|
||||
if (typeof this.$options.fetchKey === 'function') { |
||||
this._fetchKey = this.$options.fetchKey.call(this, getCounter) |
||||
} else { |
||||
const key = 'string' === typeof this.$options.fetchKey ? this.$options.fetchKey : defaultKey |
||||
this._fetchKey = key ? key + ':' + getCounter(key) : String(getCounter(key)) |
||||
} |
||||
|
||||
// Added for remove vue undefined warning while ssr
|
||||
this.$fetch = () => {} // issue #8043
|
||||
Vue.util.defineReactive(this, '$fetchState', { |
||||
pending: true, |
||||
error: null, |
||||
timestamp: Date.now() |
||||
}) |
||||
|
||||
addLifecycleHook(this, 'serverPrefetch', serverPrefetch) |
||||
} |
||||
} |
@ -0,0 +1,133 @@
@@ -0,0 +1,133 @@
|
||||
import Vue from 'vue' |
||||
import Router from 'vue-router' |
||||
import { normalizeURL, decode } from 'ufo' |
||||
import { interopDefault } from './utils' |
||||
import scrollBehavior from './router.scrollBehavior.js' |
||||
|
||||
const _4b81ed11 = () => interopDefault(import('../src/pages/article/index.vue' /* webpackChunkName: "pages/article/index" */)) |
||||
const _45864e0c = () => interopDefault(import('../src/pages/doctor/index.vue' /* webpackChunkName: "pages/doctor/index" */)) |
||||
const _3729b6ed = () => interopDefault(import('../src/pages/more.vue' /* webpackChunkName: "pages/more" */)) |
||||
const _779b537a = () => interopDefault(import('../src/pages/search/index.vue' /* webpackChunkName: "pages/search/index" */)) |
||||
const _240caca6 = () => interopDefault(import('../src/pages/user/index.vue' /* webpackChunkName: "pages/user/index" */)) |
||||
const _4ad07256 = () => interopDefault(import('../src/pages/video/index.vue' /* webpackChunkName: "pages/video/index" */)) |
||||
const _394ae834 = () => interopDefault(import('../src/pages/other/about.vue' /* webpackChunkName: "pages/other/about" */)) |
||||
const _4c6b2b63 = () => interopDefault(import('../src/pages/other/agreement.vue' /* webpackChunkName: "pages/other/agreement" */)) |
||||
const _114b4da8 = () => interopDefault(import('../src/pages/other/cookies.vue' /* webpackChunkName: "pages/other/cookies" */)) |
||||
const _23ed8784 = () => interopDefault(import('../src/pages/other/corporate.vue' /* webpackChunkName: "pages/other/corporate" */)) |
||||
const _9e1bc1dc = () => interopDefault(import('../src/pages/other/disclaimer.vue' /* webpackChunkName: "pages/other/disclaimer" */)) |
||||
const _2424f59e = () => interopDefault(import('../src/pages/other/link.vue' /* webpackChunkName: "pages/other/link" */)) |
||||
const _5edfebbe = () => interopDefault(import('../src/pages/other/privacy.vue' /* webpackChunkName: "pages/other/privacy" */)) |
||||
const _3b2c0424 = () => interopDefault(import('../src/pages/user/setting.vue' /* webpackChunkName: "pages/user/setting" */)) |
||||
const _3a19ffb9 = () => interopDefault(import('../src/pages/article/_id.vue' /* webpackChunkName: "pages/article/_id" */)) |
||||
const _3a40d1e2 = () => interopDefault(import('../src/pages/doctor/_id.vue' /* webpackChunkName: "pages/doctor/_id" */)) |
||||
const _6763123e = () => interopDefault(import('../src/pages/video/_id.vue' /* webpackChunkName: "pages/video/_id" */)) |
||||
const _147516ea = () => interopDefault(import('../src/pages/index.vue' /* webpackChunkName: "pages/index" */)) |
||||
|
||||
const emptyFn = () => {} |
||||
|
||||
Vue.use(Router) |
||||
|
||||
export const routerOptions = { |
||||
mode: 'history', |
||||
base: '/', |
||||
linkActiveClass: 'nuxt-link-active', |
||||
linkExactActiveClass: 'nuxt-link-exact-active', |
||||
scrollBehavior, |
||||
|
||||
routes: [{ |
||||
path: "/article", |
||||
component: _4b81ed11, |
||||
name: "article" |
||||
}, { |
||||
path: "/doctor", |
||||
component: _45864e0c, |
||||
name: "doctor" |
||||
}, { |
||||
path: "/more", |
||||
component: _3729b6ed, |
||||
name: "more" |
||||
}, { |
||||
path: "/search", |
||||
component: _779b537a, |
||||
name: "search" |
||||
}, { |
||||
path: "/user", |
||||
component: _240caca6, |
||||
name: "user" |
||||
}, { |
||||
path: "/video", |
||||
component: _4ad07256, |
||||
name: "video" |
||||
}, { |
||||
path: "/other/about", |
||||
component: _394ae834, |
||||
name: "other-about" |
||||
}, { |
||||
path: "/other/agreement", |
||||
component: _4c6b2b63, |
||||
name: "other-agreement" |
||||
}, { |
||||
path: "/other/cookies", |
||||
component: _114b4da8, |
||||
name: "other-cookies" |
||||
}, { |
||||
path: "/other/corporate", |
||||
component: _23ed8784, |
||||
name: "other-corporate" |
||||
}, { |
||||
path: "/other/disclaimer", |
||||
component: _9e1bc1dc, |
||||
name: "other-disclaimer" |
||||
}, { |
||||
path: "/other/link", |
||||
component: _2424f59e, |
||||
name: "other-link" |
||||
}, { |
||||
path: "/other/privacy", |
||||
component: _5edfebbe, |
||||
name: "other-privacy" |
||||
}, { |
||||
path: "/user/setting", |
||||
component: _3b2c0424, |
||||
name: "user-setting" |
||||
}, { |
||||
path: "/article/:id", |
||||
component: _3a19ffb9, |
||||
name: "article-id" |
||||
}, { |
||||
path: "/doctor/:id", |
||||
component: _3a40d1e2, |
||||
name: "doctor-id" |
||||
}, { |
||||
path: "/video/:id", |
||||
component: _6763123e, |
||||
name: "video-id" |
||||
}, { |
||||
path: "/", |
||||
component: _147516ea, |
||||
name: "index" |
||||
}], |
||||
|
||||
fallback: false |
||||
} |
||||
|
||||
export function createRouter (ssrContext, config) { |
||||
const base = (config._app && config._app.basePath) || routerOptions.base |
||||
const router = new Router({ ...routerOptions, base }) |
||||
|
||||
// TODO: remove in Nuxt 3
|
||||
const originalPush = router.push |
||||
router.push = function push (location, onComplete = emptyFn, onAbort) { |
||||
return originalPush.call(this, location, onComplete, onAbort) |
||||
} |
||||
|
||||
const resolve = router.resolve.bind(router) |
||||
router.resolve = (to, current, append) => { |
||||
if (typeof to === 'string') { |
||||
to = normalizeURL(to) |
||||
} |
||||
return resolve(to, current, append) |
||||
} |
||||
|
||||
return router |
||||
} |
@ -0,0 +1,76 @@
@@ -0,0 +1,76 @@
|
||||
import { getMatchedComponents, setScrollRestoration } from './utils' |
||||
|
||||
if (process.client) { |
||||
if ('scrollRestoration' in window.history) { |
||||
setScrollRestoration('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', () => { |
||||
setScrollRestoration('auto') |
||||
}) |
||||
|
||||
// Setting scrollRestoration to manual again when returning to this page.
|
||||
window.addEventListener('load', () => { |
||||
setScrollRestoration('manual') |
||||
}) |
||||
} |
||||
} |
||||
|
||||
function shouldScrollToTop(route) { |
||||
const Pages = getMatchedComponents(route) |
||||
if (Pages.length === 1) { |
||||
const { options = {} } = Pages[0] |
||||
return options.scrollToTop !== false |
||||
} |
||||
return Pages.some(({ options }) => options && options.scrollToTop) |
||||
} |
||||
|
||||
export default function (to, from, savedPosition) { |
||||
// If the returned position is falsy or an empty object, will retain current scroll position
|
||||
let position = false |
||||
const isRouteChanged = to !== from |
||||
|
||||
// savedPosition is only available for popstate navigations (back button)
|
||||
if (savedPosition) { |
||||
position = savedPosition |
||||
} else if (isRouteChanged && shouldScrollToTop(to)) { |
||||
position = { x: 0, y: 0 } |
||||
} |
||||
|
||||
const nuxt = window.$nuxt |
||||
|
||||
if ( |
||||
// Initial load (vuejs/vue-router#3199)
|
||||
!isRouteChanged || |
||||
// Route hash changes
|
||||
(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,110 @@
@@ -0,0 +1,110 @@
|
||||
[ |
||||
{ |
||||
"name": "article", |
||||
"path": "/article", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/article/index.vue", |
||||
"chunkName": "pages/article/index" |
||||
}, |
||||
{ |
||||
"name": "doctor", |
||||
"path": "/doctor", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/doctor/index.vue", |
||||
"chunkName": "pages/doctor/index" |
||||
}, |
||||
{ |
||||
"name": "more", |
||||
"path": "/more", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/more.vue", |
||||
"chunkName": "pages/more" |
||||
}, |
||||
{ |
||||
"name": "search", |
||||
"path": "/search", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/search/index.vue", |
||||
"chunkName": "pages/search/index" |
||||
}, |
||||
{ |
||||
"name": "user", |
||||
"path": "/user", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/user/index.vue", |
||||
"chunkName": "pages/user/index" |
||||
}, |
||||
{ |
||||
"name": "video", |
||||
"path": "/video", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/video/index.vue", |
||||
"chunkName": "pages/video/index" |
||||
}, |
||||
{ |
||||
"name": "other-about", |
||||
"path": "/other/about", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/other/about.vue", |
||||
"chunkName": "pages/other/about" |
||||
}, |
||||
{ |
||||
"name": "other-agreement", |
||||
"path": "/other/agreement", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/other/agreement.vue", |
||||
"chunkName": "pages/other/agreement" |
||||
}, |
||||
{ |
||||
"name": "other-cookies", |
||||
"path": "/other/cookies", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/other/cookies.vue", |
||||
"chunkName": "pages/other/cookies" |
||||
}, |
||||
{ |
||||
"name": "other-corporate", |
||||
"path": "/other/corporate", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/other/corporate.vue", |
||||
"chunkName": "pages/other/corporate" |
||||
}, |
||||
{ |
||||
"name": "other-disclaimer", |
||||
"path": "/other/disclaimer", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/other/disclaimer.vue", |
||||
"chunkName": "pages/other/disclaimer" |
||||
}, |
||||
{ |
||||
"name": "other-link", |
||||
"path": "/other/link", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/other/link.vue", |
||||
"chunkName": "pages/other/link" |
||||
}, |
||||
{ |
||||
"name": "other-privacy", |
||||
"path": "/other/privacy", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/other/privacy.vue", |
||||
"chunkName": "pages/other/privacy" |
||||
}, |
||||
{ |
||||
"name": "user-setting", |
||||
"path": "/user/setting", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/user/setting.vue", |
||||
"chunkName": "pages/user/setting" |
||||
}, |
||||
{ |
||||
"name": "article-id", |
||||
"path": "/article/:id", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/article/_id.vue", |
||||
"chunkName": "pages/article/_id" |
||||
}, |
||||
{ |
||||
"name": "doctor-id", |
||||
"path": "/doctor/:id", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/doctor/_id.vue", |
||||
"chunkName": "pages/doctor/_id" |
||||
}, |
||||
{ |
||||
"name": "video-id", |
||||
"path": "/video/:id", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/video/_id.vue", |
||||
"chunkName": "pages/video/_id" |
||||
}, |
||||
{ |
||||
"name": "index", |
||||
"path": "/", |
||||
"component": "/home/kola-wsl/project/tangwang-pc/src/pages/index.vue", |
||||
"chunkName": "pages/index" |
||||
} |
||||
] |
@ -0,0 +1,292 @@
@@ -0,0 +1,292 @@
|
||||
import Vue from 'vue' |
||||
import { joinURL, normalizeURL, withQuery } from 'ufo' |
||||
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', { domProps: { id: '__nuxt' } }) }) |
||||
|
||||
const createNext = ssrContext => (opts) => { |
||||
// If static target, render on client-side
|
||||
ssrContext.redirected = opts |
||||
if (ssrContext.target === 'static' || !ssrContext.res) { |
||||
ssrContext.nuxt.serverRendered = false |
||||
return |
||||
} |
||||
let fullPath = withQuery(opts.path, opts.query) |
||||
const $config = ssrContext.runtimeConfig || {} |
||||
const routerBase = ($config._app && $config._app.basePath) || '/' |
||||
if (!fullPath.startsWith('http') && (routerBase !== '/' && !fullPath.startsWith(routerBase))) { |
||||
fullPath = joinURL(routerBase, fullPath) |
||||
} |
||||
// Avoid loop redirect
|
||||
if (decodeURI(fullPath) === decodeURI(ssrContext.url)) { |
||||
ssrContext.redirected = false |
||||
return |
||||
} |
||||
ssrContext.res.writeHead(opts.status, { |
||||
Location: normalizeURL(fullPath) |
||||
}) |
||||
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, state: null, serverRendered: true, routePath: '' } |
||||
|
||||
ssrContext.fetchCounters = {} |
||||
|
||||
// Remove query from url is static target
|
||||
|
||||
// Public runtime config
|
||||
ssrContext.nuxt.config = ssrContext.runtimeConfig.public |
||||
if (ssrContext.nuxt.config._app) { |
||||
__webpack_public_path__ = joinURL(ssrContext.nuxt.config._app.cdnURL, ssrContext.nuxt.config._app.assetsPath) |
||||
} |
||||
// Create the app definition and the instance (created for each request)
|
||||
const { app, router, store } = await createApp(ssrContext, ssrContext.runtimeConfig.private) |
||||
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 }))) |
||||
|
||||
ssrContext.rendered = () => { |
||||
// Add the state from the vuex store
|
||||
ssrContext.nuxt.state = store.state |
||||
} |
||||
} |
||||
|
||||
const renderErrorPage = async () => { |
||||
// Don't server-render the page in static target
|
||||
if (ssrContext.target === 'static') { |
||||
ssrContext.nuxt.serverRendered = false |
||||
} |
||||
|
||||
// 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() |
||||
} |
||||
|
||||
// Components are already resolved by setContext -> getRouteData (app/utils.js)
|
||||
const Components = getMatchedComponents(app.context.route) |
||||
|
||||
/* |
||||
** Dispatch store nuxtServerInit |
||||
*/ |
||||
if (store._actions && store._actions.nuxtServerInit) { |
||||
try { |
||||
await store.dispatch('nuxtServerInit', app.context) |
||||
} catch (err) { |
||||
console.debug('Error occurred when calling nuxtServerInit: ', err.message) |
||||
throw err |
||||
} |
||||
} |
||||
// ...If there is a redirect or an error, stop the process
|
||||
if (ssrContext.redirected) { |
||||
return noopApp() |
||||
} |
||||
if (ssrContext.nuxt.error) { |
||||
return renderErrorPage() |
||||
} |
||||
|
||||
/* |
||||
** Call global middleware (nuxt.config.js) |
||||
*/ |
||||
let midd = ["device"] |
||||
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) { |
||||
// 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) |
||||
})) |
||||
|
||||
// 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,130 @@
@@ -0,0 +1,130 @@
|
||||
import Vue from 'vue' |
||||
import Vuex from 'vuex' |
||||
|
||||
Vue.use(Vuex) |
||||
|
||||
const VUEX_PROPERTIES = ['state', 'getters', 'actions', 'mutations'] |
||||
|
||||
let store = {}; |
||||
|
||||
(function updateModules () { |
||||
store = normalizeRoot(require('../src/store/index.js'), 'store/index.js') |
||||
|
||||
// If store is an exported method = classic mode (deprecated)
|
||||
|
||||
// Enforce store modules
|
||||
store.modules = store.modules || {} |
||||
|
||||
resolveStoreModules(require('../src/store/device.js'), 'device.js') |
||||
resolveStoreModules(require('../src/store/user.js'), 'user.js') |
||||
|
||||
// If the environment supports hot reloading...
|
||||
})() |
||||
|
||||
// createStore
|
||||
export const createStore = store instanceof Function ? store : () => { |
||||
return new Vuex.Store(Object.assign({ |
||||
strict: (process.env.NODE_ENV !== 'production') |
||||
}, store)) |
||||
} |
||||
|
||||
function normalizeRoot (moduleData, filePath) { |
||||
moduleData = moduleData.default || moduleData |
||||
|
||||
if (moduleData.commit) { |
||||
throw new Error(`[nuxt] ${filePath} should export a method that returns a Vuex instance.`) |
||||
} |
||||
|
||||
if (typeof moduleData !== 'function') { |
||||
// Avoid TypeError: setting a property that has only a getter when overwriting top level keys
|
||||
moduleData = Object.assign({}, moduleData) |
||||
} |
||||
return normalizeModule(moduleData, filePath) |
||||
} |
||||
|
||||
function normalizeModule (moduleData, filePath) { |
||||
if (moduleData.state && typeof moduleData.state !== 'function') { |
||||
console.warn(`'state' should be a method that returns an object in ${filePath}`) |
||||
|
||||
const state = Object.assign({}, moduleData.state) |
||||
// Avoid TypeError: setting a property that has only a getter when overwriting top level keys
|
||||
moduleData = Object.assign({}, moduleData, { state: () => state }) |
||||
} |
||||
return moduleData |
||||
} |
||||
|
||||
function resolveStoreModules (moduleData, filename) { |
||||
moduleData = moduleData.default || moduleData |
||||
// Remove store src + extension (./foo/index.js -> foo/index)
|
||||
const namespace = filename.replace(/\.(js|mjs)$/, '') |
||||
const namespaces = namespace.split('/') |
||||
let moduleName = namespaces[namespaces.length - 1] |
||||
const filePath = `store/${filename}` |
||||
|
||||
moduleData = moduleName === 'state' |
||||
? normalizeState(moduleData, filePath) |
||||
: normalizeModule(moduleData, filePath) |
||||
|
||||
// If src is a known Vuex property
|
||||
if (VUEX_PROPERTIES.includes(moduleName)) { |
||||
const property = moduleName |
||||
const propertyStoreModule = getStoreModule(store, namespaces, { isProperty: true }) |
||||
|
||||
// Replace state since it's a function
|
||||
mergeProperty(propertyStoreModule, moduleData, property) |
||||
return |
||||
} |
||||
|
||||
// If file is foo/index.js, it should be saved as foo
|
||||
const isIndexModule = (moduleName === 'index') |
||||
if (isIndexModule) { |
||||
namespaces.pop() |
||||
moduleName = namespaces[namespaces.length - 1] |
||||
} |
||||
|
||||
const storeModule = getStoreModule(store, namespaces) |
||||
|
||||
for (const property of VUEX_PROPERTIES) { |
||||
mergeProperty(storeModule, moduleData[property], property) |
||||
} |
||||
|
||||
if (moduleData.namespaced === false) { |
||||
delete storeModule.namespaced |
||||
} |
||||
} |
||||
|
||||
function normalizeState (moduleData, filePath) { |
||||
if (typeof moduleData !== 'function') { |
||||
console.warn(`${filePath} should export a method that returns an object`) |
||||
const state = Object.assign({}, moduleData) |
||||
return () => state |
||||
} |
||||
return normalizeModule(moduleData, filePath) |
||||
} |
||||
|
||||
function getStoreModule (storeModule, namespaces, { isProperty = false } = {}) { |
||||
// If ./mutations.js
|
||||
if (!namespaces.length || (isProperty && namespaces.length === 1)) { |
||||
return storeModule |
||||
} |
||||
|
||||
const namespace = namespaces.shift() |
||||
|
||||
storeModule.modules[namespace] = storeModule.modules[namespace] || {} |
||||
storeModule.modules[namespace].namespaced = true |
||||
storeModule.modules[namespace].modules = storeModule.modules[namespace].modules || {} |
||||
|
||||
return getStoreModule(storeModule.modules[namespace], namespaces, { isProperty }) |
||||
} |
||||
|
||||
function mergeProperty (storeModule, moduleData, property) { |
||||
if (!moduleData) { |
||||
return |
||||
} |
||||
|
||||
if (property === 'state') { |
||||
storeModule.state = moduleData || storeModule.state |
||||
} else { |
||||
storeModule[property] = Object.assign({}, storeModule[property], moduleData) |
||||
} |
||||
} |
@ -0,0 +1,627 @@
@@ -0,0 +1,627 @@
|
||||
import Vue from 'vue' |
||||
import { isSamePath as _isSamePath, joinURL, normalizeURL, withQuery, withoutTrailingSlash } from 'ufo' |
||||
|
||||
// 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 createGetCounter (counterObject, defaultKey = '') { |
||||
return function getCounter (id = defaultKey) { |
||||
if (counterObject[id] === undefined) { |
||||
counterObject[id] = 0 |
||||
} |
||||
return counterObject[id]++ |
||||
} |
||||
} |
||||
|
||||
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 purifyData(data) { |
||||
if (process.env.NODE_ENV === 'production') { |
||||
return data |
||||
} |
||||
|
||||
return Object.entries(data).filter( |
||||
([key, value]) => { |
||||
const valid = !(value instanceof Function) && !(value instanceof Promise) |
||||
if (!valid) { |
||||
console.warn(`${key} is not able to be stringified. This will break in a production environment.`) |
||||
} |
||||
return valid |
||||
} |
||||
).reduce((obj, [key, value]) => { |
||||
obj[key] = value |
||||
return obj |
||||
}, {}) |
||||
} |
||||
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) { |
||||
try { |
||||
Component = await Component() |
||||
} catch (error) { |
||||
// Handle webpack chunk loading errors
|
||||
// This may be due to a new deployment or a network problem
|
||||
if ( |
||||
error && |
||||
error.name === 'ChunkLoadError' && |
||||
typeof window !== 'undefined' && |
||||
window.sessionStorage |
||||
) { |
||||
const timeNow = Date.now() |
||||
const previousReloadTime = parseInt(window.sessionStorage.getItem('nuxt-reload')) |
||||
|
||||
// check for previous reload time not to reload infinitely
|
||||
if (!previousReloadTime || previousReloadTime + 60000 < timeNow) { |
||||
window.sessionStorage.setItem('nuxt-reload', timeNow) |
||||
window.location.reload(true /* skip cache */) |
||||
} |
||||
} |
||||
|
||||
throw error |
||||
} |
||||
} |
||||
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: false, |
||||
isHMR: false, |
||||
app, |
||||
store: app.store, |
||||
payload: context.payload, |
||||
error: context.error, |
||||
base: app.router.options.base, |
||||
env: {"VUE_APP_TITLE":"production"} |
||||
} |
||||
// 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 = withQuery(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 = false |
||||
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) { |
||||
// 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) { |
||||
if (mode === 'hash') { |
||||
return window.location.hash.replace(/^#\//, '') |
||||
} |
||||
|
||||
base = decodeURI(base).slice(0, -1) // consideration is base is normalized with trailing slash
|
||||
let path = decodeURI(window.location.pathname) |
||||
|
||||
if (base && path.startsWith(base)) { |
||||
path = path.slice(base.length) |
||||
} |
||||
|
||||
const fullPath = (path || '/') + window.location.search + window.location.hash |
||||
|
||||
return normalizeURL(fullPath) |
||||
} |
||||
|
||||
// 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' |
||||
} |
||||
|
||||
export function addLifecycleHook(vm, hook, fn) { |
||||
if (!vm.$options[hook]) { |
||||
vm.$options[hook] = [] |
||||
} |
||||
if (!vm.$options[hook].includes(fn)) { |
||||
vm.$options[hook].push(fn) |
||||
} |
||||
} |
||||
|
||||
export const urlJoin = joinURL |
||||
|
||||
export const stripTrailingSlash = withoutTrailingSlash |
||||
|
||||
export const isSamePath = _isSamePath |
||||
|
||||
export function setScrollRestoration (newVal) { |
||||
try { |
||||
window.history.scrollRestoration = newVal; |
||||
} catch(e) {} |
||||
} |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
{ |
||||
"Barrage": { |
||||
"description": "Auto imported from components/Barrage.vue" |
||||
}, |
||||
"BottomRightFixed": { |
||||
"description": "Auto imported from components/BottomRightFixed.vue" |
||||
}, |
||||
"CommonHeader": { |
||||
"description": "Auto imported from components/CommonHeader.vue" |
||||
}, |
||||
"DoctorItem": { |
||||
"description": "Auto imported from components/DoctorItem.vue" |
||||
}, |
||||
"DoctorVideoItem": { |
||||
"description": "Auto imported from components/DoctorVideoItem.vue" |
||||
}, |
||||
"MobileHeader": { |
||||
"description": "Auto imported from components/MobileHeader.vue" |
||||
}, |
||||
"NotLogin": { |
||||
"description": "Auto imported from components/NotLogin.vue" |
||||
}, |
||||
"UserInfoCommon": { |
||||
"description": "Auto imported from components/UserInfoCommon.vue" |
||||
}, |
||||
"VideoArticleListItem": { |
||||
"description": "Auto imported from components/VideoArticleListItem.vue" |
||||
}, |
||||
"WebFooter": { |
||||
"description": "Auto imported from components/WebFooter.vue" |
||||
}, |
||||
"WebHeader": { |
||||
"description": "Auto imported from components/WebHeader.vue" |
||||
} |
||||
} |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html> |
||||
<html {{ HTML_ATTRS }}> |
||||
<head {{ HEAD_ATTRS }}> |
||||
{{ HEAD }} |
||||
</head> |
||||
<body {{ BODY_ATTRS }}> |
||||
{{ APP }} |
||||
</body> |
||||
</html> |
@ -0,0 +1,23 @@
@@ -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">An error occurred in the application and your page could not be served. If you are the application owner, check your logs for details.</div> |
||||
</div> |
||||
<div class="logo"> |
||||
<a href="https://nuxtjs.org" target="_blank" rel="noopener">Nuxt</a> |
||||
</div> |
||||
</div> |
||||
</body> |
||||
</html> |
Loading…
Reference in new issue