一文带你掌握Vue中的路由守卫
说在前面
在构建 Vue 应用程序时,路由守卫是确保流畅用户体验和强大逻辑控制的关键工具。本文将一起探讨 Vue 路由守卫的概念、类型和实际应用,帮助你理解如何利用这些守卫来增强应用的导航能力、实现权限控制和优化用户流程。
一、 简介
在构建现代 Web 应用程序时,路由管理是实现复杂页面结构和流畅用户体验的关键。Vue.js,作为一种流行的前端框架,通过 Vue Router 插件提供了强大的路由管理功能。在这个框架中,路由守卫(Route Guards)扮演着至关重要的角色,它们是确保应用程序导航逻辑正确性和增强用户体验的重要工具。
路由守卫的重要性在于它们提供了一种机制,允许我们在路由发生之前执行逻辑检查和操作。无论是验证用户权限、保护页面免受未授权访问,还是实现复杂的导航逻辑,路由守卫都能确保我们的应用程序在用户与界面交互时保持一致性和安全性。它们是开发者用来控制路由访问权限、管理页面转换和优化加载流程的强大盟友。
二、 Vue 路由守卫概念
什么是路由守卫
路由守卫(Route Guards)是 Vue Router 的一个功能,它允许我们在路由发生之前执行逻辑判断。这些守卫可以用来实现权限验证、页面访问控制、数据预加载等逻辑,确保用户在应用中的导航流程符合我们的业务需求。
路由守卫主要有以下几种类型:
1.全局守卫(Global Guards):
beforeEach
:在路由进入之前全局调用。可以用来做权限验证,如果用户未登录,可以重定向到登录页面。beforeResolve
:在路由解析之前调用,它在beforeEach
之后和afterEach
之前调用。afterEach
:在路由确认之后调用,通常用于记录日志或执行一些全局操作。
2.路由独享守卫(Route-specific Guards):
可以在路由配置中直接定义 beforeEnter
守卫,这些守卫只适用于特定的路由。
3.组件内守卫(Component-specific Guards):
- 在 Vue 组件中定义,包括
beforeRouteEnter
、beforeRouteUpdate
和beforeRouteLeave
。 beforeRouteEnter
:在渲染该组件的对应路由被确认前调用,不能访问组件实例 (this
)。beforeRouteUpdate
:在当前路由改变,但是该组件被复用时调用(例如,对于一个带有动态参数的路径)。beforeRouteLeave
:在离开该组件的对应路由时调用,可以用来阻止用户在未保存修改的情况下离开。
路由守卫通过 next
函数来决定路由是否继续。next
函数可以接受三个参数:next()
、next(false)
或 next('/')
。其中,next()
表示继续当前路由,next(false)
表示中断当前路由,next('/')
表示跳转到一个新路由。
使用路由守卫,我们可以在路由进入前进行条件判断,从而实现更细粒度的控制,确保用户只能访问他们有权限的页面,或者在页面跳转前完成必要的数据加载和验证。这是构建健壮、用户友好的 Vue 应用程序的重要工具之一。
路由守卫与导航流程的关系
路由守卫(Route Guards)与导航流程在 Vue 应用中的关系密切,它们共同构成了应用的路由导航机制。以下是它们之间的关系:
导航流程
在 Vue 应用中,用户可以通过点击链接、使用 vue-router
的 push
或 replace
方法,或者直接在浏览器地址栏输入 URL 来触发导航。导航流程通常包括以下几个步骤:
- 导航触发:用户或程序发起导航请求。
- 路由匹配:
vue-router
查找与请求路径匹配的路由记录。 - 路由守卫调用:在找到匹配的路由后,根据配置的路由守卫执行相应的逻辑。
- 组件路由:如果路由守卫允许导航继续,
vue-router
将加载或激活相应的组件。 - 导航确认:组件被渲染或激活,导航完成。
- 导航完成:
vue-router
确认导航,可能触发afterEach
全局守卫。
路由守卫
路由守卫是导航流程中的关键环节,它们可以在导航的不同阶段执行逻辑判断和处理:
- 全局守卫(如
beforeEach
)在每次导航开始前被调用,可以用来执行全局的权限验证或资源加载。 - 路由独享守卫(如路由配置中的
beforeEnter
)针对特定路由执行逻辑,通常用于该路由特有的权限或条件检查。 - 组件内守卫(如
beforeRouteEnter
、beforeRouteUpdate
、beforeRouteLeave
)与组件实例相关,用于处理组件级别的导航逻辑,如数据预加载或离开前的确认。
关系
路由守卫与导航流程的关系可以这样理解:导航流程是导航发生的顺序和步骤,而路由守卫是嵌入到这个流程中的逻辑检查点。它们允许开发者在导航的关键时刻介入,根据应用的业务需求做出决策,如是否允许导航继续、是否需要重定向到其他路由、是否需要加载额外的数据等。
通过这种方式,路由守卫为 Vue 应用提供了灵活性和控制力,确保用户在应用中的导航体验既安全又高效。开发者可以利用路由守卫来保护资源、优化性能和提升用户体验。
三、全局守卫
beforeEach 和 afterEach 全局守卫
beforeEach
和 afterEach
是 Vue Router 提供的全局守卫,它们允许你在路由导航的前后执行逻辑,这些守卫可以用于所有路由的导航,不仅限于特定的路径或组件。
beforeEach 全局前置守卫
beforeEach
是在路由进入之前全局调用的守卫。它是导航过程中最早被调用的守卫,可以用来做权限验证、重定向未认证用户、记录日志等。如果这个守卫返回 false
或调用 next(false)
,当前导航将中断;如果调用 next()
或 next(true)
,则导航继续。
router.beforeEach((to, from, next) => { // 检查用户是否已登录,如果未登录,重定向到登录页面 if (to.meta.requiresAuth) { if (isAuthenticated()) { next(); // 用户已登录,允许访问 } else { next({ path: '/login' }); // 用户未登录,重定向到登录页面 } } else { next(); // 无需验证,继续导航 } });
afterEach 全局后置守卫
afterEach
是在路由确认之后调用的守卫,它在导航完成后执行,不会接收 next
函数也不会改变导航本身。这个守卫通常用于记录用户的行为、更新页面标题、清理资源等。
router.afterEach((to, from) => { // 记录路由访问日志 console.log(`Navigated to ${to.path}`); // 设置页面标题 document.title = to.meta.title; // 执行其他清理或更新操作 });
应用场景
- 权限验证:在
beforeEach
中检查用户是否有权限访问某个路由。 - 重定向:将未认证的用户重定向到登录页面。
- 页面访问记录:在
afterEach
中记录用户的访问路径。 - 设置文档标题:根据当前路由动态设置网页的标题。
- 状态恢复:在
afterEach
中恢复或清理组件的状态。
全局守卫是 Vue 应用中控制路由行为的强大工具,它们使得开发者能够在全局范围内对路由进行统一管理。通过合理使用这些守卫,可以提升应用的安全性、用户体验和性能。
实例:实现全局权限验证
要实现全局权限验证,你可以使用 Vue Router 的 beforeEach
全局前置守卫。这个守卫会在路由跳转之前运行,因此它是检查用户权限的理想位置。以下是一个简单的实例,展示了如何实现全局权限验证:
// 假设你有一个方法来检查用户是否已经登录 const isAuthenticated = () => { // 这里应该是你的认证逻辑,例如检查本地存储或会话中的 token // 返回 true 如果用户已登录,否则返回 false return localStorage.getItem('userToken') !== null; }; // 假设你有一个路由配置对象 const router = new VueRouter({ // ...你的路由配置 }); // 全局前置守卫 router.beforeEach((to, from, next) => { // 如果路由需要认证(例如,不是登录页面) if (to.matched.some(record => record.meta.requiresAuth)) { // 检查用户是否已登录 if (isAuthenticated()) { // 用户已登录,允许访问 next(); } else { // 用户未登录,重定向到登录页面 next({ path: '/login', query: { redirect: to.fullPath } // 保存要重定向的路由 }); } else { // 如果用户尝试访问不需要认证的路由,直接放行 next(); } } else { // 路由不需要认证,直接放行 next(); } }); // 然后你可以在你的 Vue 应用中使用这个路由器 new Vue({ router, // ...其他选项 });
在这个例子中,我们首先定义了一个 isAuthenticated
方法,它应该包含你的认证逻辑,例如检查用户的登录状态。然后,我们在 beforeEach
全局前置守卫中检查即将访问的路由是否需要认证。如果用户已经登录,我们就允许路由继续;如果用户未登录,我们就将用户重定向到登录页面,并保存当前想要访问的路由路径,以便登录后可以返回。
请注意,这个例子假设你已经在本地存储(如 localStorage
)中保存了一个表示用户登录状态的标记(例如,token)。你需要根据你的后端认证系统来实现 isAuthenticated
方法的具体逻辑。此外,你可能还需要在用户登录后更新 localStorage
中的标记,并在用户登出时清除它。
实例:记录用户导航日志
要记录用户的导航日志,你可以使用 Vue Router 的 afterEach
全局后置守卫。这个守卫在路由导航确认之后被调用,可以用来记录用户访问的页面信息。以下是一个简单的实例:
import router from './router'; // 假设你已经定义了一个 router 对象 // 日志记录函数 function logNavigation(to, from) { console.log(`User navigated from ${from.path} to ${to.path}`); // 这里你可以将日志信息发送到服务器,或者存储在本地 } // 使用全局后置守卫记录导航日志 router.afterEach(logNavigation); // 现在,每当用户导航到新路由时,logNavigation 函数都会被调用
在这个例子中,我们定义了一个 logNavigation
函数,它接收两个参数:to
和 from
。to
对象包含用户即将导航到的路由信息,而 from
对象包含用户从哪个路由导航过来。这个函数被用作 afterEach
守卫的回调,因此它会在每次导航完成后被调用。
你可以将这个日志记录函数扩展为更复杂的逻辑,例如:
- 将日志信息保存到本地存储或数据库中。
- 发送日志信息到服务器端的日志系统。
- 添加时间戳和其他有用的上下文信息。
- 根据用户的导航模式执行不同的日志记录策略。
请注意,如果你选择将日志信息发送到服务器,你可能需要在 logNavigation
函数中调用一个异步的日志上传函数,并确保在发送日志之前用户已经成功导航到新路由。这可以通过在 next
函数调用之后执行日志上传来实现。
四、路由独享守卫
路由独享守卫的用法
路由独享守卫是针对特定路由的守卫,它们只在特定路由被访问时触发。这些守卫可以在路由配置中直接定义,用于执行与该路由相关的逻辑,如权限检查、数据预加载等。
路由独享守卫的用法示例
假设你有一个用户 dashboard 页面,只有管理员才能访问。你可以在路由配置中为这个路由添加一个 beforeEnter
守卫来确保只有管理员可以访问:
const router = new VueRouter({ routes: [ { path: '/dashboard', component: DashboardComponent, meta: { requiresAuth: true }, // 路由元信息,标记此路由需要认证 beforeEnter: (to, from, next) => { // 检查用户是否为管理员 if (userIsAdmin()) { next(); // 允许进入 } else { next({ path: '/' }); // 非管理员用户重定向到首页 } } }, // ...其他路由 ] });
在这个例子中,userIsAdmin
是一个假设的函数,用来检查当前用户是否具有管理员权限。如果用户是管理员,next()
被调用,允许路由继续;如果不是,next({ path: '/' })
被调用,用户将被重定向到首页。
路由独享守卫的特点
- 针对性强:路由独享守卫只作用于特定的路由,不会影响其他路由。
- 配置灵活:可以在路由配置中直接添加,无需在全局守卫中编写特定逻辑。
- 元信息使用:可以通过路由的
meta
属性传递额外的信息,如本例中的requiresAuth
。
注意事项
- 路由独享守卫不会接收
next
函数作为参数,因为它们是作为路由配置的一部分定义的。 - 如果你需要访问组件实例,应该使用组件内的导航守卫,如
beforeRouteEnter
。 - 路由独享守卫应该用于简单的逻辑判断,对于复杂的逻辑,可能需要结合全局守卫或组件内守卫来实现。
通过使用路由独享守卫,你可以为每个路由定制特定的导航逻辑,从而更好地控制应用的路由行为和用户体验。
实例:为特定路由添加访问限制
为特定路由添加访问限制通常涉及到权限验证。以下是一个实例,展示了如何为一个假设的后台管理系统中的 /admin
路由添加访问限制,确保只有具有管理员权限的用户才能访问:
import Vue from 'vue'; import VueRouter from 'vue-router'; import AdminComponent from './components/AdminComponent.vue'; import LoginComponent from './components/LoginComponent.vue'; Vue.use(VueRouter); const routes = [ { path: '/', component: LoginComponent, // 默认重定向到登录页面 }, { path: '/admin', component: AdminComponent, meta: { requiresAuth: true }, // 标记此路由需要认证 // 路由独享守卫 beforeEnter: (to, from, next) => { // 假设有一个方法来检查用户是否是管理员 const isAdmin = checkIfAdmin(); if (isAdmin) { next(); // 用户是管理员,允许访问 } else { next('/'); // 用户不是管理员,重定向到首页或其他允许访问的页面 } }, }, // ...其他路由 ]; const router = new VueRouter({ routes, }); function checkIfAdmin() { // 这里应该是你的认证逻辑,例如检查本地存储中的管理员令牌 // 返回 true 如果用户是管理员,否则返回 false return localStorage.getItem('adminToken') !== null; } export default router;
在这个例子中,我们定义了一个 checkIfAdmin
函数,它应该包含你的管理员权限验证逻辑。我们假设管理员权限是通过一个存储在本地存储中的令牌来标识的。如果用户是管理员,next()
被调用,允许路由继续;如果用户不是管理员,next('/')
被调用,用户将被重定向到首页或其他允许访问的页面。
请注意,这个例子假设你已经在本地存储中保存了一个表示用户管理员状态的标记(例如,adminToken
)。你需要根据你的后端认证系统来实现 checkIfAdmin
函数的具体逻辑,并在用户登出时清除这个标记。
通过这种方式,你可以为 Vue 应用中的特定路由添加访问限制,确保只有具有相应权限的用户才能访问敏感内容。
五、组件内守卫
beforeRouteEnter、beforeRouteUpdate 和 beforeRouteLeave 守卫
在 Vue 路由中,beforeRouteEnter
、beforeRouteUpdate
和 beforeRouteLeave
是组件级别的导航守卫,它们允许你在组件内部控制路由的导航行为。这些守卫是 Vue 组件的生命周期钩子,可以在组件实例化的不同阶段被调用。
beforeRouteEnter 守卫
beforeRouteEnter
是在路由配置为当前路由,但组件实例尚未创建时调用的守卫。这意味着你不能在 beforeRouteEnter
守卫中访问组件实例的 this
,因为它还没有被创建。这个守卫通常用于执行不需要组件实例的逻辑,如验证路由的合法性。
export default { // ... beforeRouteEnter(to, from, next) { // 验证逻辑,例如检查用户权限 next(); // 放行 next(false); // 禁止访问 next({ path: '/some-path' }); // 重定向到其他路由 }, // ... };
beforeRouteUpdate 守卫
beforeRouteUpdate
是在当前路由改变,但该组件被复用时调用的守卫。例如,当用户在 /user/1
和 /user/2
之间切换时,因为这两个路由使用了相同的组件,所以 beforeRouteUpdate
会被调用。这个守卫可以用来处理组件激活时的相关逻辑,如数据预加载。
export default { // ... beforeRouteUpdate(to, from, next) { // 可以访问组件实例的 this // 例如,你可以在这里加载 to.params.id 对应的数据 next(); // 必须调用 next() 以允许导航继续 }, // ... };
beforeRouteLeave 守卫
beforeRouteLeave
是在离开该组件的对应路由时调用的守卫。这个守卫可以用来阻止用户在未保存修改的情况下离开,或者执行一些清理工作。
export default { // ... beforeRouteLeave(to, from, next) { // 可以访问组件实例的 this // 例如,提示用户保存修改 if (needToSave()) { next(new Promise((resolve, reject) => { saveData(() => { // 数据保存成功后,继续导航 resolve(); }, () => { // 用户取消保存,留在当前页面 reject(); }); })); } else { next(); // 无需保存,直接导航 } }, // ... };
在这个例子中,needToSave
是一个假设的函数,用来检查是否有需要保存的数据。saveData
是一个假设的函数,用来执行数据保存操作。beforeRouteLeave
守卫通过返回一个 Promise
来等待数据保存操作完成,确保用户不会在数据未保存的情况下离开。
总结
beforeRouteEnter
用于在组件创建之前执行逻辑。beforeRouteUpdate
用于在组件被复用时更新数据。beforeRouteLeave
用于在离开组件时执行清理或确认操作。
这些守卫为 Vue 组件提供了在路由变化时执行复杂逻辑的能力,增强了组件的响应性和交互性。使用这些守卫时,记得始终调用 next
函数来控制路由的导航流程。
实例:组件级别的权限控制
组件级别的权限控制通常涉及到检查用户是否有权访问特定的组件。以下是一个示例,展示了如何在 Vue 组件中使用 beforeRouteEnter
守卫来实现权限控制:
<template> <div> This is a protected admin page. </div> </template> <script> export default { name: 'AdminPage', beforeRouteEnter(to, from, next) { // 在渲染该组件的对应路由被确认前调用 // 不能访问组件实例 `this` checkPermission(to, from, next); }, beforeRouteUpdate(to, from, next) { // 在当前路由改变,但是该组件被复用时调用 // 可以访问组件实例 `this` checkPermission(to, from, next); }, methods: { checkPermission(to, from, next) { // 假设有一个方法来检查用户是否具有管理员权限 if (isUserAdmin()) { next(); // 用户具有权限,允许访问 } else { next({ path: '/unauthorized' }); // 用户没有权限,重定向到无权限页面 } } } }; </script>
在这个例子中,checkPermission
方法用于验证用户是否具有访问 AdminPage
的权限。如果用户是管理员,next()
被调用,允许路由继续;如果不是,用户将被重定向到一个无权限访问的页面。
实例:数据预加载和表单保护
数据预加载是在组件渲染之前加载所需数据的过程,而表单保护是防止用户在表单填写一半时离开页面而丢失数据的机制。以下是一个示例,展示了如何在 Vue 组件中使用 beforeRouteUpdate
和 beforeRouteLeave
守卫来实现这两功能:
<template> <div> <form @submit.prevent="onSubmit"> <!-- 表单输入字段 --> </form> <button @click="saveDraft">Save Draft</button> </div> </template> <script> export default { name: 'FormPage', data() { return { formData: {} }; }, beforeRouteUpdate(to, from, next) { // 在当前路由改变,但是该组件被复用时调用 // 用于数据预加载 this.fetchFormData(); next(); }, beforeRouteLeave(to, from, next) { // 在离开该组件的对应路由时调用 // 用于表单保护 if (this.isFormDirty()) { const answer = confirm('You have unsaved changes. Are you sure you want to leave?'); if (answer) { next(); // 用户确认离开,允许导航 } else { next(false); // 用户取消离开,阻止导航 } } else { next(); // 没有未保存的更改,允许导航 } }, methods: { fetchFormData() { // 从服务器获取表单数据 }, onSubmit() { // 提交表单 }, saveDraft() { // 保存表单草稿 }, isFormDirty() { // 检查表单是否有未保存的更改 } } }; </script>
在这个例子中,beforeRouteUpdate
守卫用于在用户导航到新的表单路由时预加载表单数据。beforeRouteLeave
守卫用于在用户离开表单页面时提示保存更改。如果表单有未保存的更改,用户将被提示确认是否离开,从而避免数据丢失。
六、高级路由守卫应用
路由守卫的高级用法,如滚动行为控制、路由元信息等
Vue 路由守卫除了基本的导航控制和权限验证外,还有一些高级用法,可以帮助你更细致地管理路由行为,例如控制滚动行为和利用路由元信息。
滚动行为控制
Vue Router 默认会在路由跳转后,将滚动条回到页面顶部。在某些情况下,你可能希望保持滚动位置不变,或者滚动到特定位置。你可以通过设置路由的 scrollBehavior
函数来控制滚动行为。
const router = new VueRouter({ // ... scrollBehavior(to, from, savedPosition) { if (to.hash) { // 如果路由包含锚点,则滚动到锚点 return { selector: to.hash, behavior: 'smooth' }; } else if (from.path === to.path) { // 如果是相同路由的复用(如通过按钮切换视图),保持当前滚动位置 return savedPosition; } else { // 否则,滚动到顶部 return { top: 0 }; } } });
路由元信息
路由元信息(meta fields)允许你在路由配置中添加额外的数据,这些数据可以用于守卫逻辑判断,或者在组件内部使用。
const routes = [ { path: '/user/:id', component: UserComponent, meta: { // 路由元信息 requiresAuth: true, // 需要认证 isBeta: false // 标记为 Beta 功能 } } // ...其他路由 ];
然后,你可以在守卫中使用这些元信息来进行决策:
router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requiresAuth)) { // 检查用户是否已登录 if (isAuthenticated()) { next(); } else { next({ path: '/login' }); } } else { next(); } });
在这个例子中,我们检查即将进入的路由是否有 requiresAuth
元信息。如果有,我们再检查用户是否已登录,以决定是否允许用户访问该路由。
高级用法示例
结合滚动行为控制和路由元信息,你可以创建一个高级路由守卫,它根据路由的元信息来决定是否允许访问,并控制滚动行为:
router.beforeEach((to, from, next) => { // 检查路由是否为 Beta 功能 if (to.meta.isBeta) { if (isBetaUser()) { next({ scroll: false // 不滚动到顶部 }); } else { next('/beta-redirect'); // 非 Beta 用户重定向到特定页面 } } else { next({ scroll: { top: 0 } // 默认滚动到顶部 }); } });
在这个例子中,我们首先检查目标路由是否有 isBeta
元信息。如果有,我们检查用户是否有权访问 Beta 功能。根据结果,我们决定是否允许用户访问,并设置滚动行为。
通过这些高级用法,你可以更精细地控制路由的行为,从而提供更好的用户体验。
七、结论
路由守卫(Route Guards)在 Vue 应用程序中提供了多种优势,特别是在管理用户导航和执行特定逻辑时。以下是路由守卫的一些主要优势:
- 权限控制:路由守卫允许你在用户访问特定页面或组件之前执行权限验证。这确保了只有具有适当权限的用户才能访问敏感或受保护的路由。
- 导航逻辑集中管理:通过在全局守卫中定义导航逻辑,你可以避免在多个组件中重复相同的代码。这使得逻辑集中化,便于维护和更新。
- 数据预加载:在路由切换之前,你可以使用路由守卫来预加载必要的数据。这样可以减少路由切换后的加载时间,提升用户体验。
- 用户体验优化:守卫可以用来执行如记录日志、更新页面标题、处理异常情况等操作,从而提高应用程序的整体用户体验。
- 流程控制:路由守卫可以在导航流程的不同阶段执行,如进入路由前、路由确认后等,这为开发者提供了更多的控制点来管理应用程序的流程。
- 异步操作处理:路由守卫可以处理异步操作,如等待后端验证用户的登录状态,然后根据结果决定是否允许导航。
- 条件性重定向:你可以在守卫中根据条件重定向用户到不同的路由,例如,如果用户未登录,可以重定向到登录页面。
- 组件状态管理:组件内守卫可以在组件被激活或离开时执行逻辑,如保存表单状态或执行组件级别的清理操作。
- 灵活性和可扩展性:路由守卫的 API 设计灵活,可以根据应用程序的复杂性轻松扩展,以满足不同的业务需求。
- 错误处理:在守卫中可以捕获和处理导航错误,如访问不存在的路由,从而提供更友好的错误提示和恢复策略。
总之,路由守卫是 Vue 路由系统的重要组成部分,它们为开发者提供了强大的工具来管理应用程序的路由行为,保护资源,以及优化用户导航体验。
八、附录
Vue 路由官方文档快速入口
Vue 路由(Vue Router)的官方文档可以在 Vue.js 的官方网站找到。以下是快速入口的链接:
在这个页面上,你可以找到关于 Vue 路由的所有信息,包括安装指南、基本用法、路由守卫、路由元信息、导航、链接、命名路由、重定向和别名等。官方文档还提供了各种示例和最佳实践,帮助你更好地理解和使用 Vue 路由。
以上就是一文带你掌握Vue中的路由守卫的详细内容,更多关于Vue路由守卫的资料请关注脚本之家其它相关文章!
相关文章
Vue+Video.js实现视频抽帧并返回抽帧图片Base64
这篇文章主要为大家详细介绍了Vue如何利用Video.js实现视频抽帧并返回抽帧图片Base64,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下2024-01-01ant design中upload组件上传大文件,显示进度条进度的实例
这篇文章主要介绍了ant design中upload组件上传大文件,显示进度条进度的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧2020-10-10
最新评论