“路由”这个词,大概是最容易被低估的技术名词之一。
做运维的同事说路由是route add、ip route,是 Linux 服务器上让数据包找到出口的规则;做前端的同事说路由是 Vue Router 里配置path和component,是 SPA 页面切换的导航机制;做网络的同事则说路由是 OSPF、BGP,是核心交换机上那条决定流量走向的路径。
同一个词,三种完全不同的理解,可它们解决的问题本质是一样的:给数据或请求找到一条正确的到达路径。
过去几年我观察到一个现象:很多项目的线上故障,最后排查到根因时,往往不是代码逻辑问题,而是“路由”出了问题。要么是服务器双网卡回来路由走了错误出口,要么是前端动态路由在刷新后 404,要么是策略路由没配好导致业务流量走了低带宽链路。
所以这次我想把“路由”这件事从网络层、系统层、应用层三层拆开讲一遍。不是为了让你背命令,而是帮你建立一套完整的路由认知框架。你会发现,90% 的项目里,路由都不是“配一次就完事”的,它需要设计、需要验证、需要排错,也需要根据不同场景做取舍。
1. 路由到底是什么:一张跨层的“路径决策表”
在没有路由的世界里,网络通信只能靠广播,所有设备都在同一层楼里喊话,谁听到了谁响应。这在几台电脑的小房间里没问题,但在互联网这种亿万节点组成的复杂网络里,完全不现实。
路由的核心作用,就是为每一次通信决定“下一步往哪走”。
理解这一点,你就能把各个领域里的“路由”概念统一起来:
- 网络层路由:IP 数据包到达路由器后,路由器查路由表,决定从哪个接口转发出去。
- 系统层路由:Linux 主机有多块网卡时,内核根据路由表决定访问某个目标 IP 走哪块网卡、哪个网关。
- 前端路由:浏览器地址变化后,前端框架根据路由表匹配对应的组件,决定渲染哪个页面。
- 后端路由:请求到达服务端后,框架根据 URL 和方法匹配对应的 Controller 或处理函数。
它们本质都是一张“路径决策表”,只是服务的对象不同。
这也是我为什么强调“别再只会简单配置路由”。因为大多数人只接触过其中一层,遇到跨层问题时就会懵。比如服务器上明明配了静态路由,但前端页面访问还是超时;比如 Vue 动态路由在页面刷新后失效,你查了半天前端代码,最后发现是路由持久化的问题。
认清路由的多层属性后,你会更容易定位问题:先分清是网络层、系统层还是应用层,再针对性排查。
2. 网络层路由:从静态路由到动态路由
网络层的路由是基础中的基础,也是很多后端开发容易忽略的部分。你可能在笔记本上敲过ip route,但未必真正理解它在生产环境中的意义。
2.1 静态路由配置实战
所谓静态路由,就是管理员手动在路由器或主机上写死的路径规则。它简单、可控、没有协议开销,适合网络拓扑稳定、规模较小的场景。
Linux 下查看当前路由表:
ip route show输出类似:
default via 192.168.1.1 dev eth0 proto static 10.0.0.0/8 via 192.168.1.254 dev eth0 172.16.0.0/12 dev eth1 proto kernel scope link src 172.16.0.2这三行含义分别是:
- 默认路由:所有不匹配其他规则的数据包,都走
eth0,网关是192.168.1.1。 - 去往
10.0.0.0/8网段的数据包,走eth0,网关是192.168.1.254。 172.16.0.0/12直连网段,通过eth1直接可达,不需要网关。
临时添加一条静态路由:
ip route add 10.10.0.0/16 via 192.168.2.1 dev eth1删除路由:
ip route del 10.10.0.0/16 via 192.168.2.1 dev eth1为什么用ip route而不是老式的route add?因为ip命令是iproute2工具包提供的,功能更强,语法更清晰,而且能直接看到proto、scope这些元信息。很多老教程还停留在route add -net ... gw ...,在生产环境里建议尽早切换到ip命令体系。
静态路由看似简单,真正容易踩坑的地方是“永久配置”。直接用ip route add添加的路由,重启网络服务或重启服务器后会消失。这正好对应热搜词里那个高频问题:Linux 添加静态路由提示 file exist。
这个报错通常有两个原因:
- 路由已经存在,重复添加。
- 添加的路由和现有路由冲突,比如你试图添加一条更具体的路由,但内核认为它已经被现有规则覆盖。
排查方式很简单:先执行ip route show | grep <目标网段>确认是否已存在,再用ip route replace替代ip route add来做更新操作。
2.2 永久静态路由配置
不同 Linux 发行版的永久路由配置方式不同,这是很多人换系统后就懵的原因。
CentOS / RHEL 7/8 系列,在网卡配置文件中添加:
# /etc/sysconfig/network-scripts/route-eth1 10.10.0.0/16 via 192.168.2.1 dev eth1配置完成后:
systemctl restart network麒麟系统等基于 RPM 体系的国产操作系统,也可以沿用这套方式。如果是在较新的 NetworkManager 环境里,更推荐用nmcli管理:
nmcli connection modify eth1 +ipv4.routes "10.10.0.0/16 192.168.2.1" nmcli connection up eth1Ubuntu / Debian 系列则在/etc/netplan/或/etc/network/interfaces中配置。Netplan 示例:
# /etc/netplan/01-netcfg.yaml network: version: 2 ethernets: eth1: routes: - to: 10.10.0.0/16 via: 192.168.2.12.3 动态路由与策略路由
静态路由适合小规模、拓扑稳定的场景。但一旦网络规模变大,链路故障频繁,人工维护静态路由就不现实了。这时候需要动态路由协议,让路由器之间自动交换路由信息。
- OSPF:内部网关协议,适合企业内部网络。它通过链路状态算法计算最短路径,收敛速度快。
- BGP:外部网关协议,用于互联网 AS 之间的路由交换,也是云厂商专线接入常用的协议。
动态路由的配置属于网络工程师的核心技能,通常要结合实验环境学习。对后端开发和运维来说,理解“动态路由能自动感知链路变化并重新计算路径”就够了,真正在路由器上配 OSPF 的场景不多。
但策略路由(PBR)值得多了解一些。它解决的是“路由表无法表达复杂策略”的问题。
举个例子:服务器有电信和联通两条出口线路。默认路由走电信,但某些业务需要强制走联通出口。普通路由表做不到按业务区分,因为路由表只认目标 IP。策略路由可以基于源 IP、源端口、协议等条件选择不同的路由表。
Linux 下用ip rule配合多路由表实现:
# 创建一张独立路由表 echo "100 custom" >> /etc/iproute2/rt_tables # 在这张表里添加默认路由,走联通网关 ip route add default via 203.0.113.1 dev eth2 table custom # 指定来自 10.0.0.0/8 的数据包查询 custom 表 ip rule add from 10.0.0.0/8 table custom # 刷新策略路由缓存 ip route flush cache这样来自内网的流量就会优先查询custom表,而不是主路由表。应用层无需做任何改造,就把流量按源地址分流了。策略路由在生产环境非常实用,但也是理解门槛最高的路由技术之一。
3. Linux 系统路由配置实战:从双网卡到 VIP 漂移
后端开发和运维在日常工作中接触最多的,其实是 Linux 主机上的路由配置。别小看这块,很多“神秘”的线上故障最后都指向它。
3.1 双网卡场景:路由优先级是关键
很多服务器有两块网卡:一块内网、一块外网。如果默认路由配置不正确,很容易出现“能通外网但内网不通”或者反过来。
看一个典型场景:
# eth0: 内网 192.168.1.10 # eth1: 外网 10.0.0.10如果不做任何额外配置,系统会为两个直连网段自动生成路由,但默认路由只会有一个。这时候访问互联网走的是系统选中的那个网关,可能走内网出去了,结果源 IP 是内网地址,回包到不了,业务就超时。
正确的做法是:明确指定外网网卡为默认路由,内网走静态路由:
# 删除可能冲突的默认路由 ip route del default # 添加默认路由指向外网网关 ip route add default via 10.0.0.1 dev eth1 # 添加内网网段静态路由 ip route add 192.168.0.0/16 via 192.168.1.1 dev eth0把这段配置写入永久配置(route-eth1 或 netplan),重启后依然生效。
这里真正容易踩坑的是,多网卡环境下ip route add添加静态路由时提示file exist。原因往往不是路由真的存在,而是你添加的路由与内核自动生成的直连路由冲突,或者网卡没有处于 up 状态。先ip addr show确认网卡状态,再查路由表。
3.2 Keepalived VIP 漂移后路由不清理
搜索热词里有一个很典型的问题:Keepalived 的 VIP 漂移后,VIP 路由不会自动清理。
场景还原:两台服务器通过 Keepalived 组成主备,VIP 绑定在主节点上。当主节点宕机,VIP 漂移到备节点后,客户端访问依然失败。排查发现是备节点上还残留着指向旧 VIP 的静态路由,或者 Keepalived 切换后没有重新添加 VIP 的路由。
这个问题的根因通常有两个:
- Keepalived 配置中
vrrp_instance的virtual_ipaddress没有在切换时正确执行ip addr add/ip addr del。 - 系统层面存在静态路由指向 VIP 的旧路径,漂移后没有联动更新。
更稳妥的做法是用 Keepalived 的notify_master和notify_backup脚本,在状态切换时主动执行路由更新逻辑:
# /etc/keepalived/notify_master.sh #!/bin/bash ip route replace 10.0.0.0/8 via 192.168.10.1 dev eth0 ip route flush cache这个例子说明:在系统层,路由不仅仅是初始化时配一次,还要考虑故障切换时的动态调整。
4. 网关、VLAN、DHCP、DNS 与路由的关系
很多人分不清 IP、子网掩码、网关、VLAN、DHCP、DNS、路由、端口这些概念,因为它们总是一起出现。这里用一个通俗的类比解释一下:
你在一栋写字楼里办公,这栋楼就是你的网络。
- IP 地址:你的具体房间号。
- 子网掩码:告诉你哪些房间在同一层楼,可以直接串门,不需要经过前台。
- 网关:楼层出入口,去其他楼层必须经过这里。
- 路由:前台手里的登记本,记录去不同楼层应该走哪个通道。
- VLAN:在物理楼层的墙上隔出透明玻璃墙,让不同公司的人即使在同层也不能直接串门。
- DHCP:前台自动分配房间号的机制,你入住时不需要自己选房间。
- DNS:公司内部通讯录,你只需说“找张三”,它帮你查到张三的房间号。
- 端口:房间里的具体工位,同一个房间可能坐了好几个人。
把这些概念分开理解,再看网络问题就会清晰很多。路由解决的是“数据包从哪走”的问题,而网关是路由路径上的第一个必经节点。很多路由故障,本质上不是路由表配错了,而是网关不通或者子网掩码算错了范围。
5. 前端路由:应用层的路径决策
聊完网络层和系统层,接下来是前端开发最熟悉的领域:应用层路由。为什么前端需要路由?因为 SPA(单页应用)要在一个 HTML 页面里模拟多个页面的效果,需要一套机制来响应 URL 变化并渲染不同组件。
5.1 前端路由的两种模式
Vue Router 和 React Router 都支持两种模式,理解它们的差异是排查问题的前提。
Hash 模式:
URL 形如http://example.com/#/user/123,路由信息在#后面。#的变化不会触发浏览器向服务器发请求,所以部署简单,不需要服务端额外配置。缺点是 URL 不美观,SEO 不友好。
History 模式:
URL 形如http://example.com/user/123,依赖 HTML5 History API。URL 美观、SEO 友好,但刷新页面时浏览器会真实请求/user/123这个路径,如果服务端没有配置 fallback 到index.html,就会出现 404。
这是前端路由最常见的坑:本地开发正常,部署到服务器后一刷新就 404。解决方案是让 Nginx 把所有未匹配到静态文件的请求都 rewrite 到index.html:
location / { try_files $uri $uri/ /index.html; }5.2 Vue Router 3 与 Vue Router 4 的核心差异
Vue Router 4 是为 Vue 3 设计的版本,和 Vue Router 3 相比有几个关键变化:
createRouter/createWebHistory替代了new VueRouter()。- 不再依赖
this.$router,而是通过useRouter()组合式 API 获取路由实例。 - 移除了
*通配符路由,改用/:pathMatch(.*)*捕获所有未匹配路径。 - 类型推导更强,对 TypeScript 更友好。
Vue Router 4 在 Vite 项目里的基础配置:
// src/router/index.js import { createRouter, createWebHistory } from 'vue-router' const routes = [ { path: '/', name: 'Home', component: () => import('../views/Home.vue') }, { path: '/user/:id', name: 'UserDetail', component: () => import('../views/UserDetail.vue') } ] const router = createRouter({ history: createWebHistory(), routes }) export default router注意这里用了动态导入() => import(...),实现了路由级代码分割。访问该路由时才加载对应组件,减少首屏体积。这是 90% 项目都应该养成的习惯,但很多新手图省事会直接import Home from '../views/Home.vue'然后静态注册,导致首屏加载大量无关代码。
5.3 动态路由与权限控制
生产项目里,路由表常常不是写死的,而是根据用户权限动态生成。比如管理员能看到“用户管理”菜单,普通用户看不到。典型做法是:
- 用户登录后,后端返回该用户可访问的路由权限列表。
- 前端根据权限列表,用
router.addRoute()动态注册路由。 - 同时把菜单树渲染出来。
Vue 3 + Vue Router 4 动态注册路由的代码实践:
// src/permission.js import router from './router' import { useUserStore } from './stores/user' router.beforeEach(async (to, from, next) => { const userStore = useUserStore() if (!userStore.token) { if (to.path === '/login') { next() } else { next('/login') } return } // 已登录且路由表未初始化 if (userStore.roles.length === 0) { try { const permissions = await userStore.fetchUserInfo() const dynamicRoutes = generateRoutes(permissions) dynamicRoutes.forEach(route => { router.addRoute(route) }) // 重新触发当前导航,避免刷新后动态路由不生效导致 404 next({ ...to, replace: true }) } catch (error) { userStore.resetToken() next('/login') } return } next() })这段代码解决了热搜词里“vue3 vite 动态路由”和“刷新页面后动态路由失效”的问题。核心要点是:动态路由是在导航守卫里异步添加的,添加完成后必须用next({ ...to, replace: true })重新触发一次导航,否则当前路由可能匹配不到。
路由传参也是高频问题。Vue Router 4 中,query方式传参会反映在 URL 的?后面,params方式配合动态路径参数。很多新手会在params里传对象或数组,刷新后丢失,这是因为params里的数据没有持久化到 URL。正确做法是:需要持久化的数据用query或状态管理,临时数据用params。
5.4 React Router 与 Vue Router 的差异与选型
React Router 和 Vue Router 解决的是同一类问题,但设计理念有明显差异:
| 维度 | React Router | Vue Router |
|---|---|---|
| 声明方式 | 组件式声明,路由是 JSX 组件 | 配置式为主,路由是配置对象 |
| 路由守卫 | 没有内置守卫,用组件生命周期或封装实现 | 内置 beforeEach 等守卫,权限控制更直接 |
| 数据获取 | 路由与组件解耦,由组件自行处理 | 可以配置 beforeRouteEnter 等路由级钩子 |
| 嵌套路由 | 通过 Outlet 实现 | 通过 children 配置实现 |
| 学习曲线 | 需要理解渲染模型,初学略绕 | 配置直观,容易上手 |
选型建议其实很简单:
- 如果你用 Vue 技术栈,首选 Vue Router,它们深度集成,响应式更新更自然。
- 如果你用 React 技术栈,React Router 是事实标准,配合数据路由模式(loaders/actions)可以实现更强的前后端路由协同。
- 如果项目使用了微前端或大型管理系统,Vue Router 的配置式路由和守卫机制在权限控制上更顺手;React Router 则更灵活,适合深度自定义场景。
React Router v6 的数据路由写法:
// src/main.jsx import { createBrowserRouter, RouterProvider } from 'react-router-dom' const router = createBrowserRouter([ { path: '/', element: <Layout />, children: [ { path: 'dashboard', element: <Dashboard /> }, { path: 'user/:id', element: <UserDetail /> } ] } ]) export default function App() { return <RouterProvider router={router} /> }React Router 的守卫可以由包装组件实现:
function RequireAuth({ children }) { const { token } = useAuthStore() const location = useLocation() if (!token) { return <Navigate to="/login" state={{ from: location }} replace /> } return children }6. 路由设计在微前端与权限系统中的实践
当项目发展到一定规模,路由设计不再只是“配几个页面路径”,而是要和权限系统、微前端架构结合。
6.1 服务端返回路由菜单
搜索热词里有一条“arco pro vue 从服务端获取路由菜单”,这是中后台系统的常见需求。思路是:前端不写死菜单,登录后请求后端接口,后端根据用户角色返回菜单树,每个菜单项包含path、name、component等字段,前端拿到后动态生成路由和菜单。
这个模式下有个关键设计问题:菜单接口返回的 component 字段怎么处理?你不能直接拿字符串去加载组件。常见的解决方案是维护一个组件映射表:
// src/router/component-map.js const componentMap = { 'system/UserManage': () => import('../views/system/UserManage.vue'), 'system/RoleManage': () => import('../views/system/RoleManage.vue'), 'order/OrderList': () => import('../views/order/OrderList.vue') } export function mapComponent(componentKey) { return componentMap[componentKey] || (() => import('../views/error/NotFound.vue')) }后端返回component: 'system/UserManage',前端查表得到真正的组件。这个方案比用import()动态拼接路径安全得多,因为动态拼接路径在打包时容易被 Tree-shaking 误删,而且存在路径注入风险。
6.2 微前端子应用路由异常
微前端场景下,Vue 主应用 + Vue 子应用的路由冲突是常见问题。典型表现是:子应用内部跳转正常,但从主应用切换到子应用时白屏或 404;或者子应用路由跳转后主应用的路由被覆盖。
解决思路是要给子应用路由设置 base,让子应用路由只在它自己的挂载路径下生效:
// 子应用 router const router = createRouter({ history: createWebHistory(window.__POWERED_BY_QIANKUN__ ? '/subapp' : '/'), routes })这里用了微前端框架注入的全局变量__POWERED_BY_QIANKUN__来判断运行环境,动态设置 base 路径。这个做法的核心是:主应用和子应用各管各的路由前缀,互不侵入。
另外一个高频问题是“vue3 路由跳转不刷新页面”。这通常是因为路由没有真正变化,比如跳转到当前路由但参数不同,而组件复用了。解决方案是用watch监听路由变化:
// 在组件中监听路由参数变化 import { watch } from 'vue' import { useRoute } from 'vue-router' const route = useRoute() watch( () => route.params.id, (newId, oldId) => { if (newId && newId !== oldId) { fetchData(newId) } }, { immediate: true } )注意:路由跳转不刷新页面不一定是 bug,组件复用是性能优化机制。如果确实需要强制刷新,可以给组件加:key="$route.fullPath",但更推荐的方式是监听路由变化主动更新数据。
7. 常见问题与排查思路
从网络层到应用层,路由相关的问题五花八门。这里整理一份高频排查表,建议收藏备用。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| Linux 添加静态路由提示 file exist | 路由已存在或与直连路由冲突 | ip route show查现有路由 | 用ip route replace替代 add |
| 重启后静态路由消失 | 使用了临时命令,未写入配置文件 | 查看 route-ethX 或 netplan 配置 | 写入对应发行版的永久配置 |
| 双网卡服务器外网访问异常 | 默认路由指向了内网网关 | ip route show确认 default 路由 | 删除错误默认路由,指定外网网关 |
| 多出口流量无法按业务分流 | 路由表不支持基于源的策略 | 用ip rule show查看策略规则 | 配置策略路由 PBR |
| Keepalived VIP 漂移后业务中断 | 切换后路由未联动更新 | ip addr show、ip route show | 使用 notify_master/backup 脚本更新路由 |
| Vue 动态路由刷新后 404 | 路由未持久化或未重新触发导航 | 刷新后查看当前路由路径 | 在导航守卫中重新注册路由并next({...to, replace: true}) |
| History 模式部署后刷新 404 | Nginx 未配置 try_files fallback | 请求 URL 直接访问看返回结果 | 配置try_files $uri $uri/ /index.html |
| 路由跳转后页面不刷新 | 组件复用导致数据未更新 | 打印路由参数确认是否变化 | 用 watch 监听路由参数变化 |
| 微前端子应用路由跳转后主应用串路由 | 子应用路由 base 未设置 | 查看子应用 URL 和主应用 URL | 按微前端挂载路径设置 base |
| 前端路由传参刷新丢失 | 把复杂对象放到了 params | 刷新后打印路由参数 | 持久化数据用 query 或状态管理 |
在这份表里,真正的高频问题集中在两个方向:静态路由的持久化和前端动态路由的刷新失效。它们背后其实是同一个教训——路由规则不能只存在于运行时内存里,必须有持久化方案和重新触发的机制。
8. 最佳实践与工程建议
围绕路由设计,我总结了几条在不同层面都通用的最佳实践。
8.1 网络与系统层:最小权限、可回滚、可观测
- 测试环境验证后再上生产。路由变更尤其是策略路由变更,影响的是整个数据路径,一旦配错可能导致大面积网络中断。至少在测试环境执行一遍
ip route add、ip route del、重启网络服务、重启服务器的完整流程,确认永久配置有效。 - 变更前备份路由表。执行复杂变更前用
ip route save > /tmp/route.bak保存,回滚时用ip route restore恢复。这个习惯能让你在故障时快速回到上一个稳定状态。 - 所有永久配置要写清楚注释。路由配置不像应用代码有完整的代码评审,但它同样需要被维护。在
/etc/sysconfig/network-scripts/route-eth1里写清楚为什么要添加这条路由、目标网段是什么、网关为什么选这个,能帮同事省下大量排查时间。 - 监控路由变化。生产环境的静态路由不应该频繁变化。如果路由表经常变动,说明网络拓扑不稳定或者有 DHCP 动态干扰,需要从根因治理,而不是被动加路由。
8.2 前端层:动态路由要解决持久化和权限恢复
- 动态路由的权限数据尽量从服务端获取。不要让前端根据角色自己去拼路由表,服务端统一管理权限规则,前端只负责渲染,权限变更不需要发版。
- 动态路由添加后要处理刷新场景。如果动态路由数据只存在内存里,刷新就会丢失。把权限列表持久化到 localStorage 或 Pinia 的持久化插件里,并在导航守卫里做恢复。
- 路由守卫里的业务逻辑要精简。不要在
beforeEach里做太多异步操作,否则每次导航都会被拖慢。把用户信息获取、权限校验、路由注册拆成明确的步骤,并加上超时和错误处理。 - 约定式路由和配置式路由按项目规模选择。小项目用约定式路由(文件系统即路由)能提升开发效率,大项目建议配置式路由,路径更清晰,权限控制更可控。Vite 等构建工具已经支持约定式路由的自动生成,但别盲目追求“少写代码”而牺牲可维护性。
8.3 通用原则:先区分层,再动手排错
遇到路由问题,最忌讳的是在错误的层面反复尝试。一条标准排错路径:
- 先确认问题发生在哪层。前端页面 404,先看是不是 History 模式刷新问题;服务器访问超时,先看路由表和网关。
- 网络层看连通性,系统层看路由表,应用层看路由配置和守卫逻辑。
- 每一次修改都要验证,不要一次改多处以免无法定位问题。
- 把排查过程记录下来,路由问题往往具有环境相关性,下次遇到同类型问题可以直接复用。
9. 总结与后续学习方向
路由不是一个可以“配一次就忘记”的技术点。它在不同技术栈里反复出现,每次出现都在解决同一个核心问题:如何把请求或导航引导到正确的位置。
本文从网络层的静态路由、动态路由和策略路由,讲到 Linux 系统的双网卡路由和 Keepalived 联动,再延伸到前端 Vue Router 与 React Router 的动态路由、权限控制、微前端路由隔离,覆盖了 90% 项目里会遇到的典型路由场景。
如果你的下一步想继续深入,可以从这几个方向入手:
- 用虚拟化环境搭建一个双网卡 Ubuntu/CentOS 服务器,练习永久静态路由配置,并故意断开链路观察路由变化。
- 把一个 Vue 3 项目改造成动态路由 + 服务端返回菜单的架构,重点测试刷新后路由恢复和权限变更场景。
- 学习 OSPF 和 BGP 的基本原理,用模拟器跑一个多路由器互联实验,理解动态路由协议是如何自动计算和收敛路径的。
- 如果项目用到微前端,重点研究主应用和子应用的路由通信与隔离方案,这部分坑最多,也最能体现路由设计的工程价值。
路由相关的知识看起来零散,实际有一条主线:它永远是数据和流量的导数,决定了前往目的地的方向。把这个方向感掌握好,无论是写前端页面还是管服务器,你都比只会敲命令或只会在框架里配 path 的人多一层全局判断力。建议收藏备用,遇到路由问题回来翻一翻。