本文将使用vue3与ts从零实现一个类vueuse的useroutequery方法,接受基本相同的参数(移除了router与route参数),并解决vueuse的useroutequery方法存在的一些问题。
使用 vueuse 的 useroutequery 碰到的问题
问:为什么不使用vueuse的提供的useroutequery方法?
答:因为在使用<keepalive>保活的页面级组件之间切换时,在所有组件中使用vueuse的useroutequery方法定义的变量都会更新。
例如:打开a页面(保活)后,修改page为2,假设url现在为/pagea?page=2,然后切换到b页面,a页面将会触发watch且page将会更新为默认值,并且每次修改b页面的query都会触发a页面的query更新。代码如下。
import { useroutequery } from '@vueuse/router'
import { watch } from 'vue'
// 页面a (keepalive)
const page = useroutequery('page', 1, { transform: number })
watch(page, (value) => {
console.log('page a', value)
})
// 页面b (keepalive)
const pagesize = useroutequery('pagesize', 10, { transform: number })
watch(pagesize, (value) => {
console.log('page b', value)
})
查看vueuse的useroutequery的源码后发现它是根据router对象去保存query信息的,因此每次url的query变化时,所有已存在的相关变量都会更新。
从零实现一个 useroutequery
思考:
- 监听
route.query的变化,在值变化时更新响应式变量的值; - 监听响应式变量的变化,在值变化时修改
route.query的值; - 在
route.query变化时,判断是否是当前页面,不是则跳过更新过程。
1. 简易实现
import { watch, ref, type ref } from 'vue'
import { useroute, userouter } from 'vue-router'
type iquery = string | number | string[] | null | undefined
/**
* 获取当前页面的query
* @param name
* @param defaultvalue
* @param options
* @returns
*/
export const useroutequery = <t extends iquery, k extends iquery = t>(
name: string,
defaultvalue?: t,
options: {
transform?: (value: any) => k
mode?: 'push' | 'replace'
isencodeuricomponent?: boolean
} = {}
) => {
const { mode = 'push', transform = (value) => value, isencodeuricomponent = false } = options
const route = useroute()
const router = userouter()
const currentpath = route.path
const query = ref(defaultvalue) as ref<t | k>
watch(
() => route.query[name],
(value) => {
// 不是当前页面时不更新
if (route.path !== currentpath) {
return
}
if (value === undefined) {
query.value = defaultvalue as t
return
}
if (!isencodeuricomponent) {
query.value = transform(value)
return
}
query.value = transform(decodeuricomponent(value as string))
},
{ immediate: true })
watch(query, (value) => {
const { params, query: oldquery, hash } = route
router[mode]({
params,
query: {
...oldquery,
[name]: isencodeuricomponent ? encodeuricomponent(value as string) : value
},
hash
})
})
return query
}
实际使用过后,我们会发现以上实现存在一些问题:
多个变量同步修改时,route.query上只会保留最后一个修改的响应式变量,代码如下;
import { useroutequery } from '@/hooks/useroutequery'
const disabled = useroutequery('disabled', 0)
const title = useroutequery('title', '')
disabled.value = 1
title.value = 'test'
// 预想:?disabled=1&title=test
// 实际:?title=test
特殊值undefined、null经过encodeuricomponent处理后会转换为字符串格式,因此还会带在route.query上,如“?diabled=null&title=undefined”。
2. 完整实现
解决方案:
- 通过在函数外定义一个队列来保存所有需要更新的值,并异步更新
route.query; - 特殊值不使用
encodeuricomponent方法转义。
import { nexttick, watch, ref, type ref } from 'vue'
import { useroute, userouter } from 'vue-router'
type iquery = string | number | string[] | null | undefined
// 用来保存所有的query
const queriesqueue = new map<string, record<string, iquery>>()
/**
* 获取当前页面的query
* @param name
* @param defaultvalue
* @param options
* @returns
*/
export const useroutequery = <t extends iquery, k extends iquery = t>(
name: string,
defaultvalue?: t,
options: {
transform?: (value: any) => k
mode?: 'push' | 'replace'
isencodeuricomponent?: boolean
} = {}
) => {
const { mode = 'push', transform = (value) => value, isencodeuricomponent = false } = options
const route = useroute()
const router = userouter()
const currentpath = route.path
const query = ref(defaultvalue) as ref<t | k>
watch(
() => route.query[name],
(value) => {
if (route.path !== currentpath) {
return
}
if (value === undefined) {
query.value = defaultvalue as t
return
}
if (!isencodeuricomponent) {
query.value = transform(value)
return
}
query.value = transform(decodeuricomponent(value as string))
},
{ immediate: true })
const setqueryqueue = (value: iquery) => {
const currentpagequeries = queriesqueue.get(currentpath) || {}
// 特殊值不转义
if (value === null || value === undefined) {
currentpagequeries[name] = value
} else {
currentpagequeries[name] = isencodeuricomponent ? encodeuricomponent(value as string) : value
}
queriesqueue.set(currentpath, currentpagequeries)
}
watch(query, (value) => {
setqueryqueue(value as iquery)
// 异步更新
nexttick(() => {
// 获取当前页面所有的query
const currentpagequeries = queriesqueue.get(currentpath) || {}
const { params, query: oldquery, hash } = route
router[mode]({
params,
query: {
...oldquery,
...currentpagequeries
},
hash
})
})
})
return query
}
查看完整实现源码。
到此这篇关于vue3结合ts从零实现vueuse的useroutequery方法的文章就介绍到这了,更多相关vue实现vueuse的useroutequery内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论