Angular 13中如何通过路由创建携带文章标题的动态URL?
博客单篇文章动态URL实现方案
核心思路是将原静态详情页路由改为带动态路径参数的路由,将文章标题处理为URL友好的字符串(slug)作为路径段,实现每篇文章对应独立访问地址,整体分为4个核心步骤:
核心实现步骤
- 生成URL兼容的文章slug:将文章标题做格式化处理,替换空格、特殊字符为短横线,统一大小写,避免URL出现非法字符
- 替换原有静态路由规则:将原
/post-detail静态路由修改为带slug参数的动态路由格式 - 列表页跳转时拼接动态路径:渲染文章列表时,为每篇文章生成带对应slug的跳转链接
- 详情页匹配参数渲染内容:进入详情页时从URL中提取slug参数,匹配对应文章内容,匹配失败时跳转404页面
通用工具函数
先写一个通用的slug生成函数,兼容中英文标题:
// utils/slug.js export function generateSlug(title) { return title .trim() .toLowerCase() // 匹配非中文、非字母、非数字的字符,替换为短横线 .replace(/[^\w\u4e00-\u9fa5]+/g, '-') // 清除首尾多余的短横线 .replace(/^-+|-+$/g, '') }
常见框架示例代码
Vue3 + Vue Router 实现
- 路由配置修改
// router/index.js import { createRouter, createWebHistory } from 'vue-router' import PostList from '../views/PostList.vue' import PostDetail from '../views/PostDetail.vue' import NotFound from '../views/NotFound.vue' const routes = [ { path: '/', component: PostList }, // 原静态路由 { path: '/post-detail', component: PostDetail } 替换为动态路由 { path: '/post/:slug', component: PostDetail }, { path: '/:pathMatch(.*)*', component: NotFound } ] const router = createRouter({ history: createWebHistory(), routes }) export default router
- 文章列表跳转写法
<!-- views/PostList.vue --> <template> <div class="post-list"> <article v-for="post in postList" :key="post.id" class="post-card"> <h3> <router-link :to="`/post/${generateSlug(post.title)}`"> {{ post.title }} </router-link> </h3> <p>{{ post.excerpt }}</p> </article> </div> </template> <script setup> import { generateSlug } from '../utils/slug' // 实际场景替换为接口/本地数据源拉取的文章列表 const postList = [ { id: 1, title: 'Vue3路由配置指南', content: '...' }, { id: 2, title: 'React Hooks最佳实践', content: '...' } ] </script>
- 详情页参数匹配
<!-- views/PostDetail.vue --> <template> <div v-if="currentPost" class="post-detail"> <h1>{{ currentPost.title }}</h1> <div class="post-content">{{ currentPost.content }}</div> </div> </template> <script setup> import { ref, onMounted } from 'vue' import { useRoute, useRouter } from 'vue-router' import { generateSlug } from '../utils/slug' import { postList } from '../mock/postData' // 替换为实际数据源 const route = useRoute() const router = useRouter() const currentPost = ref(null) onMounted(() => { const targetSlug = route.params.slug // 匹配对应文章 const targetPost = postList.find(post => generateSlug(post.title) === targetSlug) if (!targetPost) { router.replace('/404') return } currentPost.value = targetPost }) </script>
React + React Router v6 实现
- 路由配置修改
// App.jsx import { BrowserRouter, Routes, Route } from 'react-router-dom' import PostList from './pages/PostList' import PostDetail from './pages/PostDetail' import NotFound from './pages/NotFound' function App() { return ( <BrowserRouter> <Routes> <Route path="/" element={<PostList />} /> {/* 原静态路由<Route path="/post-detail" element={<PostDetail />} /> 替换 */} <Route path="/post/:slug" element={<PostDetail />} /> <Route path="*" element={<NotFound />} /> </Routes> </BrowserRouter> ) } export default App
- 列表页跳转写法
// pages/PostList.jsx import { Link } from 'react-router-dom' import { generateSlug } from '../utils/slug' import { postList } from '../mock/postData' export default function PostList() { return ( <div className="post-list"> {postList.map(post => ( <article key={post.id} className="post-card"> <h3> <Link to={`/post/${generateSlug(post.title)}`}> {post.title} </Link> </h3> <p>{post.excerpt}</p> </article> ))} </div> ) }
- 详情页参数匹配
// pages/PostDetail.jsx import { useEffect, useState } from 'react' import { useParams, useNavigate } from 'react-router-dom' import { generateSlug } from '../utils/slug' import { postList } from '../mock/postData' export default function PostDetail() { const { slug } = useParams() const navigate = useNavigate() const [currentPost, setCurrentPost] = useState(null) useEffect(() => { const targetPost = postList.find(post => generateSlug(post.title) === slug) if (!targetPost) { navigate('/404', { replace: true }) return } setCurrentPost(targetPost) }, [slug]) if (!currentPost) return null return ( <div className="post-detail"> <h1>{currentPost.title}</h1> <div className="post-content">{currentPost.content}</div> </div> ) }
避坑提示:如果存在标题重名的文章,仅用标题生成slug会出现匹配冲突,可以在slug前拼接文章唯一ID,格式如
/post/1-vue3-lu-you-pei-zhi-zhi-nan,匹配时提取ID字段查询文章即可,兼顾URL可读性和唯一性。
内容的提问来源于stack exchange,提问作者Sajid Hussain
相关产品推荐
相关产品推荐

