update
This commit is contained in:
+53
-23
@@ -1,4 +1,5 @@
|
||||
import Vue from 'vue'
|
||||
import { decode, parsePath, withoutBase, withoutTrailingSlash, normalizeURL } from 'ufo'
|
||||
<% utilsImports = [
|
||||
...(features.asyncData || features.fetch) ? [
|
||||
'getMatchedComponentsInstances',
|
||||
@@ -12,7 +13,7 @@ import Vue from 'vue'
|
||||
]: []
|
||||
] %>
|
||||
<% if (utilsImports.length) { %>import { <%= utilsImports.join(', ') %> } from './utils'<% } %>
|
||||
<% if (features.layouts && components.ErrorPage) { %>import NuxtError from '<%= components.ErrorPage %>'<% } %>
|
||||
import NuxtError from '<%= components.ErrorPage ? components.ErrorPage : "./components/nuxt-error.vue" %>'
|
||||
<% if (loading) { %>import NuxtLoading from '<%= (typeof loading === "string" ? loading : "./components/nuxt-loading.vue") %>'<% } %>
|
||||
<% if (buildIndicator) { %>import NuxtBuildIndicator from './components/nuxt-build-indicator'<% } %>
|
||||
<% css.forEach((c) => { %>
|
||||
@@ -98,7 +99,8 @@ export default {
|
||||
},
|
||||
created () {
|
||||
// Add this.$nuxt in child instances
|
||||
Vue.prototype.<%= globals.nuxt %> = this
|
||||
this.$root.$options.<%= globals.nuxt %> = this
|
||||
|
||||
if (process.client) {
|
||||
// add to window so we can listen when ready
|
||||
window.<%= globals.nuxt %> = <%= (globals.nuxt !== '$nuxt' ? 'window.$nuxt = ' : '') %>this
|
||||
@@ -129,11 +131,9 @@ export default {
|
||||
<% } %>
|
||||
},
|
||||
<% } %>
|
||||
<% if (loading) { %>
|
||||
watch: {
|
||||
'nuxt.err': 'errorChanged'
|
||||
},
|
||||
<% } %>
|
||||
<% if (features.clientOnline) { %>
|
||||
computed: {
|
||||
isOffline () {
|
||||
@@ -215,18 +215,29 @@ export default {
|
||||
<% if (loading) { %>this.$loading.finish()<% } %>
|
||||
<% } %>
|
||||
},
|
||||
<% if (loading) { %>
|
||||
errorChanged () {
|
||||
if (this.nuxt.err && this.$loading) {
|
||||
if (this.$loading.fail) {
|
||||
this.$loading.fail(this.nuxt.err)
|
||||
<% if (splitChunks.layouts) { %>async <% } %>errorChanged () {
|
||||
if (this.nuxt.err) {
|
||||
<% if (loading) { %>
|
||||
if (this.$loading) {
|
||||
if (this.$loading.fail) {
|
||||
this.$loading.fail(this.nuxt.err)
|
||||
}
|
||||
if (this.$loading.finish) {
|
||||
this.$loading.finish()
|
||||
}
|
||||
}
|
||||
if (this.$loading.finish) {
|
||||
this.$loading.finish()
|
||||
<% } %>
|
||||
let errorLayout = (NuxtError.options || NuxtError).layout;
|
||||
|
||||
if (typeof errorLayout === 'function') {
|
||||
errorLayout = errorLayout(this.context)
|
||||
}
|
||||
<% if (splitChunks.layouts) { %>
|
||||
await this.loadLayout(errorLayout)
|
||||
<% } %>
|
||||
this.setLayout(errorLayout)
|
||||
}
|
||||
},
|
||||
<% } %>
|
||||
<% if (features.layouts) { %>
|
||||
<% if (splitChunks.layouts) { %>
|
||||
setLayout (layout) {
|
||||
@@ -285,24 +296,43 @@ export default {
|
||||
<% } /* splitChunks.layouts */ %>
|
||||
<% } /* features.layouts */ %>
|
||||
<% if (isFullStatic) { %>
|
||||
getRouterBase() {
|
||||
return withoutTrailingSlash(this.$router.options.base)
|
||||
},
|
||||
getRoutePath(route = '/') {
|
||||
const base = this.getRouterBase()
|
||||
return withoutTrailingSlash(withoutBase(parsePath(route).pathname, base))
|
||||
},
|
||||
getStaticAssetsPath(route = '/') {
|
||||
const { staticAssetsBase } = window.<%= globals.context %>
|
||||
|
||||
return urlJoin(staticAssetsBase, this.getRoutePath(route))
|
||||
},
|
||||
<% if (nuxtOptions.generate.manifest) { %>
|
||||
async fetchStaticManifest() {
|
||||
return window.__NUXT_IMPORT__('manifest.js', normalizeURL(urlJoin(this.getStaticAssetsPath(), 'manifest.js')))
|
||||
},
|
||||
<% } %>
|
||||
setPagePayload(payload) {
|
||||
this._pagePayload = payload
|
||||
this._payloadFetchIndex = 0
|
||||
this._fetchCounters = {}
|
||||
},
|
||||
async fetchPayload(route) {
|
||||
const { staticAssetsBase } = window.<%= globals.context %>
|
||||
const base = (this.$router.options.base || '').replace(/\/+$/, '')
|
||||
if (base && route.startsWith(base)) {
|
||||
route = route.substr(base.length)
|
||||
async fetchPayload(route, prefetch) {
|
||||
const path = decode(this.getRoutePath(route))
|
||||
<% if (nuxtOptions.generate.manifest) { %>
|
||||
const manifest = await this.fetchStaticManifest()
|
||||
if (!manifest.routes.includes(path)) {
|
||||
if (!prefetch) { this.setPagePayload(false) }
|
||||
throw new Error(`Route ${path} is not pre-rendered`)
|
||||
}
|
||||
route = (route.replace(/\/+$/, '') || '/').split('?')[0].split('#')[0]
|
||||
const src = urlJoin(base, staticAssetsBase, route, 'payload.js')
|
||||
<% } %>
|
||||
const src = urlJoin(this.getStaticAssetsPath(route), 'payload.js')
|
||||
try {
|
||||
const payload = await window.__NUXT_IMPORT__(decodeURI(route), encodeURI(src))
|
||||
this.setPagePayload(payload)
|
||||
const payload = await window.__NUXT_IMPORT__(path, normalizeURL(src))
|
||||
if (!prefetch) { this.setPagePayload(payload) }
|
||||
return payload
|
||||
} catch (err) {
|
||||
this.setPagePayload(false)
|
||||
if (!prefetch) { this.setPagePayload(false) }
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
+46
-24
@@ -14,12 +14,16 @@ import {
|
||||
<% if (features.transitions || features.asyncData || features.fetch) { %>getLocation,<% } %>
|
||||
compile,
|
||||
getQueryDiff,
|
||||
globalHandleError
|
||||
globalHandleError,
|
||||
isSamePath,
|
||||
urlJoin
|
||||
} from './utils.js'
|
||||
import { createApp<% if (features.layouts) { %>, NuxtError<% } %> } from './index.js'
|
||||
<% if (features.fetch) { %>import fetchMixin from './mixins/fetch.client'<% } %>
|
||||
import NuxtLink from './components/nuxt-link.<%= features.clientPrefetch ? "client" : "server" %>.js' // should be included after ./index.js
|
||||
<% if (isFullStatic) { %>import './jsonp'<% } %>
|
||||
<% if (isFullStatic) { %>import { installJsonp } from './jsonp'<% } %>
|
||||
|
||||
<% if (isFullStatic) { %>installJsonp()<% } %>
|
||||
|
||||
<% if (features.fetch) { %>
|
||||
// Fetch mixin
|
||||
@@ -44,6 +48,11 @@ let router
|
||||
// Try to rehydrate SSR data from window
|
||||
const NUXT = window.<%= globals.context %> || {}
|
||||
|
||||
const $config = NUXT.config || {}
|
||||
if ($config._app) {
|
||||
__webpack_public_path__ = urlJoin($config._app.cdnURL, $config._app.assetsPath)
|
||||
}
|
||||
|
||||
Object.assign(Vue.config, <%= serialize(vue.config) %>)<%= isTest ? '// eslint-disable-line' : '' %>
|
||||
|
||||
<% if (nuxtOptions.render.ssrLog) { %>
|
||||
@@ -220,10 +229,8 @@ function applySSRData (Component, ssrData) {
|
||||
}
|
||||
|
||||
// Get matched components
|
||||
function resolveComponents (router) {
|
||||
const path = getLocation(router.options.base, router.options.mode)
|
||||
|
||||
return flatMapComponents(router.match(path), async (Component, _, match, key, index) => {
|
||||
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()
|
||||
@@ -525,10 +532,12 @@ async function render (to, from, next) {
|
||||
<% } %>
|
||||
|
||||
<% if (isFullStatic && store) { %>
|
||||
// Replay store mutations, catching to avoid error page on SPA fallback
|
||||
promises.push(this.fetchPayload(to.path).then(payload => {
|
||||
payload.mutations.forEach(m => { this.$store.commit(m[0], m[1]) })
|
||||
}).catch(err => null))
|
||||
if (!this.isPreview && !spaFallback) {
|
||||
// Replay store mutations, catching to avoid error page on SPA fallback
|
||||
promises.push(this.fetchPayload(to.path).then(payload => {
|
||||
payload.mutations.forEach(m => { this.$store.commit(m[0], m[1]) })
|
||||
}).catch(err => null))
|
||||
}
|
||||
<% } %>
|
||||
|
||||
// Check disabled page loading
|
||||
@@ -610,7 +619,7 @@ function normalizeComponents (to, ___) {
|
||||
}
|
||||
|
||||
<% if (features.layouts) { %>
|
||||
function setLayoutForNextPage (to) {
|
||||
<% if (splitChunks.layouts) { %>async <% } %>function setLayoutForNextPage (to) {
|
||||
// Set layout
|
||||
let hasError = Boolean(this.$options.nuxt.err)
|
||||
if (this._hadError && this._dateLastError === this.$options.nuxt.dateErr) {
|
||||
@@ -623,6 +632,9 @@ function setLayoutForNextPage (to) {
|
||||
if (typeof layout === 'function') {
|
||||
layout = layout(app.context)
|
||||
}
|
||||
<% if (splitChunks.layouts) { %>
|
||||
await this.loadLayout(layout)
|
||||
<% } %>
|
||||
this.setLayout(layout)
|
||||
}
|
||||
<% } %>
|
||||
@@ -644,6 +656,8 @@ function fixPrepatch (to, ___) {
|
||||
const instances = getMatchedComponentsInstances(to)
|
||||
const Components = getMatchedComponents(to)
|
||||
|
||||
let triggerScroll = <%= features.transitions ? 'false' : 'true' %>
|
||||
|
||||
Vue.nextTick(() => {
|
||||
instances.forEach((instance, i) => {
|
||||
if (!instance || instance._isDestroyed) {
|
||||
@@ -661,12 +675,17 @@ function fixPrepatch (to, ___) {
|
||||
Vue.set(instance.$data, key, newData[key])
|
||||
}
|
||||
|
||||
// Ensure to trigger scroll event after calling scrollBehavior
|
||||
window.<%= globals.nuxt %>.$nextTick(() => {
|
||||
window.<%= globals.nuxt %>.$emit('triggerScroll')
|
||||
})
|
||||
triggerScroll = true
|
||||
}
|
||||
})
|
||||
|
||||
if (triggerScroll) {
|
||||
// Ensure to trigger scroll event after calling scrollBehavior
|
||||
window.<%= globals.nuxt %>.$nextTick(() => {
|
||||
window.<%= globals.nuxt %>.$emit('triggerScroll')
|
||||
})
|
||||
}
|
||||
|
||||
checkForErrors(this)
|
||||
<% if (isDev) { %>
|
||||
// Hot reloading
|
||||
@@ -827,7 +846,7 @@ async function mountApp (__app) {
|
||||
// Load page chunk
|
||||
if (!NUXT.data && NUXT.serverRendered) {
|
||||
try {
|
||||
const payload = await _app.fetchPayload(_app.context.route.path)
|
||||
const payload = await _app.fetchPayload(NUXT.routePath || _app.context.route.path)
|
||||
Object.assign(NUXT, payload)
|
||||
} catch (err) {}
|
||||
}
|
||||
@@ -863,7 +882,7 @@ async function mountApp (__app) {
|
||||
}
|
||||
<% if (features.transitions) { %>
|
||||
// Resolve route components
|
||||
const Components = await Promise.all(resolveComponents(router))
|
||||
const Components = await Promise.all(resolveComponents(app.context.route))
|
||||
|
||||
// Enable transitions
|
||||
_app.setTransitions = _app.$options.nuxt.setTransitions.bind(_app)
|
||||
@@ -872,7 +891,7 @@ async function mountApp (__app) {
|
||||
_lastPaths = router.currentRoute.matched.map(route => compile(route.path)(router.currentRoute.params))
|
||||
}
|
||||
<% } else if (features.asyncData || features.fetch) { %>
|
||||
await Promise.all(resolveComponents(router))
|
||||
await Promise.all(resolveComponents(app.context.route))
|
||||
<% } %>
|
||||
// Initialize error handler
|
||||
_app.$loading = {} // To avoid error while _app.$nuxt does not exist
|
||||
@@ -885,14 +904,17 @@ async function mountApp (__app) {
|
||||
router.beforeEach(render.bind(_app))
|
||||
|
||||
// Fix in static: remove trailing slash to force hydration
|
||||
if (process.static && NUXT.serverRendered && NUXT.routePath !== '/' && NUXT.routePath.slice(-1) !== '/' && _app.context.route.path.slice(-1) === '/') {
|
||||
_app.context.route.path = _app.context.route.path.replace(/\/+$/, '')
|
||||
// Full static, if server-rendered: hydrate, to allow custom redirect to generated page
|
||||
<% if (isFullStatic) { %>
|
||||
if (NUXT.serverRendered) {
|
||||
return mount()
|
||||
}
|
||||
// If page already is server rendered and it was done on the same route path as client side render
|
||||
if (NUXT.serverRendered && NUXT.routePath === _app.context.route.path) {
|
||||
mount()
|
||||
return
|
||||
<% } else { %>
|
||||
// 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 = () => {
|
||||
|
||||
+2
-1
@@ -7,7 +7,8 @@
|
||||
|
||||
<div class="title">{{ message }}</div>
|
||||
<p v-if="statusCode === 404" class="description">
|
||||
<NuxtLink class="error-link" to="/"><%= messages.back_to_home %></NuxtLink>
|
||||
<a v-if="typeof $route === 'undefined'" class="error-link" href="/"><% messages.back_to_home %></a>
|
||||
<NuxtLink v-else class="error-link" to="/"><%= messages.back_to_home %></NuxtLink>
|
||||
</p>
|
||||
<% if(debug) { %>
|
||||
<p class="description" v-else><%= messages.client_error_details %></p>
|
||||
|
||||
+3
-2
@@ -17,7 +17,7 @@ const cancelIdleCallback = window.cancelIdleCallback || function (id) {
|
||||
|
||||
const observer = window.IntersectionObserver && new window.IntersectionObserver((entries) => {
|
||||
entries.forEach(({ intersectionRatio, target: link }) => {
|
||||
if (intersectionRatio <= 0) {
|
||||
if (intersectionRatio <= 0 || !link.__prefetch) {
|
||||
return
|
||||
}
|
||||
link.__prefetch()
|
||||
@@ -111,7 +111,8 @@ export default {
|
||||
// Preload the data only if not in preview mode
|
||||
if (!this.$root.isPreview) {
|
||||
const { href } = this.$router.resolve(this.to, this.$route, this.append)
|
||||
this.$nuxt.fetchPayload(href).catch(() => {})
|
||||
if (this.<%= globals.nuxt %>)
|
||||
this.<%= globals.nuxt %>.fetchPayload(href, true).catch(() => {})
|
||||
}
|
||||
<% } %>
|
||||
<% if (router.linkPrefetchedClass) { %>
|
||||
|
||||
+51
-16
@@ -1,4 +1,5 @@
|
||||
import Vue from 'vue'
|
||||
<% if (store) { %>import Vuex from 'vuex'<% } %>
|
||||
<% if (features.meta) { %>import Meta from 'vue-meta'<% } %>
|
||||
<% if (features.componentClientOnly) { %>import ClientOnly from 'vue-client-only'<% } %>
|
||||
<% if (features.deprecations) { %>import NoSsr from 'vue-no-ssr'<% } %>
|
||||
@@ -43,6 +44,17 @@ Vue.component(NuxtChild.name, NuxtChild)
|
||||
// Component: <Nuxt>
|
||||
Vue.component(Nuxt.name, Nuxt)
|
||||
|
||||
Object.defineProperty(Vue.prototype, '<%= globals.nuxt %>', {
|
||||
get() {
|
||||
const globalNuxt = this.$root.$options.<%= globals.nuxt %>
|
||||
if (process.client && !globalNuxt && typeof window !== 'undefined') {
|
||||
return window.<%= globals.nuxt %>
|
||||
}
|
||||
return globalNuxt
|
||||
},
|
||||
configurable: true
|
||||
})
|
||||
|
||||
<% if (features.meta) {
|
||||
// vue-meta configuration
|
||||
const vueMetaOptions = {
|
||||
@@ -66,8 +78,21 @@ const defaultTransition = <%=
|
||||
%><%= isTest ? '// eslint-disable-line' : '' %>
|
||||
<% } %>
|
||||
|
||||
<% if (store) { %>
|
||||
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)
|
||||
const router = await createRouter(ssrContext, config)
|
||||
|
||||
<% if (store) { %>
|
||||
const store = createStore(ssrContext)
|
||||
@@ -75,8 +100,7 @@ async function createApp(ssrContext, config = {}) {
|
||||
store.$router = router
|
||||
<% if (mode === 'universal') { %>
|
||||
// Fix SSR caveat https://github.com/nuxt/nuxt.js/issues/3757#issuecomment-414689141
|
||||
const registerModule = store.registerModule
|
||||
store.registerModule = (path, rawModule, options) => registerModule.call(store, path, rawModule, Object.assign({ preserveState: process.client }, options))
|
||||
store.registerModule = registerModule
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
@@ -243,22 +267,33 @@ async function createApp(ssrContext, config = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
// If server-side, wait for async component to be resolved first
|
||||
if (process.server && ssrContext && ssrContext.url) {
|
||||
await new Promise((resolve, reject) => {
|
||||
router.push(ssrContext.url, resolve, () => {
|
||||
// navigated to a different route in router guard
|
||||
const unregister = router.afterEach(async (to, from, next) => {
|
||||
// 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()
|
||||
})
|
||||
}
|
||||
app.context.route = await getRouteData(to)
|
||||
app.context.params = to.params || {}
|
||||
app.context.query = to.query || {}
|
||||
unregister()
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
<% if(store) { %>store,<% } %>
|
||||
|
||||
+6
-3
@@ -75,6 +75,9 @@ function importChunk(chunkId, src) {
|
||||
return promise
|
||||
}
|
||||
|
||||
window.__NUXT_JSONP__ = function (chunkId, exports) { chunks[chunkId] = exports }
|
||||
window.__NUXT_JSONP_CACHE__ = chunks
|
||||
window.__NUXT_IMPORT__ = importChunk
|
||||
export function installJsonp() {
|
||||
window.__NUXT_JSONP__ = function (chunkId, exports) { chunks[chunkId] = exports }
|
||||
window.__NUXT_JSONP_CACHE__ = chunks
|
||||
window.__NUXT_IMPORT__ = importChunk
|
||||
}
|
||||
|
||||
|
||||
+21
-5
@@ -1,5 +1,5 @@
|
||||
import Vue from 'vue'
|
||||
import { hasFetch, normalizeError, addLifecycleHook } from '../utils'
|
||||
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.<%= globals.context %>
|
||||
@@ -38,7 +38,7 @@ function created() {
|
||||
|
||||
// Hydrate component
|
||||
this._hydrated = true
|
||||
this._fetchKey = +this.$vnode.elm.dataset.fetchKey
|
||||
this._fetchKey = this.$vnode.elm.dataset.fetchKey
|
||||
const data = nuxtState.fetch[this._fetchKey]
|
||||
|
||||
// If fetch error
|
||||
@@ -60,12 +60,22 @@ function createdFullStatic() {
|
||||
if (typeof this.$options.fetchOnServer === 'function') {
|
||||
fetchedOnServer = this.$options.fetchOnServer.call(this) !== false
|
||||
}
|
||||
if (!fetchedOnServer || this.$nuxt.isPreview || !this.$nuxt._pagePayload) {
|
||||
if (!fetchedOnServer || this.<%= globals.nuxt %>.isPreview || !this.<%= globals.nuxt %>._pagePayload) {
|
||||
return
|
||||
}
|
||||
this._hydrated = true
|
||||
this._fetchKey = this.$nuxt._payloadFetchIndex++
|
||||
const data = this.$nuxt._pagePayload.fetch[this._fetchKey]
|
||||
|
||||
const defaultKey = this.$options._scopeId || this.$options.name || ''
|
||||
const getCounter = createGetCounter(this.<%= globals.nuxt %>._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))
|
||||
}
|
||||
|
||||
const data = this.<%= globals.nuxt %>._pagePayload.fetch[this._fetchKey]
|
||||
|
||||
// If fetch error
|
||||
if (data && data._error) {
|
||||
@@ -73,6 +83,12 @@ function createdFullStatic() {
|
||||
return
|
||||
}
|
||||
|
||||
// If there is a missing payload
|
||||
if (!data) {
|
||||
this.$fetch()
|
||||
return
|
||||
}
|
||||
|
||||
// Merge data
|
||||
for (const key in data) {
|
||||
Vue.set(this.$data, key, data[key])
|
||||
|
||||
+21
-3
@@ -1,5 +1,5 @@
|
||||
import Vue from 'vue'
|
||||
import { hasFetch, normalizeError, addLifecycleHook } from '../utils'
|
||||
import { hasFetch, normalizeError, addLifecycleHook, purifyData, createGetCounter } from '../utils'
|
||||
|
||||
async function serverPrefetch() {
|
||||
if (!this._fetchOnServer) {
|
||||
@@ -19,14 +19,20 @@ async function serverPrefetch() {
|
||||
|
||||
|
||||
// Define an ssrKey for hydration
|
||||
this._fetchKey = this.$ssrContext.nuxt.fetch.length
|
||||
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.push(this.$fetchState.error ? { _error: this.$fetchState.error } : this._data)
|
||||
<% if (debug) { %>
|
||||
if (this.$ssrContext.nuxt.fetch[this._fetchKey] !== undefined) {
|
||||
console.warn(`Duplicate fetch key detected (${this._fetchKey}). This may lead to unexpected results.`)
|
||||
}
|
||||
<% } %>
|
||||
this.$ssrContext.nuxt.fetch[this._fetchKey] =
|
||||
this.$fetchState.error ? { _error: this.$fetchState.error } : purifyData(this._data)
|
||||
}
|
||||
|
||||
export default {
|
||||
@@ -41,6 +47,18 @@ export default {
|
||||
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,
|
||||
|
||||
+22
-9
@@ -1,5 +1,6 @@
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import { normalizeURL, decode } from 'ufo'
|
||||
import { interopDefault } from './utils'<%= isTest ? '// eslint-disable-line no-unused-vars' : '' %>
|
||||
import scrollBehavior from './router.scrollBehavior.js'
|
||||
|
||||
@@ -50,7 +51,7 @@ import scrollBehavior from './router.scrollBehavior.js'
|
||||
res += firstIndent + 'path: ' + JSON.stringify(route.path)
|
||||
res += (route.components) ? nextIndent + 'components: {' + resMap + '\n' + baseIndent + tab + '}' : ''
|
||||
res += (route.component) ? nextIndent + 'component: ' + route._name : ''
|
||||
res += (route.redirect) ? nextIndent + 'redirect: ' + JSON.stringify(route.redirect) : ''
|
||||
res += (route.redirect) ? nextIndent + 'redirect: ' + (typeof route.redirect === 'function' ? serialize(route.redirect) : JSON.stringify(route.redirect)) : ''
|
||||
res += (route.meta) ? nextIndent + 'meta: ' + JSON.stringify(route.meta) : ''
|
||||
res += (typeof route.props !== 'undefined') ? nextIndent + 'props: ' + (typeof route.props === 'function' ? serialize(route.props) : JSON.stringify(route.props)) : ''
|
||||
res += (typeof route.caseSensitive !== 'undefined') ? nextIndent + 'caseSensitive: ' + JSON.stringify(route.caseSensitive) : ''
|
||||
@@ -82,18 +83,13 @@ const _routes = recursiveRoutes(router.routes, ' ', _components, 1)
|
||||
}
|
||||
}).join('\n')%>
|
||||
|
||||
// TODO: remove in Nuxt 3
|
||||
const emptyFn = () => {}
|
||||
const originalPush = Router.prototype.push
|
||||
Router.prototype.push = function push (location, onComplete = emptyFn, onAbort) {
|
||||
return originalPush.call(this, location, onComplete, onAbort)
|
||||
}
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
export const routerOptions = {
|
||||
mode: '<%= router.mode %>',
|
||||
base: decodeURI('<%= router.base %>'),
|
||||
base: '<%= router.base %>',
|
||||
linkActiveClass: '<%= router.linkActiveClass %>',
|
||||
linkExactActiveClass: '<%= router.linkExactActiveClass %>',
|
||||
scrollBehavior,
|
||||
@@ -105,6 +101,23 @@ export const routerOptions = {
|
||||
fallback: <%= router.fallback %>
|
||||
}
|
||||
|
||||
export function createRouter () {
|
||||
return new Router(routerOptions)
|
||||
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
|
||||
}
|
||||
|
||||
+24
-22
@@ -2,53 +2,55 @@
|
||||
<%= isTest ? '/* eslint-disable quotes, semi, indent, comma-spacing, key-spacing, object-curly-spacing, space-before-function-paren */' : '' %>
|
||||
export default <%= serializeFunction(router.scrollBehavior) %>
|
||||
<%= isTest ? '/* eslint-enable quotes, semi, indent, comma-spacing, key-spacing, object-curly-spacing, space-before-function-paren */' : '' %>
|
||||
<% } else { %>import { getMatchedComponents } from './utils'
|
||||
<% } else { %>import { getMatchedComponents, setScrollRestoration } from './utils'
|
||||
|
||||
if (process.client) {
|
||||
if ('scrollRestoration' in window.history) {
|
||||
window.history.scrollRestoration = 'manual'
|
||||
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', () => {
|
||||
window.history.scrollRestoration = 'auto'
|
||||
setScrollRestoration('auto')
|
||||
})
|
||||
|
||||
// Setting scrollRestoration to manual again when returning to this page.
|
||||
window.addEventListener('load', () => {
|
||||
window.history.scrollRestoration = 'manual'
|
||||
setScrollRestoration('manual')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default function (to, from, savedPosition) {
|
||||
// if the returned position is falsy or an empty object,
|
||||
// will retain current scroll position.
|
||||
let position = false
|
||||
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)
|
||||
}
|
||||
|
||||
// if no children detected and scrollToTop is not explicitly disabled
|
||||
const Pages = getMatchedComponents(to)
|
||||
if (
|
||||
Pages.length < 2 &&
|
||||
Pages.every(Page => Page.options.scrollToTop !== false)
|
||||
) {
|
||||
// scroll to the top of the page
|
||||
position = { x: 0, y: 0 }
|
||||
} else if (Pages.some(Page => Page.options.scrollToTop)) {
|
||||
// if one of the children has scrollToTop option set to true
|
||||
position = { x: 0, y: 0 }
|
||||
}
|
||||
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.<%= globals.nuxt %>
|
||||
|
||||
// triggerScroll is only fired when a new component is loaded
|
||||
if (to.path === from.path && to.hash !== from.hash) {
|
||||
if (
|
||||
// Initial load (vuejs/vue-router#3199)
|
||||
!isRouteChanged ||
|
||||
// Route hash changes
|
||||
(to.path === from.path && to.hash !== from.hash)
|
||||
) {
|
||||
nuxt.$nextTick(() => nuxt.$emit('triggerScroll'))
|
||||
}
|
||||
|
||||
|
||||
+40
-17
@@ -1,5 +1,5 @@
|
||||
import { stringify } from 'querystring'
|
||||
import Vue from 'vue'
|
||||
import { joinURL, normalizeURL, withQuery } from 'ufo'
|
||||
<% if (fetch.server) { %>import fetch from 'node-fetch'<% } %>
|
||||
<% if (features.middleware) { %>import middleware from './middleware.js'<% } %>
|
||||
import {
|
||||
@@ -24,17 +24,31 @@ if (!Vue.__nuxt__fetch__mixin__) {
|
||||
}
|
||||
<% } %>
|
||||
|
||||
<% if (isDev) { %>
|
||||
if (!Vue.__original_use__) {
|
||||
Vue.__original_use__ = Vue.use
|
||||
Vue.__install_times__ = 0
|
||||
Vue.use = function (plugin, ...args) {
|
||||
plugin.__nuxt_external_installed__ = Vue._installedPlugins.includes(plugin)
|
||||
return Vue.__original_use__(plugin, ...args)
|
||||
}
|
||||
}
|
||||
if (Vue.__install_times__ === 2) {
|
||||
Vue.__install_times__ = 0
|
||||
Vue._installedPlugins = Vue._installedPlugins.filter(plugin => {
|
||||
return plugin.__nuxt_external_installed__ === true
|
||||
})
|
||||
}
|
||||
Vue.__install_times__++
|
||||
<% } %>
|
||||
|
||||
// Component: <NuxtLink>
|
||||
Vue.component(NuxtLink.name, NuxtLink)
|
||||
<% if (features.componentAliases) { %>Vue.component('NLink', NuxtLink)<% } %>
|
||||
|
||||
<% if (fetch.server) { %>if (!global.fetch) { global.fetch = fetch }<% } %>
|
||||
|
||||
const noopApp = () => new Vue({ render: h => h('div') })
|
||||
|
||||
function urlJoin () {
|
||||
return Array.prototype.slice.call(arguments).join('/').replace(/\/+/g, '/')
|
||||
}
|
||||
const noopApp = () => new Vue({ render: h => h('div', { domProps: { id: '<%= globals.id %>' } }) })
|
||||
|
||||
const createNext = ssrContext => (opts) => {
|
||||
// If static target, render on client-side
|
||||
@@ -43,19 +57,19 @@ const createNext = ssrContext => (opts) => {
|
||||
ssrContext.nuxt.serverRendered = false
|
||||
return
|
||||
}
|
||||
opts.query = stringify(opts.query)
|
||||
opts.path = opts.path + (opts.query ? '?' + opts.query : '')
|
||||
const routerBase = '<%= router.base %>'
|
||||
if (!opts.path.startsWith('http') && (routerBase !== '/' && !opts.path.startsWith(routerBase))) {
|
||||
opts.path = urlJoin(routerBase, opts.path)
|
||||
let fullPath = withQuery(opts.path, opts.query)
|
||||
const $config = ssrContext.runtimeConfig || {}
|
||||
const routerBase = ($config._app && $config._app.basePath) || '<%= router.base %>'
|
||||
if (!fullPath.startsWith('http') && (routerBase !== '/' && !fullPath.startsWith(routerBase))) {
|
||||
fullPath = joinURL(routerBase, fullPath)
|
||||
}
|
||||
// Avoid loop redirect
|
||||
if (opts.path === ssrContext.url) {
|
||||
if (decodeURI(fullPath) === decodeURI(ssrContext.url)) {
|
||||
ssrContext.redirected = false
|
||||
return
|
||||
}
|
||||
ssrContext.res.writeHead(opts.status, {
|
||||
Location: opts.path
|
||||
Location: normalizeURL(fullPath)
|
||||
})
|
||||
ssrContext.res.end()
|
||||
}
|
||||
@@ -72,15 +86,24 @@ export default async (ssrContext) => {
|
||||
// Used for beforeNuxtRender({ Components, nuxtState })
|
||||
ssrContext.beforeRenderFns = []
|
||||
// Nuxt object (window.{{globals.context}}, defaults to window.__NUXT__)
|
||||
ssrContext.nuxt = { <% if (features.layouts) { %>layout: 'default', <% } %>data: [], <% if (features.fetch) { %>fetch: [], <% } %>error: null<%= (store ? ', state: null' : '') %>, serverRendered: true, routePath: '' }
|
||||
ssrContext.nuxt = { <% if (features.layouts) { %>layout: 'default', <% } %>data: [], <% if (features.fetch) { %>fetch: {}, <% } %>error: null<%= (store ? ', state: null' : '') %>, serverRendered: true, routePath: '' }
|
||||
<% if (features.fetch) { %>
|
||||
ssrContext.fetchCounters = {}
|
||||
<% } %>
|
||||
|
||||
// Remove query from url is static target
|
||||
if (process.static && ssrContext.url) {
|
||||
<% if (isFullStatic) { %>
|
||||
if (ssrContext.url) {
|
||||
ssrContext.url = ssrContext.url.split('?')[0]
|
||||
}
|
||||
<% } %>
|
||||
// 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 ? ', store' : '') %> } = await createApp(ssrContext, { ...ssrContext.runtimeConfig.public, ...ssrContext.runtimeConfig.private })
|
||||
const { app, router<%= (store ? ', 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
|
||||
@@ -133,7 +156,7 @@ export default async (ssrContext) => {
|
||||
<% if (debug) { %>const s = Date.now()<% } %>
|
||||
|
||||
// Components are already resolved by setContext -> getRouteData (app/utils.js)
|
||||
const Components = getMatchedComponents(router.match(ssrContext.url))
|
||||
const Components = getMatchedComponents(app.context.route)
|
||||
|
||||
<% if (store) { %>
|
||||
/*
|
||||
|
||||
+2
-2
@@ -94,10 +94,10 @@ function resolveStoreModules (moduleData, filename) {
|
||||
// If src is a known Vuex property
|
||||
if (VUEX_PROPERTIES.includes(moduleName)) {
|
||||
const property = moduleName
|
||||
const storeModule = getStoreModule(store, namespaces, { isProperty: true })
|
||||
const propertyStoreModule = getStoreModule(store, namespaces, { isProperty: true })
|
||||
|
||||
// Replace state since it's a function
|
||||
mergeProperty(storeModule, moduleData, property)
|
||||
mergeProperty(propertyStoreModule, moduleData, property)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+77
-95
@@ -1,4 +1,5 @@
|
||||
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)
|
||||
@@ -9,6 +10,15 @@ if (process.client) {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -25,6 +35,24 @@ export function interopDefault (promise) {
|
||||
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) {
|
||||
@@ -118,7 +146,29 @@ export function resolveRouteComponents (route, fn) {
|
||||
flatMapComponents(route, async (Component, instance, match, key) => {
|
||||
// If component is a function, resolve it
|
||||
if (typeof Component === 'function' && !Component.options) {
|
||||
Component = await Component()
|
||||
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
|
||||
@@ -152,16 +202,18 @@ export async function setContext (app, context) {
|
||||
<%= (store ? 'store: app.store,' : '') %>
|
||||
payload: context.payload,
|
||||
error: context.error,
|
||||
base: '<%= router.base %>',
|
||||
base: app.router.options.base,
|
||||
env: <%= JSON.stringify(env) %><%= isTest ? '// eslint-disable-line' : '' %>
|
||||
}
|
||||
// Only set once
|
||||
if (!process.static && context.req) {
|
||||
<% if (!isFullStatic) { %>
|
||||
if (context.req) {
|
||||
app.context.req = context.req
|
||||
}
|
||||
if (!process.static && context.res) {
|
||||
if (context.res) {
|
||||
app.context.res = context.res
|
||||
}
|
||||
<% } %>
|
||||
if (context.ssrContext) {
|
||||
app.context.ssrContext = context.ssrContext
|
||||
}
|
||||
@@ -189,7 +241,7 @@ export async function setContext (app, context) {
|
||||
status
|
||||
})
|
||||
} else {
|
||||
path = formatUrl(path, query)
|
||||
path = withQuery(path, query)
|
||||
if (process.server) {
|
||||
app.context.next({
|
||||
path,
|
||||
@@ -278,15 +330,20 @@ export function promisify (fn, context) {
|
||||
|
||||
// Imported from vue-router
|
||||
export function getLocation (base, mode) {
|
||||
let path = decodeURI(window.location.pathname)
|
||||
if (mode === 'hash') {
|
||||
return window.location.hash.replace(/^#\//, '')
|
||||
}
|
||||
// To get matched with sanitized router.base add trailing slash
|
||||
if (base && (path.endsWith('/') ? path : path + '/').startsWith(base)) {
|
||||
|
||||
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)
|
||||
}
|
||||
return (path || '/') + window.location.search + window.location.hash
|
||||
|
||||
const fullPath = (path || '/') + window.location.search + window.location.hash
|
||||
|
||||
return normalizeURL(fullPath)
|
||||
}
|
||||
|
||||
// Imported from path-to-regexp
|
||||
@@ -559,86 +616,6 @@ function flags (options) {
|
||||
return options && options.sensitive ? '' : 'i'
|
||||
}
|
||||
|
||||
/**
|
||||
* Format given url, append query to url query string
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {string} query
|
||||
* @return {string}
|
||||
*/
|
||||
function formatUrl (url, query) {
|
||||
<% if (features.clientUseUrl) { %>
|
||||
url = new URL(url, top.location.href)
|
||||
for (const key in query) {
|
||||
const value = query[key]
|
||||
if (value == null) {
|
||||
continue
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
for (const arrayValue of value) {
|
||||
url.searchParams.append(key, arrayValue)
|
||||
}
|
||||
continue
|
||||
}
|
||||
url.searchParams.append(key, value)
|
||||
}
|
||||
url.searchParams.sort()
|
||||
return url.toString()
|
||||
<% } else { %>
|
||||
let protocol
|
||||
const index = url.indexOf('://')
|
||||
if (index !== -1) {
|
||||
protocol = url.substring(0, index)
|
||||
url = url.substring(index + 3)
|
||||
} else if (url.startsWith('//')) {
|
||||
url = url.substring(2)
|
||||
}
|
||||
|
||||
let parts = url.split('/')
|
||||
let result = (protocol ? protocol + '://' : '//') + parts.shift()
|
||||
|
||||
let path = parts.join('/')
|
||||
if (path === '' && parts.length === 1) {
|
||||
result += '/'
|
||||
}
|
||||
|
||||
let hash
|
||||
parts = path.split('#')
|
||||
if (parts.length === 2) {
|
||||
[path, hash] = parts
|
||||
}
|
||||
|
||||
result += path ? '/' + path : ''
|
||||
|
||||
if (query && JSON.stringify(query) !== '{}') {
|
||||
result += (url.split('?').length === 2 ? '&' : '?') + formatQuery(query)
|
||||
}
|
||||
result += hash ? '#' + hash : ''
|
||||
|
||||
return result
|
||||
<% } %>
|
||||
}
|
||||
<% if (!features.clientUseUrl) { %>
|
||||
/**
|
||||
* Transform data object to query string
|
||||
*
|
||||
* @param {object} query
|
||||
* @return {string}
|
||||
*/
|
||||
function formatQuery (query) {
|
||||
return Object.keys(query).sort().map((key) => {
|
||||
const val = query[key]
|
||||
if (val == null) {
|
||||
return ''
|
||||
}
|
||||
if (Array.isArray(val)) {
|
||||
return val.slice().map(val2 => [key, '=', val2].join('')).join('&')
|
||||
}
|
||||
return key + '=' + val
|
||||
}).filter(Boolean).join('&')
|
||||
}
|
||||
<% } %>
|
||||
|
||||
export function addLifecycleHook(vm, hook, fn) {
|
||||
if (!vm.$options[hook]) {
|
||||
vm.$options[hook] = []
|
||||
@@ -648,10 +625,15 @@ export function addLifecycleHook(vm, hook, fn) {
|
||||
}
|
||||
}
|
||||
|
||||
export const urlJoin = function urlJoin () {
|
||||
return [].slice
|
||||
.call(arguments)
|
||||
.join('/')
|
||||
.replace(/\/+/g, '/')
|
||||
.replace(':/', '://')
|
||||
export const urlJoin = joinURL
|
||||
|
||||
export const stripTrailingSlash = withoutTrailingSlash
|
||||
|
||||
export const isSamePath = _isSamePath
|
||||
|
||||
export function setScrollRestoration (newVal) {
|
||||
try {
|
||||
window.history.scrollRestoration = newVal;
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user