vue3结合ts从零实现vueuse的useRouteQuery方法

2024-04-18 0 832
目录
  • 使用 vueuse 的 useRouteQuery 碰到的问题
  • 从零实现一个 useRouteQuery
    • 1. 简易实现
    • 2. 完整实现

本文将使用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内容请搜索悠久资源网以前的文章或继续浏览下面的相关文章希望大家以后多多支持悠久资源网!

您可能感兴趣的文章:

  • Vue生态系统工具库Vueuse的使用示例详解
  • 一文详解VueUse中useAsyncState的实现原理
  • vue前端框架vueuse的useScroll函数使用源码分析
  • VueUse使用及造轮子选择对比示例详解
  • Vue3+vueuse实现放大镜示例详解
  • 5个可以加速开发的VueUse函数库(小结)
  • Vue新玩具VueUse的具体用法

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悠久资源 JavaScript vue3结合ts从零实现vueuse的useRouteQuery方法 https://www.u-9.cn/biancheng/javascript/186904.html

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务