Code前端首页关于Code前端联系我们

Vue3怎么快速准确获取URL参数?动态路由/Query/非路由场景全解决

terry 6小时前 阅读数 88 #Vue

做Vue3开发的同学,不管是刚入门做练手项目,还是上线复杂的业务系统,获取URL参数肯定是逃不开的高频需求——比如跳转商品详情页要带ID,搜索结果页要存关键词、页码,分享页面要带上邀请码做裂变统计,偶尔可能还要在没引入Vue Router的静态页面里抓点参数应急,但很多人要么只会死记硬背一种方法,要么混用各种场景的API出了bug找半天,今天咱们就把所有常见场景拆得明明白白,连避坑点、封装优化这种进阶内容都给你覆盖到。

先理清楚:Vue3里遇到的URL参数有哪几种?

别上来就看代码,先把场景分清楚,不然拿到代码只会复制粘贴,换个参数名或者换个页面跳转方式就懵了,我们平时遇到的Vue3相关URL参数,其实主要分三类: 一类是动态路由参数,这种参数是嵌在路由路径中间的,比如/product/123里的123就是商品ID的动态参数,没有这个参数整个路由可能就匹配不上; 一类是Query查询参数,这种参数是跟在后面的键值对,多个参数用&连,比如/search?keyword=手机&page=2&priceMin=2000,哪怕没有这些参数,页面也能正常访问,只是可能展示默认内容; 还有一类是Hash查询参数,这种是跟在后面的开头的键值对,比如一些SPA单页应用跳转到第三方登录页后,第三方返回的Token会放在Hash里避免被Referer泄露,这种处理方式稍微特殊一点,但也得会。 哦对了,还有一种是不依赖Vue Router的原生场景,比如你用Vue CLI或Vite搭了个纯静态Vue3项目,没加路由插件,或者某个临时弹窗组件不想通过路由传参,直接从浏览器地址栏拿原生URL解析。

使用Vue Router 4的话,该怎么拿?(90%的业务场景都用这个)

Vue3官方推荐的路由管理插件是Vue Router 4,别再用Router 3了,兼容性有问题,而且Composition API的写法是真的香,不管你用的是Options API(老习惯或者项目重构过渡)还是Composition API,Router 4都提供了对应的获取方式。

Options API写法:$route.params和$route.query

如果你还在维护旧项目,或者刚从Vue2转过来暂时用不惯Composition API,那就直接用组件实例上的$route对象,这个对象和Vue2里的差不多,但底层已经适配了Vue3的响应式原理。 怎么拿动态路由参数?比如路由配置是这样的:

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
  {
    path: '/product/:id', // 动态参数用:开头
    name: 'ProductDetail',
    component: () => import('@/views/ProductDetail.vue')
  }
]
const router = createRouter({
  history: createWebHistory(),
  routes
})
export default router

那在ProductDetail.vue的Options API里,就用this.$route.params.id,比如可以在mounted钩子或者created钩子(Vue3里这俩钩子的时机和Vue2差不多,但现在Composition API的setup在beforeCreate和created之前执行,得注意初始化顺序)里打印或者请求接口:

// ProductDetail.vue Options API版本
export default {
  name: 'ProductDetail',
  data() {
    return {
      productInfo: null
    }
  },
  mounted() {
    // 先判断一下参数是否存在,避免接口报错
    if (this.$route.params.id) {
      this.getProductDetail(this.$route.params.id)
    }
  },
  methods: {
    getProductDetail(id) {
      // 这里是模拟请求接口
      console.log('请求商品详情,ID是:', id)
    }
  }
}

那Query参数呢?更简单,比如跳转到搜索页的时候传了keyword和page,在Search.vue里就用this.$route.query.keywordthis.$route.query.page,注意Query参数不管传的是数字还是布尔值,拿到的都是字符串类型,后期做计算或者判断的时候记得转换,比如parseInt(this.$route.query.page) || 1这种写法就很实用,默认显示第一页。 避坑点来了哦:在Options API里直接修改$route.params或者$route.query是没用的,路由不会更新,页面也不会重新请求数据,要是需要修改参数后刷新内容,应该用this.$router.push()或者this.$router.replace(),比如翻页的时候:

// Search.vue翻页方法
goToPage(page) {
  // push会留下历史记录,用户点浏览器后退可以回去;replace不会,适合像分享后跳转、翻页但不想让后退太麻烦的场景
  this.$router.push({
    name: 'Search', // 用name跳转比用path更稳定,后期改路由路径不用改组件代码
    query: {
      ...this.$route.query, // 保留之前的关键词、价格区间等参数
      page: page
    }
  })
}

Composition API写法:useRoute和useRouter(推荐!)

现在做Vue3新项目,肯定首选Composition API加<script setup>语法糖,代码更简洁,逻辑复用也更方便,这时候不能直接用this了,得从vue-router里引入两个核心的组合式函数:useRouteuseRouteruseRoute返回的就是当前路由的响应式对象,和this.$route功能一样,但因为是响应式的,在setup里解构的时候要注意!不能直接解构出paramsquery,不然会失去响应式,后期参数变了(比如从/search?keyword=手机&page=1变成/search?keyword=手机&page=2),解构出来的变量不会更新,导致页面不刷新。 正确的写法应该是要么全程用route.paramsroute.query,要么用Vue3的toRefsparamsquery转成响应式的Ref对象。 举个全程用route的例子,还是ProductDetail.vue:

// ProductDetail.vue Composition API <script setup>版本
import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
// 引入useRoute
const route = useRoute()
const productInfo = ref(null)
// 模拟请求接口
const getProductDetail = async (id) => {
  // 这里替换成真实的接口请求
  productInfo.value = await fetch(`/api/product/${id}`).then(res => res.json())
}
onMounted(() => {
  if (route.params.id) {
    getProductDetail(route.params.id)
  }
})

如果想用解构的方式,比如Search.vue里要用到很多Query参数,就用toRefs

// Search.vue Composition API <script setup>版本
import { onMounted, ref, watch, toRefs } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
// 这里用toRefs包裹params和query,解构出来的就是响应式Ref
const { keyword, page, priceMin, priceMax } = toRefs(route.query)
const searchResults = ref([])
const totalPages = ref(1)
// 模拟搜索接口
const getSearchResults = async () => {
  // 转成数字,注意空字符串的情况
  const currentPage = parseInt(page.value) || 1
  const minPrice = priceMin.value ? parseInt(priceMin.value) : 0
  const maxPrice = priceMax.value ? parseInt(priceMax.value) : Infinity
  // 这里替换成真实的接口请求
  const res = await fetch(`/api/search?keyword=${keyword.value || ''}&page=${currentPage}&priceMin=${minPrice}&priceMax=${maxPrice}`).then(res => res.json())
  searchResults.value = res.data
  totalPages.value = res.totalPages
}
// 第一次加载页面的时候调用
onMounted(() => {
  getSearchResults()
})
// 这里加个watch监听route.query的变化!非常重要!
// 刚才说了,直接用push修改Query参数,组件不会重新mounted(因为只是路由参数变了,路由组件本身没变),所以要加watch来触发重新请求
watch(
  () => route.query,
  () => {
    getSearchResults()
  },
  {
    deep: true, // 必须加deep,因为query是个对象,监听对象的属性变化需要深度监听
    immediate: false // 第一次已经用onMounted调用了,不用immediate
  }
)
// 翻页方法
const goToPage = (targetPage) => {
  if (targetPage < 1 || targetPage > totalPages.value) return
  router.push({
    name: 'Search',
    query: {
      ...route.query,
      page: targetPage
    }
  })
}

刚才提到的Hash查询参数怎么拿?其实useRoute也有对应的属性,叫route.hash,拿到的是整个Hash字符串,包括开头的,如果Hash后面还有Query参数,比如#/login/callback?token=abc123&expires=7200,这时候可以把Hash后面的去掉,然后用原生的URLSearchParams解析,或者如果整个项目用的是HashHistory路由的话,那Hash前面的是静态路径,后面的才是路由和参数,不过现在官方推荐用HistoryHistory路由,所以Hash查询参数主要用在第三方登录回调这种场景,给大家写个原生解析的小片段:

// 在组合式函数或者组件里都能用
const getHashQuery = () => {
  const hash = window.location.hash
  if (!hash.includes('?')) return {}
  const queryString = hash.split('?')[1]
  const searchParams = new URLSearchParams(queryString)
  // 转成普通对象
  const queryObj = {}
  for (const [key, value] of searchParams) {
    queryObj[key] = value
  }
  return queryObj
}
// 使用的时候
const { token, expires } = getHashQuery()
if (token) {
  // 存储token,处理登录逻辑
  localStorage.setItem('token', token)
  // 可以用router.replace清空Hash,让地址栏看起来干净一点
  router.replace({ path: route.path })
}

没引入Vue Router怎么办?用原生JS解决!

有时候我们可能不想引入Vue Router这么重的插件,比如做一个纯静态的倒计时页面,只需要从URL里拿个截止时间,或者某个组件库的演示页面不需要路由,这时候直接用原生JS的window.location对象就行,刚才解析Hash查询参数的时候已经用到过一点,现在详细说一下。 原生JS里,解析动态路由参数比较麻烦,因为动态路由是路由插件帮我们匹配的,纯原生的话得自己写正则或者字符串分割,比如路径是/timer/2024-12-31,要拿截止时间2024-12-31,可以这么做:

// 拿当前完整路径,去掉开头的/,用/分割
const pathSegments = window.location.pathname.split('/').filter(segment => segment)
// 假设截止时间是路径的第二个部分(注意数组下标从0开始)
const deadline = pathSegments[1] || '2024-12-31' // 加个默认值

不过这种纯原生解析动态路由的方式很脆弱,一旦路径变了(比如改成/countdown/2024-12-31),代码就要跟着改,所以如果路径比较复杂,还是建议加Vue Router。 那解析Query参数呢?原生JS里有个非常好用的API叫URLSearchParams,兼容性也很好,除了特别老的IE浏览器(现在谁还管IE啊),其他主流浏览器都支持。 比如当前URL是https://example.com/vue3-static-page?theme=dark&fontSize=16&lang=zh-CN,解析Query参数可以这么写:

// 原生写法,拿到当前页面的搜索参数
const searchParams = new URLSearchParams(window.location.search)
// 转成普通对象,方便后续使用
const getQueryObj = () => {
  const queryObj = {}
  // 遍历searchParams
  for (const [key, value] of searchParams) {
    // 如果有多个相同的key,tags=手机&tags=平板,searchParams.getAll('tags')会返回数组
    // 这里做个判断,根据实际需求来
    if (queryObj[key]) {
      if (Array.isArray(queryObj[key])) {
        queryObj[key].push(value)
      } else {
        queryObj[key] = [queryObj[key], value]
      }
    } else {
      queryObj[key] = value
    }
  }
  return queryObj
}
// 使用的时候
const queryObj = getQueryObj()
const theme = queryObj.theme || 'light'
const fontSize = queryObj.fontSize ? parseInt(queryObj.fontSize) : 14
const lang = queryObj.lang || 'zh-CN'

刚才也说了,多个相同的Query参数(比如多选的标签),用getAll方法更直接,比如searchParams.getAll('tags')就能拿到一个包含所有标签的数组,不用自己写循环判断了。 原生JS拿到的参数怎么实现响应式更新呢?在Vue3里,可以把拿到的原生参数赋值给ref或reactive对象,然后用window.addEventListener('popstate', callback)或者window.addEventListener('hashchange', callback)来监听URL的变化,当URL变了的时候,重新解析参数并更新ref/reactive对象,举个简单的例子:

// StaticPage.vue <script setup>纯原生版本
import { ref, onMounted, onUnmounted } from 'vue'
// 响应式存储Query参数
const theme = ref('light')
// 解析并更新参数的函数
const updateParams = () => {
  const searchParams = new URLSearchParams(window.location.search)
  theme.value = searchParams.get('theme') || 'light'
  // 这里可以根据theme.value修改页面样式
  document.body.className = theme.value
}
// 第一次加载页面调用
onMounted(() => {
  updateParams()
  // 监听popstate事件,也就是用户点击浏览器前进后退按钮的时候触发
  window.addEventListener('popstate', updateParams)
  // 如果是用pushState/replaceState修改URL的话,popstate不会自动触发,得自己手动调用
  // 比如写个修改主题的方法
})
// 组件卸载的时候移除监听,防止内存泄漏
onUnmounted(() => {
  window.removeEventListener('popstate', updateParams)
})
// 模拟修改主题的方法
const changeTheme = (newTheme) => {
  // 更新URL,不刷新页面
  const searchParams = new URLSearchParams(window.location.search)
  searchParams.set('theme', newTheme)
  const newUrl = `${window.location.pathname}?${searchParams.toString()}${window.location.hash}`
  window.history.pushState(null, '', newUrl)
  // 手动调用更新函数
  updateParams()
}

进阶优化:封装一个通用的获取URL参数的组合式函数

不管是用Vue Router还是原生JS,每次写代码都要引入一堆东西,还要处理响应式、默认值、类型转换这些问题,太麻烦了,不如封装一个通用的组合式函数useUrlParams,以后直接调用就行。 这个组合式函数要兼容两种情况:有Vue Router和没有Vue Router,还要支持设置默认值、类型转换、监听参数变化,我们可以用try-catch来判断是否引入了Vue Router,因为如果没引入的话,import('vue-router')会报错。

// composables/useUrlParams.js
import { ref, computed, toRefs, watch, onUnmounted, isRef } from 'vue'
export const useUrlParams = (options = {}) => {
  const { defaultParams = {}, parseNumbers = true, parseBooleans = true } = options
  let route, router
  let useNative = false
  // 尝试引入Vue Router
  try {
    const { useRoute: getRoute, useRouter: getRouter } = require('vue-router')
    // 注意:require的话,在Vite里可能需要配置alias或者用import()动态导入,但动态导入是异步的,组合式函数一般要同步返回,所以可以判断当前环境有没有全局的$router或者route实例?不对,Composition API里没有全局实例,换个思路:判断当前页面是否有Vue Router挂载的路由信息?
    // 或者更简单一点:让用户传一个useRouter的标志位?不对,要做到开箱即用,还是用import()加个状态?
    // 好吧,可能更稳妥的是让用户在使用的时候,如果有Vue Router就传个参数,没有就不传,或者项目里统一用一种方式,我这里写个兼容两种的,优先用Vue Router
    // 先假设用户如果引入了Vue Router,就会在组件里正确调用useRoute和useRouter,所以我们可以让用户传route和router作为可选参数进来?
    // 对,这样更稳妥,不会因为环境问题报错
    if (options.route) {
      route = options.route
      router = options.router
    } else {
      useNative = true
    }
  } catch (e) {
    useNative = true
  }
  // 内部的响应式参数对象
  const _params = ref({})
  // 内部的原始路由/原生参数对象(用于更新)
  let _rawParams = {}
  // 类型转换函数
  const parseValue = (value) => {
    if (value === null || value === undefined) return value
    if (parseBooleans && (value === 'true' || value === 'false')) {
      return value === 'true'
    }
    if (parseNumbers && !isNaN(value) && value.trim() !== '') {
      return Number(value)
    }
    return value
  }
  // 合并默认值和解析后的参数
  const mergeParams = (rawParams) => {
    _rawParams = rawParams
    const parsedParams = {}
    // 先把原始参数解析好
    for (const key in rawParams) {
      parsedParams[key] = parseValue(rawParams[key])
    }
    // 再合并默认值,默认值的类型也要保持,如果默认值是Ref,要取value
    for (const key in defaultParams) {
      const defaultValue = isRef(defaultParams[key]) ? defaultParams[key].value : defaultParams[key]
      if (parsedParams[key] === undefined || parsedParams[key] === null || parsedParams[key] === '') {
        parsedParams[key] = defaultValue
      }
    }
    _params.value = parsedParams
  }
  // 初始化原始参数
  if (useNative) {
    // 原生模式:解析pathname(动态参数)、search、hash
    const initRawParams = () => {
      const raw = {}
      // 解析动态参数:假设用户传了一个dynamicParamKeys的数组作为options?比如options.dynamicParamKeys = ['id', 'deadline'],对应路径/param1/param2
      // 加上这个功能更完善
      if (options.dynamicParamKeys && Array.isArray(options.dynamicParamKeys)) {
        const pathSegments = window.location.pathname.split('/').filter(segment => segment)
        options.dynamicParamKeys.forEach((key, index) => {
          if (pathSegments[index]) {
            raw[key] = pathSegments[index]
          }
        })
      }
      // 解析search参数
      const searchParams = new URLSearchParams(window.location.search)
      for (const [key, value] of searchParams) {
        raw[key] = value
      }
      // 解析hash参数
      const hash = window.location.hash
      if (hash.includes('?')) {
        const hashParams = new URLSearchParams(hash.split('?')[1])
        for (const [key, value] of hashParams) {
          raw[key] = value
        }
      }
      return raw
    }
    mergeParams(initRawParams())
    // 原生模式监听URL变化
    const handleUrlChange = () => {
      mergeParams(initRawParams())
    }
    window.addEventListener('popstate', handleUrlChange)
    window.addEventListener('hashchange', handleUrlChange)
    // 原生模式更新URL的方法
    const updateUrl = (newParams, replace = false) => {
      // 合并现有原始参数和新参数
      const mergedRaw = { ..._rawParams, ...newParams }
      // 先处理动态参数:修改pathname
      let newPathname = window.location.pathname
      if (options.dynamicParamKeys) {
        const pathSegments = newPathname.split('/').filter(segment => segment)
        options.dynamicParamKeys.forEach((key, index) => {
          if (mergedRaw[key] !== undefined && mergedRaw[key] !== null && mergedRaw[key] !== '') {
            pathSegments[index] = mergedRaw[key]
          }
        })
        newPathname = '/' + pathSegments.join('/')
      }
      // 处理search和hash参数:把dynamicParamKeys从mergedRaw里去掉,剩下的放search或hash?
      // 假设非动态参数都放search
      const searchRaw = {}
      for (const key in mergedRaw) {
        if (!options.dynamicParamKeys || !options.dynamicParamKeys.includes(key)) {
          searchRaw[key] = mergedRaw[key]
        }
      }
      // 生成search字符串
      let newSearch = ''
      const searchEntries = Object.entries(searchRaw).filter(([_, value]) => value !== undefined && value !== null && value !== '')
      if (searchEntries.length > 0) {
        const searchParams = new URLSearchParams()
        searchEntries.forEach(([key, value]) => {
          // 如果是数组,就add多次
          if (Array.isArray(value)) {
            value.forEach(v => searchParams.append(key, v))
          } else {
            searchParams.set(key, value)
          }
        })
        newSearch = '?' + searchParams.toString()
      }
      // 保持原有的hash(不含hash里的查询参数,因为刚才已经解析到mergedRaw里了)
      let newHash = window.location.hash.split('?')[0]
      // 生成新URL
      const newUrl = `${newPathname}${newSearch}${newHash}`
      // 更新历史记录
      if (replace) {
        window.history.replaceState(null, '', newUrl)
      } else {
        window.history.pushState(null, '', newUrl)
      }
      // 手动更新内部参数
      mergeParams(mergedRaw)
    }
    // 组件卸载时移除监听
    onUnmounted(() => {
      window.removeEventListener('popstate', handleUrlChange)
      window.removeEventListener('hashchange', handleUrlChange)
    })
    // 返回值
    return {
      ...toRefs(_params.value), // 解构出来的是响应式Ref
      params: computed(() => _params.value), // 整个响应式参数对象
      updateParams: updateUrl
    }
  } else {
    // Vue Router模式:优先用name路由更新,方便后期维护
    const initRawParams = () => {
      const raw = { ...route.params, ...route.query }
      // hash查询参数也加进去
      const hash = route.hash
      if (hash.includes('?')) {
        const hashParams = new URLSearchParams(hash.split('?')[1])
        for (const [key, value] of hashParams) {
          raw[key] = value
        }
      }
      return raw
    }
    mergeParams(initRawParams())
    // Vue Router模式监听route变化
    watch(
      () => ({ ...route.params, ...route.query, hash: route.hash }),
      () => {
        mergeParams(initRawParams())
      },
      { deep: true }
    )
    // Vue Router模式更新URL的方法
    const updateUrl = (newParams, replace = false, options = {}) => {
      const { name, path } = options
      const mergedRaw = { ..._rawParams, ...newParams }
      // 区分params和query:假设路由配置里的动态参数名我们可以通过route.matched拿到?或者让用户传paramsKeys?
      // 或者更简单一点:如果用户传了name路由,就优先从name路由的配置里拿动态参数?不过这个比较复杂,组合式函数不好直接访问路由配置,所以还是让用户把params和query分开传?或者我们做个约定,默认所有非数组、非对象的新参数都放query,除非options里指定了paramsKeys
      let params = {}
      let query = {}
      if (options.paramsKeys && Array.isArray(options.paramsKeys)) {
        options.paramsKeys.forEach(key => {
          if (mergedRaw[key] !== undefined) {
            params[key] = mergedRaw[key]
          }
        })
      }
      for (const key in mergedRaw) {
        if (!options.paramsKeys || !options.paramsKeys.includes(key)) {
          query[key] = mergedRaw[key]
        }
      }
      // 过滤掉undefined、null、空字符串的参数
      query = Object.fromEntries(
        Object.entries(query).filter(([_, value]) => value !== undefined && value !== null && value !== '')
      )
      params = Object.fromEntries(
        Object.entries(params).filter(([_, value]) => value !== undefined && value !== null && value !== '')
      )
      // 路由跳转
      const routerMethod = replace ? router.replace : router.push
      const routerOptions = { query }
      if (name) {
        routerOptions.name = name
        routerOptions.params = params
      } else if (path) {
        routerOptions.path = path
        // 注意:path路由不能带params,带了会被忽略,所以如果用path的话,动态参数要自己拼在path里
        if (Object.keys(params).length > 0) {
          console.warn('使用path路由跳转时,params会被忽略,请将动态参数拼在path中或使用name路由')
        }
      } else {
        // 用当前路由的name
        routerOptions.name = route.name
        routerOptions.params = params
      }
      routerMethod(routerOptions)
    }
    // 返回值
    return {
      ...toRefs(_params.value),
      params: computed(() => _params.value),
      updateParams: updateUrl
    }
  }
}

这个组合式函数看起来有点长,但功能很全,以后不管在什么场景下,只要引入它就能用,举个使用例子,还是Search.vue,假设引入了Vue Router:

// Search.vue使用useUrlParams
import { useUrlParams } from '@/composables/useUrlParams'
import { useRoute, useRouter } from 'vue-router'
// 传入route和router,设置默认值、动态参数key(这里Search路由没有动态参数,所以不传)
const { keyword, page, priceMin, priceMax, params, updateParams } = useUrlParams({
  route: useRoute(),
  router: useRouter(),
  defaultParams: {
    keyword: '',
    page: 1,
    priceMin: 0,
    priceMax: Infinity
  }
})
// 模拟搜索接口,直接用解构出来的Ref
const getSearchResults = async () => {
  // 这里keyword.value、page.value都是响应式的,而且已经转好类型了
  const res = await fetch(`/api/search?keyword=${keyword.value}&page=${page.value}&priceMin=${priceMin.value}&priceMax=${priceMax.value}`).then(res => res.json())
  // ...
}
// 第一次加载和参数变化都会触发,不用自己写watch了!
watch(params, getSearchResults, { deep: true, immediate: true })
// 翻页方法
const goToPage = (targetPage) => {
  // 直接调用updateParams,不用管路由还是原生
  updateParams({ page: targetPage })
}
// 修改价格区间的方法
const changePriceRange = (min, max) => {
  updateParams({ priceMin: min, priceMax: max, page: 1 })
}

你看,这样代码是不是简洁多了?而且以后如果要改参数获取的逻辑,只要修改useUrlParams.js这一个文件就行,不用改所有用到的组件。

最后总结几个常见的避坑点

今天说了这么多,再给大家划几个重点避坑点,都是我平时开发或者看别人代码遇到的最多的问题:

  1. Composition API里解构useRoute()会失去响应式:要么全程用route.params/route.query,要么用toRefs包裹;
  2. 修改路由参数后页面不刷新:因为只是参数变了,路由组件本身没有重新创建,所以要加watch监听route.queryroute.params,或者给路由组件加key(比如<router-view :key="$route.fullPath" />,这样每次参数变了key都会变,组件会强制重新mounted,不过这种方式性能不如watch,因为整个组件都会销毁重建);
  3. Query参数拿到的都是字符串:记得转换类型,尤其是数字和布尔值,用刚才封装的useUrlParams里的类型转换函数就行;
  4. path路由跳转不能带params:带了会被忽略,要用name路由;
  5. 组件卸载时要移除原生事件监听:比如popstatehashchange,不然会内存泄漏;
  6. 原生pushState/replaceState不会触发popstate:要手动调用更新参数的函数。

好了,关于Vue3获取URL参数的所有内容就说到这里了,从基础的Options API、Composition API写法,到原生JS的应急方案,再到进阶的组合式函数封装,还有避坑点,应该覆盖了99%的业务场景,大家可以根据自己的项目情况选择合适的方式,希望这篇文章能帮到你!

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

热门