Vue3 零基础每日笔记(047):动态路由与路由参数——:id 传参、query 传参、props 解耦

一、前言

详情页是所有应用的基本盘:商品详情、用户主页、订单明细------URL 里必须带"这一条数据是谁"的信息。本篇讲透三种传参方式:路径参数(:id)、查询参数(query)、props 解耦,以及新手必踩的"参数变了页面不刷新"大坑。

三种传参先混个脸熟

text 复制代码
/user/1                  → 路径参数:params.id = '1'(RESTful 风格,详情页标配)
/user?id=1               → 查询参数:query.id = '1'(列表筛选、搜索词)
props 解耦               → 前两种的"接收方式升级",组件不依赖 useRoute

二、路径参数:动态路由 :id

路由表里用 :参数名 声明"这个位置是可变的":

ts 复制代码
// src/router/index.ts
const routes = [
  {
    path: '/user/:id',          // :id 是动态段------/user/1、/user/2、/user/abc 都匹配
    name: 'user',
    component: () => import('@/views/UserView.vue')
  }
]
vue 复制代码
<!-- src/views/UserView.vue -->
<template>
  <h2>用户详情页</h2>
  <p>URL 是:{{ route.fullPath }}</p>
  <p>params.id = {{ route.params.id }}</p>
</template>

<script setup lang="ts">
import { useRoute } from 'vue-router'

const route = useRoute()
// 注意类型:params 的值是 string | string[],不是 number!
const id = route.params.id
console.log(typeof id)   // string
</script>

访问 /user/42 → route.params.id 得到字符串 '42'。要数字自己转:

ts 复制代码
const id = Number(route.params.id)
if (Number.isNaN(id)) {
  // 非法参数处理:跳 404 或提示
}

可选参数 用 ?:path: '/user/:id?'------/user 和 /user/1 都能匹配,用时记得判空。

【截图位置:访问 /user/42 显示参数的运行效果】

三、大坑登场:参数变了,页面却不刷新

复现现场:在详情页放一个"看下一位用户"的按钮:

vue 复制代码
<template>
  <p>当前用户:{{ route.params.id }}</p>
  <!-- 模板里 route.params 是响应式的,这里会更新 -->
  <RouterLink :to="`/user/${Number(route.params.id) + 1}`">下一位 →</RouterLink>
</template>

问题来了:如果页面数据是"进页面时用 onMounted 拉取的",点击链接 URL 变了,数据却不动。

原因:/user/1 → /user/2 匹配的是同一个路由 ,Vue Router 出于性能默认复用组件实例------组件不会重新创建,onMounted 不再执行(011 篇生命周期知识联动)。

解法 1:watch 监听参数(推荐,最常用)

ts 复制代码
import { watch } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()

// 进页面执行一次 + 参数变了再执行(immediate)
watch(
  () => route.params.id,
  (newId) => {
    if (newId) loadData(Number(newId))
  },
  { immediate: true }
)

function loadData(id: number) {
  console.log('加载用户', id, '的数据')
}

解法 2:router-view 上加 key,强制重建组件(简单粗暴)

vue 复制代码
<!-- App.vue -->
<RouterView :key="route.fullPath" />

URL 一变组件销毁重建,onMounted 重新执行------代价是整个页面全部重渲染,页面轻可用,页面重慎用。默认学解法 1。

四、query 查询参数:筛选与搜索的地盘

query 不需要路由表声明,拼在 URL 后面就能读:

ts 复制代码
// 跳转:/search?kw=vue3&page=2
router.push({ name: 'search', query: { kw: 'vue3', page: 2 } })

// 读取(045 篇的 search 页)
const kw = route.query.kw        // 'vue3',类型 LocationQueryValue = string | null
const page = Number(route.query.page ?? 1)

params vs query 对比表:

params(路径参数) query(查询参数)
URL 长相 /user/1 /user?id=1
路由表 必须声明 :id 不需要声明
语义 这条资源的"身份" 查询条件/视图状态
典型场景 详情页 ID 搜索词、页码、筛选条件
刷新后 保留(在 URL 里) 保留(在 URL 里)

记忆:身份进路径,条件进 query。

五、props 解耦:组件不再依赖 useRoute

问题:UserView 里到处 route.params.id,组件和路由器耦合死了,想单独复用/测试很难。路由配置提供 props 选项,把参数变成正经 props:

ts 复制代码
// 路由配置加 props: true
{
  path: '/user/:id',
  name: 'user',
  component: () => import('@/views/UserView.vue'),
  props: true   // 把 params 作为 props 传给组件
}
vue 复制代码
<!-- UserView.vue:改用 props 接收,和普通组件毫无区别 -->
<template>
  <p>用户 ID:{{ id }}(类型 {{ typeof id }})</p>
</template>

<script setup lang="ts">
// 不再需要 useRoute!
defineProps<{ id: string }>()
</script>

props 三种模式:

ts 复制代码
props: true                                  // 布尔模式:params 全量转 props
props: { id: 123 }                           // 对象模式:静态值(少用)
props: (route) => ({ id: Number(route.params.id) })  // 函数模式:自由加工(转数字常用)

函数模式是实战首选------顺手把字符串转成数字,组件拿到的就是干净的 number:

vue 复制代码
<script setup lang="ts">
defineProps<{ id: number }>()   // 函数模式加工后,直接是数字
</script>

六、综合实战:用户列表 → 详情 → 参数联动

三个文件走通全链路:

ts 复制代码
// src/router/index.ts 追加
{
  path: '/users',
  name: 'users',
  component: () => import('@/views/UserListView.vue')
},
{
  path: '/user/:id',
  name: 'user',
  component: () => import('@/views/UserDetailView.vue'),
  props: (route) => ({ id: Number(route.params.id) })
}
vue 复制代码
<!-- src/views/UserListView.vue:列表 + 声明式跳转 -->
<template>
  <h2>用户列表</h2>
  <ul>
    <li v-for="u in users" :key="u.id">
      <RouterLink :to="{ name: 'user', params: { id: u.id } }">
        {{ u.name }}
      </RouterLink>
    </li>
  </ul>
</template>

<script setup lang="ts">
const users = [
  { id: 1, name: '小明' },
  { id: 2, name: '小红' },
  { id: 3, name: '小刚' }
]
</script>
vue 复制代码
<!-- src/views/UserDetailView.vue:props 接参 + watch 响应参数变化 -->
<template>
  <h2>用户详情:ID = {{ id }}</h2>
  <p>模拟数据:{{ detail }}</p>
  <button @click="next">下一位用户 →(参数变化,页面不重建)</button>
  <RouterLink to="/users">← 回列表</RouterLink>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'
import { useRouter } from 'vue-router'

// 函数模式加工过,id 是干净的 number
const props = defineProps<{ id: number }>()
const router = useRouter()

const detail = ref('')

function load(uid: number) {
  // 模拟请求详情
  detail.value = `这是用户 ${uid} 的详细信息`
}

// 组件复用不重建 → 用 watch 响应参数变化(解法 1)
watch(() => props.id, load, { immediate: true })

function next() {
  router.push({ name: 'user', params: { id: props.id + 1 } })
}
</script>

全链路品味:列表按 name 跳转带 params → 详情页函数模式把 id 加工成 number → watch immediate 首次加载 + 参数变化重载。

【截图位置:列表进详情、点"下一位"数据联动更新的运行效果】

七、踩坑记录

  1. params 是字符串 :route.params.id + 1 得到 '11'(字符串拼接)------Number() 转换或用 props 函数模式加工
  2. 同路由参数变化组件不重建:onMounted 的数据请求只跑一次------watch 参数(推荐)或 RouterView 加 key(粗暴)
  3. query 值可能是数组 :同名参数出现多次时 ?a=1&a=2,route.query.a 变数组------读取时统一处理成数组或取第一个
  4. 可选参数忘了判空 ::id? 匹配 /user 时 params.id 是 '' 或 undefined------使用前判断
  5. props: true 后组件里还去 useRoute:能跑但白解耦了------选了 props 模式就贯彻到底
  6. 404 误伤:/user/abc 也会匹配 :id------非法值在页面里判断后跳转 404(052 篇配 catchAll)

八、今日小结

  • 动态路由 :id 声明动态段,params 取值是字符串要转数字
  • 同路由参数变化默认复用组件:watch 参数(immediate)是标准解法
  • params 管身份、query 管条件;name 配 params、path 配 query
  • props 解耦三模式,函数模式最实用(顺手转数字)

下篇预告

后台管理系统长什么样?顶部栏 + 侧边菜单 + 内容区,菜单里还能再开子页面。下一篇 048 嵌套路由与命名视图------children 配置搭出后台布局雏形。

相关推荐
yi0113 小时前
LeetCode 219:存在重复元素 II——哈希表记录“最近一次出现的位置”
数据结构·人工智能·笔记·python·算法·leetcode·哈希表
程序员Sunday3 小时前
JavaScript 事件循环面试题,宏任务与微任务怎么执行|Sunday面试指南
开发语言·javascript·面试·校招·事件循环·程序员sunday
Setsuna_F_Seiei4 小时前
前端转型 Agent 开发 06 之 Agent Memory 记忆系统(让 Agent 更智能,更懂你)
前端·agent·ai编程
志尊宝4 小时前
Vue3 零基础每日笔记(073):keep-alive 页面缓存实战——列表页返回不丢状态
vue.js·笔记·缓存·#vue #前端 #前端开发·#javascript·#vue.js
zhangzeyuaaa4 小时前
Ruby 多线程、GVL(GIL)与 Mutex 完全指南
开发语言·前端·ruby
默_笙5 小时前
🌊 向量库和 ES 都查不出"关系",我只好给奶茶建了一张人脉网
前端·javascript
优选资讯5 小时前
标签打印软件怎么对接 Excel 数据
前端·excel
思无邪665 小时前
用 AI 做 JS 逆向:从抓包到复现的完整方法论
开发语言·javascript·人工智能
weixin_448119946 小时前
Datawhale Easy Data × AI:笔记1 已重做,看新的笔记
笔记
一个风轻云淡6 小时前
GCC 和 GDB命令简单解读
java·linux·前端