当前位置: 代码网 > it编程>编程语言>Javascript > vue3结合ts从零实现vueuse的useRouteQuery方法

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

2024年05月18日 Javascript 我要评论
本文将使用vue3与ts从零实现一个类vueuse的useroutequery方法,接受基本相同的参数(移除了router与route参数),并解决vueuse的useroutequery方法存在的一

本文将使用vue3ts从零实现一个类vueuseuseroutequery方法,接受基本相同的参数(移除了router与route参数),并解决vueuseuseroutequery方法存在的一些问题。

使用 vueuse 的 useroutequery 碰到的问题

问:为什么不使用vueuse的提供的useroutequery方法?

答:因为在使用<keepalive>保活的页面级组件之间切换时,在所有组件中使用vueuseuseroutequery方法定义的变量都会更新。

例如:打开a页面(保活)后,修改page为2,假设url现在为/pagea?page=2,然后切换到b页面,a页面将会触发watchpage将会更新为默认值,并且每次修改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)
})

查看vueuseuseroutequery的源码后发现它是根据router对象去保存query信息的,因此每次urlquery变化时,所有已存在的相关变量都会更新。

从零实现一个 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

特殊值undefinednull经过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内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com