React动态路由配置踩坑记:从基础到进阶的实战经验总结
动态路由的基本玩法
最近项目里用了不少动态路由,踩了几个坑,记录一下。动态路由其实就是在URL里面塞个变量,比如用户ID,商品ID这种,React Router和Vue Router都有对应的实现。
先说React Router的写法,这是我在项目中最常用的:
import { BrowserRouter as Router, Routes, Route, useParams } from 'react-router-dom';
function UserProfile() {
const { userId } = useParams();
return (
<div>
<h1>用户ID: {userId}</h1>
</div>
);
}
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/user/:userId" element={<UserProfile />} />
<Route path="/product/:productId" element={<ProductDetail />} />
</Routes>
</Router>
);
}
这里的关键就是那个冒号 :,:userId 就表示这是一个动态参数。然后在组件里用 useParams() 钩子来获取参数值。亲测有效,基本不会出问题。
Vue Router的动态路由实现
Vue这边也差不多,不过语法稍微不同:
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import UserDetail from '../components/UserDetail.vue'
const routes = [
{ path: '/', component: Home },
{
path: '/user/:userId',
component: UserDetail,
props: true // 这个很重要,把路由参数作为props传给组件
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
<!-- UserDetail.vue -->
<template>
<div>
<h1>用户ID: {{ userId }}</h1>
</div>
</template>
<script>
export default {
props: ['userId'],
mounted() {
console.log(this.userId) // 这里就能拿到路由参数了
}
}
</script>
Vue这边需要注意 props: true 这个配置,不然参数传不到组件里去。之前我就因为忘了加这个,调试了半小时。
嵌套路由的动态参数
复杂一点的场景是嵌套路由,比如 /user/:userId/orders/:orderId 这种,参数嵌套参数:
<Route path="/user/:userId">
<Route path="orders/:orderId" element={<OrderDetail />} />
</Route>
这样在OrderDetail组件里就可以同时拿到userId和orderId:
function OrderDetail() {
const { userId, orderId } = useParams();
useEffect(() => {
// 可以用这两个参数请求具体的数据
fetchUserOrder(userId, orderId);
}, [userId, orderId]);
return <div>订单详情</div>;
}
嵌套路由的参数获取非常直观,所有参数都会合并到一个对象里返回。
参数验证和错误处理
实际项目中参数校验是必须的,不能假设用户输入的URL都是合法的。我在项目里加了一层封装:
function ValidatedUserProfile() {
const { userId } = useParams();
// 参数校验
if (!userId || isNaN(userId)) {
return <div>无效的用户ID</div>;
}
// 转换数据类型
const numericUserId = parseInt(userId);
return <UserProfile userId={numericUserId} />;
}
这里我踩过坑,用户可能输入字母或者特殊字符,如果不验证就会导致后端接口报错。建议在useEffect里也加个条件判断:
useEffect(() => {
if (userId && !isNaN(userId)) {
fetchUserData(userId);
}
}, [userId]);
可选参数和通配符路由
有时候需要可选参数,比如 /search?query=xxx&page=1 这种,可以用问号:
<Route path="/search/:query?" element={<SearchResults />} />
这里query是可选的,没有的话params.query就是undefined。还有通配符路由,用来捕获剩余路径:
<Route path="/docs/*" element={<DocsPage />} />
通配符比较有用的是做404页面:
<Route path="*" element={<NotFound />} />
这个必须放在所有路由的最后面,不然会拦截其他路由。
编程式导航传参
除了URL参数,有时候还需要传递额外数据。React Router里用state:
import { useNavigate } from 'react-router-dom';
function MyComponent() {
const navigate = useNavigate();
const handleGoToUser = (userId, userData) => {
navigate(/user/${userId}, {
state: { userData } // 额外数据
});
};
return <button onClick={() => handleGoToUser(123, {name: '张三'})}>去用户页</button>;
}
function UserProfile() {
const location = useLocation();
const { userId } = useParams();
const { userData } = location.state || {};
return (
<div>
<h1>{userData?.name || '加载中...'}</h1>
</div>
);
}
Vue那边类似,不过用query参数更常见:
const router = useRouter();
router.push({
path: '/user',
query: {
id: 123,
from: 'dashboard'
}
});
踩坑提醒:这几点一定注意
动态路由最容易踩坑的地方有几个:
- 参数类型转换:URL里的参数都是字符串,数字ID记得转成number类型
- 路由顺序:静态路由在前,动态路由在后,不然会被静态路由拦截
- 参数变化监听:在同一个组件内切换参数时,组件不会重新mount,需要用useEffect监听参数变化
- 编码问题:特殊字符记得encodeURIComponent,否则可能路由匹配失败
特别是第三点,我在项目里遇到过bug,用户从 /user/1 切到 /user/2,页面没刷新数据。后来加上useEffect监听解决了:
useEffect(() => {
// 参数变化时重新请求数据
refreshData(userId);
}, [userId]);
性能优化小技巧
动态路由的数据加载策略也很重要。我一般会先显示骨架屏,避免用户看到空白页面:
function UserProfile({ userId }) {
const [loading, setLoading] = useState(true);
const [userData, setUserData] = useState(null);
useEffect(() => {
setLoading(true);
fetchUserData(userId).then(data => {
setUserData(data);
setLoading(false);
});
}, [userId]);
if (loading) {
return <SkeletonLoader />;
}
return <UserContent data={userData} />;
}
还可以考虑预加载策略,用户鼠标悬停链接时提前加载数据,提升用户体验。
以上是我对动态路由的完整讲解,有更优的实现方式欢迎评论区交流。这个技巧的拓展用法还有很多,后续会继续分享这类博客。

暂无评论