想象你正站在一座巨大城市综合体的入口,手里攥着一张地址写着“3F-美食广场-川菜馆”。这座综合体名叫“SPA中心”,它把过去散落在街区的所有小店铺都搬进了同一栋楼。过去,每访问一家店都要走出旧楼、再进新楼——这是传统多页应用;现在,所有店铺在同一栋楼里,你要做的只是根据地址在不同楼层和展厅间穿梭,而帮你认路、带路、决定展示哪个店铺的“内部导航系统”,就是前端路由。它能让你在网址栏输入地址时,不刷新整个大楼,就能迅速把对应“店铺”呈现在你面前。这套导航系统有很多精细设计:地址用什么格式(Hash还是History)、导航手册怎么写(React Router还是Vue Router)、哪些区域需要安检(路由守卫)、哪些店铺可以到门口才装修(懒加载),以及回到上次的楼层时恢复位置(滚动行为)。本文会用这座“路由之城”的运营故事,把前端路由管理讲透。

岗位关联

掌握前端路由管理,是搭建现代Web应用(如中后台系统、电商SPA)的必备能力。熟练使用React Router和Vue Router、理解底层并做性能优化,能让你在团队中独立负责应用导航与权限架构,这也是前端进阶面试的高频考点。

目录
  1. 可视化演示
  2. 路由基础概念
  3. Hash路由原理
  4. History路由原理
  5. React Router配置
  6. Vue Router用法
  7. 路由守卫权限
  8. 懒加载优化
  9. 滚动行为处理
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示Hash路由与History路由两种模式下URL变化触发路由匹配和组件渲染的过程,切换模式对比差异。

本节配有交互动效(进入视口后自动加载,约 3KB)。

Hash路由模式 URL中#后路径变化触发hashchange事件 路由器匹配路径渲染对应组件 URL#path → hashchange → render

路由基础概念

在这座“SPA综合体”里,每个页面不再是街边独立的小屋,而是楼内的一个个展厅。传统多页应用(MPA)好比用户每访问一个“房间”都要走出整栋建筑再走进另一栋——浏览器会重新请求HTML并整页刷新。而单页应用(SPA)把所有的“展厅”都集中在一栋楼内,用户只需在楼内移动,不用出门。前端路由,就是为这栋楼设计的“内部导览系统”:它根据你当前手上的地址(URL),决定应该带你进入哪个展厅,并把这个展厅的内容呈现到眼前。

路由的核心是“映射”二字:URL的路径部分是一把钥匙,组件是锁。路由器维护着一张巨大的“钥匙-展厅”对照表。当URL变化时,路由器的查表函数会迅速找出对应的组件并渲染到页面容器中。这个过程不需要向服务器再要一份新的HTML,所有切换都在浏览器内完成。为了让读者理解这个查表逻辑,我们可以形式化地写出:

理解这张“钥匙-展厅对照表”,你就抓住了路由的命脉。无论后面讲Hash还是History、React还是Vue,本质上都是在回答两个问题:地址怎么变?变完之后查哪套表?导航系统的框架或许不同,但服务的核心目标一致——让用户在大楼里快速、准确地抵达想去的展厅。

组件=R(路径)\text{组件} = R(\text{路径})
这里的 \(R\) 代表路由表(Route Table),它把路径(如 /home)映射到具体的组件(如首页组件)。
  1. 路径输入用户输入、点击链接或调用跳转方法,产生了URL中的路径部分,如 /home。
    路径=URL 中的 pathname\text{路径} = \text{URL 中的 pathname}
  2. 路由器查表路由器在路由表中寻找与该路径匹配的映射规则,路由表是一组“路径→组件”的键值对。
    R={路径1→组件1, 路径2→组件2,… }R = \{ \text{路径}_1 \to \text{组件}_1,\ \text{路径}_2 \to \text{组件}_2, \dots \}
  3. 组件输出找到匹配项后,把对应的组件渲染到页面指定的容器里,完成一次视图切换。
    视图=组件(props)\text{视图} = \text{组件}(\text{props})

Hash路由原理

最初的地址格式设计,采用了“门牌号后加备注”的技巧。URL中 # 号后面的部分被称为Hash(片段标识符),形如 https://site.com/#/home。这个“备注”很有意思:浏览器不会因为 # 后面的内容变化而向服务器发起新的网络请求,它只会在内部记录这个片段的变化,并触发 hashchange 事件。也就是说,整栋大楼的门牌地址没有变,变的仅仅是你在楼内导览图上用笔圈出的“当前位置”。

Hash路由的前端实现,就是监听这个 hashchange 事件。当用户点击一个 href="#/about" 的链接时,URL的hash变成 #/about,事件触发,前端拿到 about 字符串去路由表中匹配,然后渲染对应组件。因为整个过程不涉及网络请求,所以Hash路由部署简单,任意静态服务器都能支持,不需要额外的服务端配置。但缺点也很明显:URL中多了一个 #,既不美观,也不利于搜索引擎抓取(许多爬虫会忽略hash后面的内容)。

Hash路由就像大楼里“楼层标注卡”的折角标记:你换标记时不需要惊动物业,也不会让整栋楼重新登记。它的简单可靠让它成为小型应用和内部系统的首选。不过,当你希望前台门面看起来正式、简洁,且愿意花精力维护物业配置时,就该请出下一位选手——History路由。

hash=URL 中 # 之后的字符串\text{hash} = \text{URL 中 } \# \text{ 之后的字符串}
例如 https://site.com/index.html#/user/123 的 hash 是 #/user/123。
  1. 定位井号浏览器解析URL时,遇到 # 就把后面的内容单独提取出来作为hash。
    URL=基础地址+#+hash值\text{URL} = \text{基础地址} + \# + \text{hash值}
  2. 截取字符串路由库拿到hash后去掉开头的 #,得到路径字符串,如 /user/123。
    路径=hash.slice(1)\text{路径} = \text{hash.slice(1)}
  3. 监听变化当hash变化时,window 触发 hashchange 事件,回调函数中重新查表渲染。
    hashchange⇒R(路径)⇒新组件\text{hashchange} \Rightarrow R(\text{路径}) \Rightarrow \text{新组件}

History路由原理

如果说Hash路由是在门牌号后贴便签,那么History路由就是直接“改正式门牌”。它借助HTML5提供的History API(pushState、replaceState、popstate事件),让URL看起来与传统的多页地址完全一样,例如 https://site.com/user/123。前端调用 pushState 改变URL,浏览器不会自动请求新页面,但会触发 popstate(在用户点击前进/后退时)。前端监听该事件,获取当前路径并渲染对应组件。

这种“改门牌”的方式有一个重要的前提:当用户直接访问 /user/123 或刷新页面时,服务器必须把该路径的请求重定向到SPA的入口HTML(通常是 index.html)。因为真实的服务器上并没有 /user/123 这个物理文件,如果服务器不配合,就会返回404。常见的配置是在Nginx中添加 try_files $uri /index.html;,或在使用Node/Express时通过 app.get('*', ...) 返回入口文件。所以History路由“好看”,但“售后麻烦”。

History路由让地址栏变得干净整洁,也给SEO优化留出了空间(配合服务端渲染或预渲染)。但请记住,它是一把双刃剑:想拥有漂亮门牌,就必须让物业(服务器)做好重定向配置。现实中很多刚接触SPA的同学做上线部署时,就是因为少了这一步,导致刷新后一片空白。

URL=origin+path\text{URL} = \text{origin} + \text{path}
其中 origin 是协议+域名+端口,path 是路径部分。History路由可以做到与后端API的URL风格统一。
  1. 修改地址使用 history.pushState(state, title, path) 在不刷新页面的情况下修改地址栏。
    pushState(路径)⇒URL 更新,页面不刷新\text{pushState}(\text{路径}) \Rightarrow \text{URL 更新,页面不刷新}
  2. 监听popstate当用户点击浏览器的前进/后退时,触发 popstate 事件,前端拿到新的路径。
    popstate⇒读取 location.pathname\text{popstate} \Rightarrow \text{读取 } \text{location.pathname}
  3. 服务端兜底直接访问深层路径时,服务器将所有非资源请求重定向到 index.html。
    服务器: 任何路径→index.html\text{服务器}:\ \text{任何路径} \rightarrow \text{index.html}

React Router配置

在“SPA综合体”中,React Router就是官方发放的“导览手册模板”。它用声明式语法告诉导航员:看到哪个路径,就带用户去哪个展厅。基本的配置骨架很简单:用 <BrowserRouter> 包裹应用,再用 <Routes> 和 <Route> 声明路径与组件的对应关系。

嵌套路由对应着综合体里的“展厅套间”:比如用户中心是一个大展厅,里面还有“订单”和“设置”两个小隔间。父级路由组件通过 <Outlet /> 占位,子路由在父级内部渲染。路径参数用冒号声明,如 /user/:id,在组件中通过 useParams() 读取;查询字符串则用 useSearchParams() 解析。React Router v6还提供了 createBrowserRouter 和 RouterProvider,让数据加载、错误处理等能力更加集中。

React Router的配置哲学是“UI与URL同步”。你把手册写好,剩下的查表、匹配、渲染都由框架代劳。注意在v6中,<Route> 必须直接或间接放在 <Routes> 内,路径匹配默认是前缀匹配,用 index 属性表示默认子路由。理解这套语法,你就能在项目里灵活搭建出多层级、带参数的复杂导航结构。

/user/:id⇒params={id:123}\text{/user/:id} \Rightarrow \text{params} = \{\text{id}: \text{123}\}
当用户访问 /user/123 时,动态片段 :id 捕获 123,作为参数传给组件。
  1. 声明路由<Route path="/user/:id" element={<User />} /> 告诉路由器“遇到此模式就渲染 User”。
    path="/user/:id"⇒element=User\text{path} = \text{"/user/:id"} \Rightarrow \text{element} = \text{User}
  2. 匹配动态参数路由器将URL中的实际值与 :id 对应,生成参数对象 {id: "123"}。
    /user/123⇒params.id="123"\text{/user/123} \Rightarrow \text{params.id} = \text{"123"}
  3. 嵌套渲染父路由的 <Outlet /> 位置渲染子路由组件,形成层级导航。
    父组件⊃Outlet⊃子组件\text{父组件} \supset \text{Outlet} \supset \text{子组件}
jsx6 行
<BrowserRouter>  <Routes>    <Route path="/" element={<Home />} />    <Route path="/user/:id" element={<User />} />  </Routes></BrowserRouter>

Vue Router用法

另一栋“分店大楼”用的是Vue Router。它的核心思路与React Router类似,但实现更“显式”:你需要先创建一个路由实例,再告诉Vue应用“用这个实例”。创建方式如下:

Vue Router的动态路由匹配同样用 :参数名 语法,组件内通过 route.params.id 获取。与React不同,Vue Router内置了完整的导航守卫体系(beforeEach、beforeResolve、afterEach),这为权限控制提供了“官方安检通道”。在组件内还可以使用 beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave 等路由守卫钩子,其中 beforeRouteUpdate 尤其适用于动态路由参数变化时重新拉取数据的场景。

Vue Router的声明式导航用 <router-link>,编程式导航用 router.push()。它的守卫机制非常适合做“进入区域前的证件检查”。当你把 beforeEach 与登录状态结合,就能构建一套完整的访问控制流程,这正是下一节的内容。

路由记录=routes 中 path 与当前 URL 匹配的项\text{路由记录} = \text{routes 中 path 与当前 URL 匹配的项}
Vue Router 的 routes 数组就是一张路由表,每个记录对象包含 path、component 等字段。
  1. 创建实例调用 createRouter({ history, routes }),传入路由表和历史模式。
    router=createRouter(history,routes)\text{router} = \text{createRouter}(\text{history}, \text{routes})
  2. 动态匹配/user/:id 与当前路径比较,捕获参数。
    path="/user/:id"⇒route.params.id\text{path} = \text{"/user/:id"} \Rightarrow \text{route.params.id}
  3. 挂载应用app.use(router) 使整个应用获得 $router 和 $route。
    app.use(router)⇒全局路由可用\text{app.use}(\text{router}) \Rightarrow \text{全局路由可用}
js8 行
import { createRouter, createWebHistory } from 'vue-router';const router = createRouter({  history: createWebHistory(),  routes: [    { path: '/', component: Home },    { path: '/user/:id', component: User, props: true }  ]});

路由守卫权限

综合体里总有一些VIP区域,不是谁都能进。路由守卫就是站在区域入口的“安检员”:他会检查你有没有通行证(登录Token),你的级别够不够(角色权限),如果不符合要求,就把你引导到登录处或权限提示页。

Vue Router提供全局前置守卫 router.beforeEach((to, from, next) => { ... }),在每次导航确认前执行。一个典型的登录认证流程:判断 to.meta.requiresAuth 为真且没有Token,则 next('/login') 并携带 redirect 参数;如果已登录访问登录页,则跳回首页。React Router没有内置守卫,但可以通过高阶组件(HOC)或包装 <Route> 组件实现同样的效果,比如:

权限控制的本质是“前端体验层的过滤”,而非安全边界。真正的数据安全必须依赖后端接口鉴权,前端守卫只是为了给用户提供合理的跳转反馈。

守卫是导航系统中最有“人情味”的一环:它知道哪些区域对你开放,哪些还差一个会员等级。但请记住,安检员只是引导员,不是执法者;真正的机密数据保护,要靠后端的保险柜(权限校验)来保证。

next(目标)={允许if 已登录跳转登录if 未登录\text{next}(\text{目标}) = \begin{cases} \text{允许} & \text{if } \text{已登录} \\ \text{跳转登录} & \text{if } \text{未登录} \end{cases}
守卫函数根据用户状态决定调用 next() 放行,还是调用 next('/login') 重定向。
  1. 读取Token从 localStorage 或状态管理库中获取当前用户的登录凭证。
    token=store.auth.token\text{token} = \text{store.auth.token}
  2. 判断权限检查目标路由的 meta 是否要求登录或特定角色。
    requiresAuth=to.meta.requiresAuth\text{requiresAuth} = \text{to.meta.requiresAuth}
  3. 决定放行满足条件调用 next(),否则调用 next('/login')。
    next() 或 next("/login")\text{next}()\ \text{或}\ \text{next}(\text{"/login"})
jsx4 行
function RequireAuth({ children }) {  const token = useAuth();  return token ? children : <Navigate to="/login" />;}

懒加载优化

试想,如果把综合体所有展厅的装修材料都堆在入口大厅,那顾客进门光看装修就得等半天。聪明的做法是:入口只放一张简洁的前台(核心框架),每个展厅的家具建材(组件代码)等顾客真正推门时才快速送入。这就是前端路由的懒加载。

具体实现上,React使用 React.lazy(() => import('./Home')),配合 <Suspense fallback={<Loading />}> 包裹路由组件;Vue Router则直接用动态导入语法 () => import('./Home.vue') 作为组件值。打包工具(Webpack/Vite)会根据这些动态导入自动把代码拆分成多个chunk,路由跳转到哪个页面,就下载哪个chunk。首屏只需要加载主框架和当前页的代码,体积大幅下降,加载速度显著提升。

懒加载并不是越多越好——如果每个页面都极小,拆分成大量几十KB的chunk会造成请求碎片化。一般建议按“页面级”划分:整个页面一个chunk,而不是把页面里的按钮也拆出去。把握好粒度,你的导航系统才能又快又稳。

首屏体积=∑chunk∈首屏所需size(chunk)\text{首屏体积} = \sum_{\text{chunk} \in \text{首屏所需}} \text{size}(\text{chunk})
懒加载后,首屏不需要的路由chunk不计入初始加载,因此首屏体积变小。
  1. 动态导入使用 import() 语法让打包器将组件拆分为独立chunk。
    chunk=import(组件路径)\text{chunk} = \text{import}(\text{组件路径})
  2. Suspense兜底React 在异步组件加载完成前显示 fallback 内容。
    Suspense(fallback=Loading)\text{Suspense}(\text{fallback} = \text{Loading})
  3. 路由触发加载只有访问对应路由时才执行 import() 并渲染组件。
    访问路由⇒加载chunk⇒渲染\text{访问路由} \Rightarrow \text{加载chunk} \Rightarrow \text{渲染}

滚动行为处理

在综合体里逛展厅,用户可能在“商品列表”页面滚到很下面的位置,然后点进详情。当他按返回键回到列表页时,如果系统把他带回顶部,他就要重新滚动,体验极差。好的导航系统应该记住他之前的“观看高度”,回来时自动恢复。

浏览器原生刷新页面时会恢复滚动位置,但SPA的“无刷新跳转”不会自动这么做。React Router v6.4+ 提供了 <ScrollRestoration /> 组件(基于数据路由),可以在 location 变化时恢复滚动;Vue Router则更直接,在创建路由时提供 scrollBehavior(to, from, savedPosition) 函数,它返回一个位置对象:

还可以返回 { el: '#main', top: 100 } 滚动到指定元素。除了滚动恢复,路由的“404处理”也属于细节工程:React Router中用 <Route path="*" element={<NotFound />} />,Vue Router用 { path: '/:pathMatch(.*)*', component: NotFound } 捕获未匹配路径。

滚动行为是“细节魔鬼”的典型代表。一个体贴的导航系统,会在用户回头时递上他刚才放下的书签。404页面则是那个和蔼的“问询台”,告诉迷路的游客:你找的展厅不存在,但我们这里有地图。处理好这些边角,你的路由管理才算真正完整。

位置=scrollBehavior(to,from,savedPosition)\text{位置} = \text{scrollBehavior}(to, from, savedPosition)
根据目标路由、来源路由和浏览器保存的滚动位置,计算并返回一个坐标或元素定位。
  1. 定义回调在Vue Router中传入 scrollBehavior 函数。
    scrollBehavior=f(to,from,savedPosition)\text{scrollBehavior} = f(to, from, savedPosition)
  2. 判断来源若 savedPosition 存在(前进/后退)则恢复原位置。
    return savedPosition\text{return } savedPosition
  3. 默认置顶普通导航返回 { top: 0 },保证新页面从顶部开始。
    return {top:0}\text{return } \{ top: 0 \}
js4 行
scrollBehavior(to, from, savedPosition) {  if (savedPosition) return savedPosition;  // 前进/后退恢复  return { top: 0 };                        // 新导航回顶部}

AI 应用场景

中后台管理系统的路由权限控制

阶段:前端工程化与权限架构设计

解决的问题:传统后台系统常用多套页面或刷新跳转实现权限判断,代码重复且体验割裂。AI赋能的现代中后台用前端路由守卫统一管理访问入口,根据用户角色动态过滤菜单和可访问路由,未授权访问自动跳转登录页,让权限逻辑集中可维护。

具体案例:一家B2B公司的管理后台包含“数据看板”“订单管理”“用户管理”三个模块。实施时在路由表中为每个模块添加 meta.roles 字段,beforeEach 中读取用户角色与 meta.roles 比对,无权限则重定向到403页。同时根据角色动态生成侧边栏菜单,不同登录者看到的功能入口不同。

电商SPA的History路由与SEO改造

阶段:Web性能优化与搜索引擎优化

解决的问题:电商站使用History路由让商品详情页URL干净清晰(如 /product/10086),但直接访问该URL会因缺少服务端配置返回404。通过Nginx配置 try_files 与预渲染/SSR技术结合,既保留SPA交互体验,又让爬虫能抓取页面内容。

具体案例:某零食电商App采用Vue 3 + History路由,部署时在Nginx添加 try_files $uri /index.html;,并针对热门商品页做SSR预渲染。改版后,商品搜索点击率提升12%,首屏加载时间降低30%。

内容社区列表页的滚动恢复优化

阶段:用户体验优化

解决的问题:用户在长列表页下滑很久后点击文章,返回时列表位置被重置到顶部,导致反复滚动。利用Vue Router的 scrollBehavior 保存和恢复滚动位置,并配合 keep-alive 缓存列表状态,显著提升阅读连续性。

具体案例:一个技术博客社区在文章详情页增加“返回列表”按钮后,用户返回时列表位置精确复原。发布后,用户平均阅读深度提升18%,跳出率下降5%。

Python 演示

python44 行
import re class RouteManager:    def __init__(self):        self.routes = []  # (pattern, component)     def register(self, pattern, component):        self.routes.append((pattern, component))     def match(self, path):        for pattern, component in self.routes:            # Convert :param to regex group            regex = re.sub(r':(\w+)', r'([^/]+)', pattern)            m = re.fullmatch(regex, path)            if m:                params = dict(zip(re.findall(r':(\w+)', pattern), m.groups()))                return component, params        return None, {}     def navigate(self, path):        component, params = self.match(path)        print(f'[Router] {path} -> {component}, params={params}')        if component:            component.render(params)        else:            print('404 Not Found') class HomePage:    @staticmethod    def render(params):        print('渲染首页') class UserPage:    @staticmethod    def render(params):        print(f'渲染用户页 id={params.get("id")}') if __name__ == '__main__':    router = RouteManager()    router.register('/', HomePage)    router.register('/user/:id', UserPage)    router.navigate('/')    router.navigate('/user/42')    router.navigate('/about')
目的:用 Python 模拟前端路由的核心“路径匹配”逻辑,演示动态参数如何被捕获。
  1. self.routes = []存放“路径模式→组件”的注册表,相当于前端路由表。
  2. regex = re.sub(r':(\w+)', r'([^/]+)', pattern)把 /user/:id 变成正则 /user/([^/]+),用于匹配真实路径。
  3. re.fullmatch(regex, path)要求路径完全匹配,避免 /user/123/abc 误匹配 /user/:id。
  4. params = dict(zip(re.findall(...), m.groups()))把正则捕获的多个值依次赋给参数名,得到 {"id": "42"}。
  5. router.register('/', HomePage)注册根路由,等价于前端 <Route path="/" element={<Home/>} />。
  6. router.navigate('/user/42')模拟用户访问,输出匹配结果。

常见误区

误区我觉得History路由的URL更好看,那就直接换掉Hash路由,应该不会有太大问题吧?反正都是前端做的事。

正解History路由依赖服务端将所有路径请求重定向到 index.html。如果你们的静态服务器没有配置 try_files,用户直接访问 /user/123 或刷新页面,就会得到404。这相当于你改了门牌号却没有通知物业,客人按新地址上门却发现根本找不到这栋楼。Hash路由则永远不会有这个问题,因为 # 后面的内容不会发给服务器。

为何常见:开发时用 npm run dev 的本地服务器已经默认支持History回退,导致很多同学只在部署生产环境时才暴露问题,于是误以为两者等价。

误区我在路由守卫里判断了用户角色,没有管理员权限就跳转到403页,这样数据就安全了吧?

正解前端路由守卫只影响界面展示和跳转体验,无法阻止恶意用户直接调用后端API。真正的数据防护必须在后端每个接口做鉴权。守卫就像一个前台接待员,她能拦住陌生人进入会议室,但机密资料锁在保险柜里,那才是后端的责任。

为何常见:很多初学者看到页面被拦截,就以为攻击者也无路可走,忽略了网络请求可以直接构造这一事实。

误区为了追求极致性能,我把每个组件甚至每个按钮都写成动态导入,这样首屏加载一定更快。

正解过度拆分会让页面变成几十个几十KB的小文件,浏览器需要发出大量HTTP请求,反而拖慢加载。懒加载的粒度通常是“页面级”或“功能模块级”,而不是“组件级”。就像综合体按“展厅”分批开放,而不是连一把椅子也等顾客来了才搬。

为何常见:性能优化焦虑让新手一味追求“按需”,却忽略了网络请求数量和打包缓存之间的平衡。

误区Vue Router有 `beforeEach` 很方便,React Router没有官方守卫,是不是只能放弃权限控制?

正解React Router v6虽然不提供内置守卫,但可以通过封装 RequireAuth 高阶组件或编写通用组件包裹 <Route> 来实现,甚至能在 createBrowserRouter 的 loader 中做重定向。比如:

为何常见:初学者容易被框架的“有/无”清单限制思维,实际上路由守卫的本质是“导航前的拦截逻辑”,这在任何路由库中都能用组合或包装实现。

误区我在 `/user/123` 页面,然后通过 `router.push('/user/456')` 跳转,以为组件会销毁重建,直接在 `created` 里请求就完事了。

正解Vue和React在路径参数变化时默认复用同一个组件实例(因为组件类型没变),created 不会再次触发。你需要在Vue中用 beforeRouteUpdate 或 watch,在React中用 useEffect 监听 params.id 变化后再发新请求。这就像旅行团到了同一个景点但换了不同的展馆,导游需要重新讲解,而不是把游客赶出大楼重新入园。

为何常见:很多教程只演示了从列表页进入详情页的首次加载,没提在详情页之间切换的场景,导致实际开发时遇到“URL变了但内容没变”的困惑。

面试题

Q1. 关于前端路由,下列说法正确的是?

单选题 · easy

  • A.前端路由会导致页面整页刷新
  • B.Hash路由刷新时不会向服务器发请求← 正确答案
  • C.History路由无需服务器配置
  • D.前端路由只能在Vue中使用

答案:B。Hash路由的 # 后内容不会参与HTTP请求,因此刷新时浏览器不会把hash传给服务器,自然也不会重新请求HTML,所以B正确。A错误,前端路由正是为了避免整页刷新。C错误,History路由需要服务端重定向配合。D错误,React Router同样常用。

Q2. 在React Router v6中,嵌套路由的子组件应放置在父组件的哪个位置?

单选题 · mid

  • A.<Outlet /> 标签← 正确答案
  • B.<Link /> 标签
  • C.<Routes /> 标签
  • D.<NavLink /> 标签

答案:A。<Outlet /> 是父路由组件中用于渲染子路由的“插座”。<Link> 用于声明式跳转,<Routes> 用于定义路由配置的顶层容器,<NavLink> 是带激活样式的链接。把子路由渲染在 <Outlet> 处,就像综合体的子展厅嵌在父展厅的指定位置。

Q3. 关于History路由,下列描述正确的是?

单选题 · hard

  • A.依赖 hashchange 事件完成视图切换
  • B.直接刷新任意子路径都会返回入口HTML
  • C.修改URL必须通过 location.reload()
  • D.使用 pushState 修改URL不会触发页面刷新← 正确答案

答案:D。History路由使用pushState修改URL,浏览器不会因此刷新页面,D正确。它依赖popstate事件而非hashchange,A错。能否返回入口HTML取决于服务端是否配置重定向,不是History API自带的,B错。pushState不需要调用reload,C错。

Q4. Vue Router选项式API中,响应动态路由参数变化的组件内守卫是?

单选题 · mid

  • A.beforeRouteUpdate← 正确答案
  • B.beforeRouteEnter
  • C.beforeRouteLeave
  • D.onBeforeRouteUpdate

答案:A。beforeRouteUpdate 在同一个组件内参数变化时触发,适合重新拉取数据。beforeRouteEnter 在进入路由前触发,此时不能访问this;beforeRouteLeave 在离开前触发;onBeforeRouteUpdate 是Vue Router 4组合式API的对应写法,在选项式API中不能用。注意题干限定“选项式API”。

Q5. 路由懒加载的主要目的是?

单选题 · easy

  • A.减少首屏加载时间← 正确答案
  • B.让路由切换动画更流畅
  • C.提高代码可读性
  • D.修复路由守卫的Bug

答案:A。懒加载把每个路由对应的组件拆成独立chunk,首屏只加载核心框架和当前页,因此能显著减少首屏加载时间。它与切换动画、代码可读性、守卫Bug都没有直接关系。

Q6. 关于前端路由守卫的实现,下列说法中可能导致运行时问题的是?

多选题 · hard

  • A.在守卫中直接调用异步API但没有处理Loading状态← 正确答案
  • B.守卫逻辑中无条件调用next('/login')造成循环跳转← 正确答案
  • C.使用localStorage保存用户登录态
  • D.在守卫中只检查前端角色而不校验后端权限← 正确答案

答案:ABD。A会导致多次快速导航时竞态,返回顺序错乱;B中若访问 /login 仍触发守卫并再次 next('/login'),会无限重定向;D将前端守卫当成安全边界,攻击者可直接构造请求访问后端接口;C是合法的常见做法,不算问题。

Q7. 用户在SPA列表中往下滚了很远,点击进入详情再返回列表,期望恢复滚动位置。Vue Router中应利用哪个配置?

单选题 · mid

  • A.scrollBehavior← 正确答案
  • B.beforeEach
  • C.lazyLoad
  • D.scrollToTop

答案:A。Vue Router的scrollBehavior(to, from, savedPosition)函数返回滚动位置对象,可用savedPosition恢复原位置。beforeEach是路由守卫,lazyLoad不是路由配置项,scrollToTop也不存在。

Q8. React Router v6中,使用createBrowserRouter和RouterProvider,关于路由错误处理说法正确的是?

单选题 · extreme

  • A.通过errorElement渲染错误边界组件← 正确答案
  • B.必须使用componentDidCatch捕获路由错误
  • C.路由错误在开发环境静默处理
  • D.路由错误会直接导致浏览器白屏

答案:A。createBrowserRouter支持在路由配置中通过errorElement指定错误边界组件,当loader或render抛错时显示该组件。componentDidCatch是类组件的错误边界,但路由错误更适合用errorElement;C、D与框架行为不符。

Q9. 关于路由参数,下列说法正确的有?

多选题 · mid

  • A.React中动态路由参数可通过useParams获取← 正确答案
  • B.Vue Router查询字符串位于route.query← 正确答案
  • C.路径参数只能出现在URL末尾
  • D.查询参数与路径参数可同时存在← 正确答案

答案:ABD。React Router的useParams返回路径参数对象;Vue Router的route.query保存查询字符串;路径参数可以出现在任意位置,如/user/:id/posts;URL可以同时包含路径参数(/user/123)和查询参数(?tab=posts),因此C错。

Q10. 用户直接访问History模式的子路径,若服务器未配置重定向,会返回?

单选题 · easy

  • A.404页面← 正确答案
  • B.200页面
  • C.index.html
  • D.空白页

答案:A。History模式下,子路径在服务器上没有对应的物理文件,如果没配置try_files或类似规则,服务器会返回404。而Hash模式的hash部分不会发给服务器,所以不会出现这个问题。这就是History路由必须服务端配合的核心原因。