mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-27 08:02:01 +00:00
feat: Add Page.watchQuery
This commit is contained in:
parent
fb44c2eb8e
commit
3d49d8d290
@ -6,13 +6,14 @@ import {
|
||||
sanitizeComponent,
|
||||
resolveRouteComponents,
|
||||
getMatchedComponents,
|
||||
getChangedComponentsInstances,
|
||||
getMatchedComponentsInstances,
|
||||
flatMapComponents,
|
||||
setContext,
|
||||
middlewareSeries,
|
||||
promisify,
|
||||
getLocation,
|
||||
compile
|
||||
compile,
|
||||
getQueryDiff
|
||||
} from './utils'
|
||||
|
||||
const noopData = () => { return {} }
|
||||
@ -20,7 +21,6 @@ const noopFetch = () => {}
|
||||
|
||||
// Global shared references
|
||||
let _lastPaths = []
|
||||
let _lastComponentsFiles = []
|
||||
let app
|
||||
let router
|
||||
<% if (store) { %>let store<% } %>
|
||||
@ -99,6 +99,8 @@ function mapTransitions(Components, to, from) {
|
||||
async function loadAsyncComponents (to, from, next) {
|
||||
// Check if route path changed (this._pathChanged), only if the page is not an error (for validate())
|
||||
this._pathChanged = !!app.nuxt.err || from.path !== to.path
|
||||
this._queryChanged = JSON.stringify(to.query) !== JSON.stringify(from.query)
|
||||
this._diffQuery = (this._queryChanged ? getQueryDiff(to.query, from.query) : [])
|
||||
|
||||
<% if (loading) { %>
|
||||
if (this._pathChanged && this.$loading.start) {
|
||||
@ -107,7 +109,24 @@ async function loadAsyncComponents (to, from, next) {
|
||||
<% } %>
|
||||
|
||||
try {
|
||||
await resolveRouteComponents(to)
|
||||
const Components = await resolveRouteComponents(to)
|
||||
<% if (loading) { %>
|
||||
if (!this._pathChanged && this._queryChanged) {
|
||||
// Add a marker on each component that it needs to refresh or not
|
||||
const startLoader = Components.some((Component) => {
|
||||
const watchQuery = Component.options.watchQuery
|
||||
if (watchQuery === true) return true
|
||||
if (Array.isArray(watchQuery)) {
|
||||
return watchQuery.some((key) => this._diffQuery[key])
|
||||
}
|
||||
return false
|
||||
})
|
||||
if (startLoader && this.$loading.start) {
|
||||
this.$loading.start()
|
||||
}
|
||||
}
|
||||
<% } %>
|
||||
// Call next()
|
||||
next()
|
||||
} catch (err) {
|
||||
err = err || {}
|
||||
@ -172,7 +191,8 @@ function callMiddleware (Components, context, layout) {
|
||||
}
|
||||
|
||||
async function render (to, from, next) {
|
||||
if (this._pathChanged === false) return next()
|
||||
if (this._pathChanged === false && this._queryChanged === false) return next()
|
||||
console.log('Render')
|
||||
|
||||
// nextCalled is true when redirected
|
||||
let nextCalled = false
|
||||
@ -259,7 +279,20 @@ async function render (to, from, next) {
|
||||
await Promise.all(Components.map((Component, i) => {
|
||||
// Check if only children route changed
|
||||
Component._path = compile(to.matched[i].path)(to.params)
|
||||
if (!this._hadError && this._isMounted && Component._path === _lastPaths[i]) {
|
||||
Component._dataRefresh = false
|
||||
// Check if Component need to be refreshed (call asyncData & fetch)
|
||||
// Only if its slug has changed or is watch query changes
|
||||
if (this._pathChanged && Component._path !== _lastPaths[i]) {
|
||||
Component._dataRefresh = true
|
||||
} else if (!this._pathChanged && 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])
|
||||
}
|
||||
}
|
||||
if (!this._hadError && this._isMounted && !Component._dataRefresh) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
@ -347,26 +380,22 @@ function showNextPage(to) {
|
||||
|
||||
// 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, from) {
|
||||
if (this._pathChanged === false) return
|
||||
function fixPrepatch(to, ___) {
|
||||
if (this._pathChanged === false && this._queryChanged === false) return
|
||||
|
||||
Vue.nextTick(() => {
|
||||
const instances = getChangedComponentsInstances(to, from)
|
||||
const instances = getMatchedComponentsInstances(to)
|
||||
|
||||
var dlen = to.matched.length - instances.length
|
||||
_lastComponentsFiles = instances.map((instance, i) => {
|
||||
if (!instance) return '';
|
||||
|
||||
if (_lastPaths[dlen + i] === instance.constructor._path && typeof instance.constructor.options.data === 'function') {
|
||||
instances.forEach((instance, i) => {
|
||||
if (!instance) return
|
||||
if (instance.constructor._dataRefresh && _lastPaths[i] === instance.constructor._path && typeof instance.constructor.options.data === 'function') {
|
||||
console.log('Refresh instance', instance)
|
||||
const newData = instance.constructor.options.data.call(instance)
|
||||
for (let key in newData) {
|
||||
Vue.set(instance.$data, key, newData[key])
|
||||
}
|
||||
}
|
||||
|
||||
return instance.constructor.options.__file
|
||||
})
|
||||
|
||||
showNextPage.call(this, to)
|
||||
<% if (isDev) { %>
|
||||
// Hot reloading
|
||||
@ -523,7 +552,6 @@ async function mountApp(__app) {
|
||||
if (Components.length) {
|
||||
_app.setTransitions(mapTransitions(Components, router.currentRoute))
|
||||
_lastPaths = router.currentRoute.matched.map(route => compile(route.path)(router.currentRoute.params))
|
||||
_lastComponentsFiles = Components.map(Component => Component.options.__file)
|
||||
}
|
||||
|
||||
// Initialize error handler
|
||||
|
@ -70,7 +70,7 @@ export default async ssrContext => {
|
||||
const renderErrorPage = async () => {
|
||||
// Load layout for error page
|
||||
let errLayout = (typeof NuxtError.layout === 'function' ? NuxtError.layout(app.context) : NuxtError.layout)
|
||||
ssrContext.nuxt.layout = errLayout || ''
|
||||
ssrContext.nuxt.layout = errLayout || 'default'
|
||||
await _app.loadLayout(errLayout)
|
||||
_app.setLayout(errLayout)
|
||||
await beforeRender()
|
||||
|
@ -65,48 +65,6 @@ export function getMatchedComponentsInstances(route) {
|
||||
}))
|
||||
}
|
||||
|
||||
function getRouteRecordWithParamNames (route) {
|
||||
return route.matched.map(m => {
|
||||
var paramNames = m.path.match(new RegExp(':[^\\/\\?]+', 'g'))
|
||||
if (paramNames !== null) {
|
||||
paramNames = paramNames.map(function (name) {
|
||||
return name.substring(1)
|
||||
})
|
||||
}
|
||||
return {
|
||||
routeRecord: m,
|
||||
paramNames: paramNames
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function getChangedComponentsInstances (to, from) {
|
||||
var records = getRouteRecordWithParamNames(to)
|
||||
var r = []
|
||||
var parentChange = false
|
||||
for (var i = 0; i < records.length; i++ ) {
|
||||
var paramNames = records[i].paramNames
|
||||
var instances = records[i].routeRecord.instances
|
||||
instances = Object.keys(instances).map(function (key) {
|
||||
return instances[key]
|
||||
})
|
||||
if (parentChange) {
|
||||
r = [].concat(r, instances)
|
||||
} else if (paramNames !== null) {
|
||||
for (var pi in paramNames) {
|
||||
var name = paramNames[pi]
|
||||
if (to.params[name] !== from.params[name]) {
|
||||
parentChange = true
|
||||
r = [].concat(r, instances)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
|
||||
export function flatMapComponents(route, fn) {
|
||||
return Array.prototype.concat.apply([], route.matched.map(function (m, index) {
|
||||
return Object.keys(m.components).map(function (key) {
|
||||
@ -116,7 +74,7 @@ export function flatMapComponents(route, fn) {
|
||||
}
|
||||
|
||||
export async function resolveRouteComponents(route) {
|
||||
await Promise.all(
|
||||
return await Promise.all(
|
||||
flatMapComponents(route, async (Component, _, match, key) => {
|
||||
// If component is a function, resolve it
|
||||
if (typeof Component === 'function' && !Component.options) {
|
||||
@ -255,6 +213,17 @@ export function compile(str, options) {
|
||||
return tokensToFunction(parse(str, 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
|
||||
}
|
||||
|
||||
/**
|
||||
* The main path matching regexp utility.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user