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,
|
sanitizeComponent,
|
||||||
resolveRouteComponents,
|
resolveRouteComponents,
|
||||||
getMatchedComponents,
|
getMatchedComponents,
|
||||||
getChangedComponentsInstances,
|
getMatchedComponentsInstances,
|
||||||
flatMapComponents,
|
flatMapComponents,
|
||||||
setContext,
|
setContext,
|
||||||
middlewareSeries,
|
middlewareSeries,
|
||||||
promisify,
|
promisify,
|
||||||
getLocation,
|
getLocation,
|
||||||
compile
|
compile,
|
||||||
|
getQueryDiff
|
||||||
} from './utils'
|
} from './utils'
|
||||||
|
|
||||||
const noopData = () => { return {} }
|
const noopData = () => { return {} }
|
||||||
@ -20,7 +21,6 @@ const noopFetch = () => {}
|
|||||||
|
|
||||||
// Global shared references
|
// Global shared references
|
||||||
let _lastPaths = []
|
let _lastPaths = []
|
||||||
let _lastComponentsFiles = []
|
|
||||||
let app
|
let app
|
||||||
let router
|
let router
|
||||||
<% if (store) { %>let store<% } %>
|
<% if (store) { %>let store<% } %>
|
||||||
@ -99,6 +99,8 @@ function mapTransitions(Components, to, from) {
|
|||||||
async function loadAsyncComponents (to, from, next) {
|
async function loadAsyncComponents (to, from, next) {
|
||||||
// Check if route path changed (this._pathChanged), only if the page is not an error (for validate())
|
// 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._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 (loading) { %>
|
||||||
if (this._pathChanged && this.$loading.start) {
|
if (this._pathChanged && this.$loading.start) {
|
||||||
@ -107,7 +109,24 @@ async function loadAsyncComponents (to, from, next) {
|
|||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
try {
|
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()
|
next()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
err = err || {}
|
err = err || {}
|
||||||
@ -172,7 +191,8 @@ function callMiddleware (Components, context, layout) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function render (to, from, next) {
|
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
|
// nextCalled is true when redirected
|
||||||
let nextCalled = false
|
let nextCalled = false
|
||||||
@ -259,7 +279,20 @@ async function render (to, from, next) {
|
|||||||
await Promise.all(Components.map((Component, i) => {
|
await Promise.all(Components.map((Component, i) => {
|
||||||
// Check if only children route changed
|
// Check if only children route changed
|
||||||
Component._path = compile(to.matched[i].path)(to.params)
|
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()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,26 +380,22 @@ function showNextPage(to) {
|
|||||||
|
|
||||||
// When navigating on a different route but the same component is used, Vue.js
|
// 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
|
// Will not update the instance data, so we have to update $data ourselves
|
||||||
function fixPrepatch (to, from) {
|
function fixPrepatch(to, ___) {
|
||||||
if (this._pathChanged === false) return
|
if (this._pathChanged === false && this._queryChanged === false) return
|
||||||
|
|
||||||
Vue.nextTick(() => {
|
Vue.nextTick(() => {
|
||||||
const instances = getChangedComponentsInstances(to, from)
|
const instances = getMatchedComponentsInstances(to)
|
||||||
|
|
||||||
var dlen = to.matched.length - instances.length
|
instances.forEach((instance, i) => {
|
||||||
_lastComponentsFiles = instances.map((instance, i) => {
|
if (!instance) return
|
||||||
if (!instance) return '';
|
if (instance.constructor._dataRefresh && _lastPaths[i] === instance.constructor._path && typeof instance.constructor.options.data === 'function') {
|
||||||
|
console.log('Refresh instance', instance)
|
||||||
if (_lastPaths[dlen + i] === instance.constructor._path && typeof instance.constructor.options.data === 'function') {
|
|
||||||
const newData = instance.constructor.options.data.call(instance)
|
const newData = instance.constructor.options.data.call(instance)
|
||||||
for (let key in newData) {
|
for (let key in newData) {
|
||||||
Vue.set(instance.$data, key, newData[key])
|
Vue.set(instance.$data, key, newData[key])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return instance.constructor.options.__file
|
|
||||||
})
|
})
|
||||||
|
|
||||||
showNextPage.call(this, to)
|
showNextPage.call(this, to)
|
||||||
<% if (isDev) { %>
|
<% if (isDev) { %>
|
||||||
// Hot reloading
|
// Hot reloading
|
||||||
@ -523,7 +552,6 @@ async function mountApp(__app) {
|
|||||||
if (Components.length) {
|
if (Components.length) {
|
||||||
_app.setTransitions(mapTransitions(Components, router.currentRoute))
|
_app.setTransitions(mapTransitions(Components, router.currentRoute))
|
||||||
_lastPaths = router.currentRoute.matched.map(route => compile(route.path)(router.currentRoute.params))
|
_lastPaths = router.currentRoute.matched.map(route => compile(route.path)(router.currentRoute.params))
|
||||||
_lastComponentsFiles = Components.map(Component => Component.options.__file)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize error handler
|
// Initialize error handler
|
||||||
|
@ -70,7 +70,7 @@ export default async ssrContext => {
|
|||||||
const renderErrorPage = async () => {
|
const renderErrorPage = async () => {
|
||||||
// Load layout for error page
|
// Load layout for error page
|
||||||
let errLayout = (typeof NuxtError.layout === 'function' ? NuxtError.layout(app.context) : NuxtError.layout)
|
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)
|
await _app.loadLayout(errLayout)
|
||||||
_app.setLayout(errLayout)
|
_app.setLayout(errLayout)
|
||||||
await beforeRender()
|
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) {
|
export function flatMapComponents(route, fn) {
|
||||||
return Array.prototype.concat.apply([], route.matched.map(function (m, index) {
|
return Array.prototype.concat.apply([], route.matched.map(function (m, index) {
|
||||||
return Object.keys(m.components).map(function (key) {
|
return Object.keys(m.components).map(function (key) {
|
||||||
@ -116,7 +74,7 @@ export function flatMapComponents(route, fn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function resolveRouteComponents(route) {
|
export async function resolveRouteComponents(route) {
|
||||||
await Promise.all(
|
return await Promise.all(
|
||||||
flatMapComponents(route, async (Component, _, match, key) => {
|
flatMapComponents(route, async (Component, _, match, key) => {
|
||||||
// If component is a function, resolve it
|
// If component is a function, resolve it
|
||||||
if (typeof Component === 'function' && !Component.options) {
|
if (typeof Component === 'function' && !Component.options) {
|
||||||
@ -255,6 +213,17 @@ export function compile(str, options) {
|
|||||||
return tokensToFunction(parse(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.
|
* The main path matching regexp utility.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user