Skip to content

useURLQuery

该函数允许您轻松地获取和设置URL查询参数,在响应式数据变化时,自动更新查询参数,在查询参数变化时,自动更新响应式数据。

为什么不使用vueuse的useRouteQuery?因为vueuse的useRouteQuery会在query变化时,会更新整个网站使用useRouteQuery定义的变量(spa)。而useURLQuery只会更新当前页面组件的变量。

实现源码
ts
import { ref, watch, computed, nextTick, type Ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';

type IQuery = string | number | string[] | null | undefined;

const queriesQueue = new Map<string, Record<string, IQuery>>();

export const useURLQuery = <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 || !isEncodeURIComponent) {
      currentPageQueries[name] = value;
    } else {
      currentPageQueries[name] = encodeURIComponent(value as string);
    }

    queriesQueue.set(currentPath, currentPageQueries);
  };

  watch(query, (value) => {
    setQueryQueue(value as IQuery);

    nextTick(() => {
      const currentPageQueries = queriesQueue.get(currentPath) || {};
      const { params, query: oldQuery, hash } = route;
      router[mode]({
        params,
        query: {
          ...oldQuery,
          ...currentPageQueries
        },
        hash
      });
    });
  });

  return query;
};

export const useURLQueryObject = <T extends Record<string, any>>(
  name: string,
  defaultValue?: T,
  options: {
    transform?: (value: string | undefined) => string;
    mode?: 'push' | 'replace';
    isEncodeURIComponent?: boolean;
  } = {}
) => {
  const str = useURLQuery<string | undefined>(name, undefined, options);

  const search = computed({
    get: () => {
      try {
        return str.value !== undefined ? JSON.parse(str.value) : defaultValue;
      } catch (error) {
        return defaultValue;
      }
    },
    set: (val: T) => {
      try {
        str.value = val !== undefined ? JSON.stringify(val) : undefined;
      } catch (error) {
        str.value = undefined;
      }
    }
  });

  if (defaultValue !== undefined) {
    search.value = defaultValue;
  }

  return search;
};

介绍

本文 useURLQuery 是基于vue-routeruseRoute实现的,因此可以在任何需要获取和设置URL查询参数的地方使用。
该函数接收三个参数:

  • name 字符串类型,查询参数的名称;
  • defaultValue 可选参数,任意类型,查询参数的默认值;
  • options 可选参数,对象类型,包含transformmodeisEncodeURIComponent三个可选属性:
    1. transform 转换函数,用于转换查询值的类型;
    2. mode 路由模式,可选值pushreplace,用于控制查询参数的更新方式;
    3. isEncodeURIComponent 布尔类型,是否对查询参数进行编码。

使用

vue
<template>
  <input v-model="name" />
</template>

<script setup>
import { useURLQuery } from 'useURLQuery.ts'

// 获取查询参数name,如果没有则使用默认值default
// 当name变化时,会自动更新查询参数,例如:http://localhost:8080/?name=xxx
const name = useURLQuery('name', 'default')
</script>

Released under the MIT License.