路由
loyalvi Lv7

路由

Vue Router

Vue Router 的底层实现主要依赖于 Vue 的响应式系统、插件机制以及对浏览器地址变化的监听。以下是其核心实现原理和工作流程:

1. Vue Router 的初始化

当 Vue Router 被安装到 Vue 应用中时,它通过 Vue.mixin 在 Vue 实例的 beforeCreate 钩子中进行初始化:

1
2
3
4
5
6
7
8
9
Vue.mixin({
beforeCreate() {
if (this.$options.router) {
this._router = this.$options.router;
this._router.init(this);
Vue.util.defineReactive(this, '_route', this._router.history.current);
}
}
});
  • 如果 Vue 实例中包含 router 选项,则初始化路由实例,并将当前路由状态 _route 定义为响应式数据。

2. 路由模式

Vue Router 支持两种模式:

  • Hash 模式:通过监听 hashchange 事件来管理路由变化。
  • History 模式:利用 HTML5 的 history API(如 pushState 和 popState)来管理路由状态。

3. 路由匹配与渲染

Vue Router 的核心功能是根据当前 URL 匹配路由规则,并渲染对应的组件:

  • 路由匹配:当 URL 发生变化时,Vue Router 会根据配置的路由规则(routes)解析当前路径,并找到匹配的路由记录。
  • 动态渲染:通过 <router-view> 组件动态渲染匹配的组件。<router-view> 会根据当前路由状态 _route 渲染对应的组件。

4. 监听地址变化

Vue Router 通过监听浏览器地址的变化来触发路由更新:

  • 在 Hash 模式下,监听 hashchange 事件。
  • 在 History 模式下,监听 popState 事件。

5. 核心组件

  • <router-link>:用于创建导航链接,类似于 <a> 标签,但不会触发页面刷新。
  • <router-view>:根据当前路由状态动态渲染匹配的组件。

6. 导航守卫

Vue Router 提供了导航守卫(如 beforeEach、afterEach),用于在路由导航发生时进行访问控制,例如登录验证。

7. 自定义实现

在自定义实现中,Vue Router 的核心逻辑包括:

  • 创建路由映射:将路由配置(routes)解析为路由映射表。
  • 监听地址变化:通过绑定事件监听地址变化,并触发路由更新。
  • 动态渲染组件:根据当前路由状态动态渲染对应的组件。

总结

Vue Router 的底层实现主要通过以下步骤完成:

  1. 在 Vue 实例中初始化路由系统。
  2. 根据路由模式监听地址变化。
  3. 解析当前路径,匹配路由规则。
  4. 动态渲染对应的组件。
  5. 提供导航守卫,控制路由导航。
    这种机制使得 Vue Router 能够高效地管理路由状态,并与 Vue 的响应式系统无缝集成。

Hash 路由和 History API

在单页面应用(SPA)中,hash 路由和 History API 是两种不同的前端路由实现方式,它们各有优缺点和适用场景。以下是它们之间的主要区别:

URL 结构

  • Hash 路由:
    • 使用井号 # 来分隔 URL 的路径部分,例如 http://example.com/#/path/to/resource。
    • URL 中的 # 后面的部分称为 hash fragment,浏览器不会将其发送到服务器,服务器无法感知这部分内容。
  • History API:
    • 使用标准的 URL 路径,不包含井号,例如 http://example.com/path/to/resource。
    • URL 的路径部分会被发送到服务器,服务器需要配置相应的路由处理逻辑。

浏览器行为

  • Hash 路由:
    • 当 URL 的 hash 部分发生变化时,浏览器不会重新加载页面,而是触发 hashchange 事件。
    • 前端应用可以监听 hashchange 事件,根据 hash 的值动态加载不同的内容或视图。
  • History API:
    • 使用 history.pushState() 和 history.replaceState() 方法可以在不重新加载页面的情况下修改 URL 的路径部分。
    • 前端应用需要手动监听 popstate 事件来处理浏览器的前进和后退操作。

SEO 友好性

  • Hash 路由:
    • 对 SEO 不太友好,因为服务器无法感知到 hash 路由的路径,搜索引擎爬虫在抓取页面时可能无法正确解析和索引 hash 路由的内容。
  • History API:
    • 对 SEO 更友好,因为服务器可以感知到 URL 的路径部分,搜索引擎爬虫可以抓取和索引这些路径对应的页面内容。

服务器配置

  • Hash 路由:
    • 服务器不需要为 hash 路由的路径提供具体的路由处理逻辑,只需将所有请求重定向到应用的入口页面(如 index.html)。
  • History API:
    • 服务器需要配置相应的路由处理逻辑,确保当请求 URL 的路径部分时,能够返回正确的页面内容或重定向到应用的入口页面。

兼容性

  • Hash 路由:
    • 兼容性较好,几乎所有现代浏览器都支持 hash 的使用和 hashchange 事件的监听。
  • History API:
    • 兼容性相对较差,一些较老的浏览器可能不支持 History API。

使用场景

  • Hash 路由:
    • 适用于对 SEO 要求不高的应用,或者在服务器配置较为复杂的情况下使用。
  • History API:
    • 适用于对 SEO 要求较高的应用,或者需要更自然的 URL 结构的场景。
      总的来说,选择使用 hash 路由还是 History API 取决于具体的应用需求、SEO 要求以及服务器配置等因素。在现代 Web 开发中,随着服务器端渲染(SSR)和静态站点生成(SSG)等技术的发展,History API 的使用越来越普遍,因为它能够提供更自然的 URL 结构和更好的 SEO 支持。

前端路由和后端路由的区别

1. 定义和作用

  • 后端路由:
    • 定义:后端路由是指服务器端处理 HTTP 请求的路径映射。当客户端(如浏览器)发送一个 HTTP 请求到服务器时,后端路由根据请求的 URL 和方法(GET、POST、PUT、DELETE 等)将请求分发到相应的处理函数或控制器。
    • 作用:主要负责处理业务逻辑,如数据库操作、文件处理、业务逻辑计算等,并返回相应的响应数据(如 HTML 页面、JSON 数据等)。
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      // 使用 Express 框架的后端路由示例
      const express = require('express');
      const app = express();
      app.get('/users', (req, res) => {
      // 查询数据库获取用户列表
      res.json({ users: ['User1', 'User2', 'User3'] });
      });
      app.post('/users', (req, res) => {
      // 创建新用户
      res.status(201).json({ message: 'User created' });
      });
      app.listen(3000, () => {
      console.log('Server is running on port 3000');
      });
  • 前端路由:
    • 定义:前端路由是指在客户端(如浏览器)中处理页面导航的路径映射。通过 JavaScript 操控浏览器的 History API(如 pushState、replaceState)或 hash 值的变化,实现页面的无刷新切换。
    • 作用:主要负责页面的导航和视图切换,不涉及后端的业务逻辑处理。前端路由通常用于单页面应用(SPA),通过动态加载组件或模块来更新页面内容。
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      // 使用 Vue Router 的前端路由示例
      import Vue from 'vue';
      import VueRouter from 'vue-router';
      import Home from './components/Home.vue';
      import About from './components/About.vue';
      import Users from './components/Users.vue';
      Vue.use(VueRouter);
      const routes = [
      { path: '/', component: Home },
      { path: '/about', component: About },
      { path: '/users', component: Users }
      ];
      const router = new VueRouter({
      mode: 'history', // 使用 history 模式
      routes
      });
      new Vue({
      router,
      render: h => h(App)
      }).$mount('#app');

2. 工作原理

  • 后端路由:
    • 工作原理:当客户端发送 HTTP 请求到服务器时,后端路由中间件(如 Express 的 app.use 或 app.get)根据请求的 URL 和方法匹配到相应的路由处理函数。该处理函数执行业务逻辑,并返回响应数据。
    • 示例:
      1
      2
      3
      4
      app.get('/api/users', (req, res) => {
      // 处理 GET 请求,返回用户列表
      res.json({ users: ['User1', 'User2', 'User3'] });
      });
  • 前端路由:
    • 工作原理:前端路由通过监听浏览器的 URL 变化(如 hash 值变化或 History API 的状态变化),动态加载相应的组件或模块,更新页面内容。前端路由不涉及服务器的请求和响应,整个过程在客户端完成。
    • 示例:
      1
      2
      // 使用 Vue Router 监听 URL 变化
      router.push('/about'); // 导航到 /about 路径

3. 性能和用户体验

  • 后端路由:
    • 性能:每次页面导航都会发送一个新的 HTTP 请求到服务器,服务器处理请求并返回新的 HTML 页面或数据。这会导致页面的重新加载,性能相对较低。
    • 用户体验:用户在每次导航时会看到页面的重新加载,可能会有短暂的白屏或闪烁,用户体验较差。
  • 前端路由:
    • 性能:页面导航时不会发送新的 HTTP 请求,而是通过 JavaScript 动态更新页面内容,性能较高。
    • 用户体验:页面导航时不会重新加载,用户可以看到平滑的过渡效果,用户体验较好。

4. 适用场景

  • 后端路由:
    • 多页面应用(MPA):适用于传统的多页面应用,每个页面都是一个独立的 HTML 文件,每次导航都会重新加载页面。
    • 服务器端渲染(SSR):适用于需要服务器端渲染的应用,如搜索引擎优化(SEO)要求较高的应用。
  • 前端路由:
    • 单页面应用(SPA):适用于单页面应用,整个应用只有一个 HTML 文件,通过前端路由动态加载不同的组件或模块,实现页面的无刷新切换。
    • 动态内容:适用于需要动态加载内容的应用,如在线编辑器、仪表盘等。

总结

  • 后端路由:主要负责处理业务逻辑,每次导航都会发送新的 HTTP 请求,性能相对较低,用户体验较差。适用于多页面应用和服务器端渲染。
  • 前端路由:主要负责页面导航和视图切换,通过 JavaScript 动态更新页面内容,性能较高,用户体验较好。适用于单页面应用和动态内容加载。
    选择使用后端路由还是前端路由取决于具体的应用需求和项目类型。对于传统的多页面应用和需要服务器端渲染的应用,后端路由是更好的选择;对于单页面应用和需要动态内容加载的应用,前端路由则更加合适。

路由守卫

使用路由守卫进行权限控制

在 Vue. js 中,路由守卫是实现权限控制的强大工具。通过路由守卫,可以在用户导航到不同页面时执行逻辑判断,从而限制访问某些页面或功能。以下是详细的步骤和示例:

1. 定义路由并添加 meta 属性

首先,在路由配置中为需要权限控制的路由添加 meta 属性,用于存储角色或权限信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// router/index.js
const routes = [
{
path: '/login',
name: 'Login',
component: () => import('@/views/Login.vue'),
meta: { auth: false } // 登录页面不需要认证
},
{
path: '/dashboard',
name: 'Dashboard',
component: () => import('@/views/Dashboard.vue'),
meta: { auth: true, roles: ['admin', 'user'], permissions: ['dashboard:view'] } // 需要认证,仅管理员和用户可以访问
},
{
path: '/admin',
name: 'Admin',
component: () => import('@/views/Admin.vue'),
meta: { auth: true, roles: ['admin'] } // 仅管理员可以访问
}
];

2. 全局前置守卫 beforeEach

使用全局前置守卫 beforeEach 来检查用户的权限。在用户尝试访问某个路由时,根据 meta 属性中的角色和权限信息进行判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// router/index.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import store from '@/store'; // 假设你使用了 Vuex 来管理状态
Vue.use(VueRouter);
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
});
router.beforeEach((to, from, next) => {
const user = store.state.user; // 从 Vuex 中获取用户信息
const { auth, roles, permissions } = to.meta;
// 检查是否需要认证
if (auth) {
// 检查用户是否登录
if (!user.token) {
next({ path: '/login', query: { redirect: to.fullPath } }); // 未登录,跳转到登录页
return;
}
// 检查角色权限
if (roles && !roles.includes(user.role)) {
next({ path: '/403' }); // 角色不匹配,跳转到403页面
return;
}
// 检查功能权限
if (permissions && !permissions.some(permission => user.permissions.includes(permission))) {
next({ path: '/403' }); // 权限不匹配,跳转到403页面
return;
}
}
next(); // 通过检查,继续导航
});
export default router;

3. 组件内守卫

在某些情况下,你可能需要在组件内部进行更细粒度的权限控制。可以使用组件内的守卫 beforeRouteEnter、beforeRouteUpdate 和 beforeRouteLeave:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/views/Dashboard.vue
<script>
export default {
beforeRouteEnter(to, from, next) {
const user = store.state.user; // 从 Vuex 中获取用户信息
const { permissions } = to.meta;
if (permissions && !permissions.some(permission => user.permissions.includes(permission))) {
next({ path: '/403' }); // 权限不匹配,跳转到403页面
} else {
next(); // 通过检查,继续导航
}
}
};
</script>

4. 使用 Vuex 管理用户状态

为了更好地管理用户状态,建议使用 Vuex。以下是一个简单的 Vuex 状态管理示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
user: {
token: localStorage.getItem('token') || null,
role: localStorage.getItem('role') || null,
permissions: JSON.parse(localStorage.getItem('permissions') || '[]')
}
},
mutations: {
SET_USER(state, user) {
state.user = user;
localStorage.setItem('token', user.token);
localStorage.setItem('role', user.role);
localStorage.setItem('permissions', JSON.stringify(user.permissions));
}
},
actions: {
login({ commit }, user) {
commit('SET_USER', user);
},
logout({ commit }) {
commit('SET_USER', { token: null, role: null, permissions: [] });
localStorage.removeItem('token');
localStorage.removeItem('role');
localStorage.removeItem('permissions');
}
}
});

5. 权限更新处理

在用户权限发生变化时,需要更新路由和菜单。可以使用 Vuex 的 actions 来处理权限更新:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// store/permission.ts
export const usePermissionStore = defineStore('permission', {
state: () => ({
routes: [] as RouteRecordRaw[],
menus: [] as MenuItem[]
}),
actions: {
async refreshPermissions() {
// 获取最新权限
const newPermissions = await fetchUserPermissions();
// 更新用户状态
const userStore = useUserStore();
userStore.updatePermissions(newPermissions);
// 重新生成路由
const router = useRouter();
const permissionCtrl = new PermissionController(userStore.state);
const routeBuilder = new DynamicRouteBuilder(router, permissionCtrl);
// 清空现有路由
this.routes.forEach(route => {
router.removeRoute(route.name as string);
});
// 添加新路由
const asyncRoutes = await fetchUserRoutes();
this.routes = await routeBuilder.addRoutes(asyncRoutes);
// 重新生成菜单
const menuGenerator = new MenuGenerator(permissionCtrl);
this.menus = menuGenerator.generateMenu(menuConfig);
}
}
});

通过以上步骤,你可以有效地使用 Vue Router 的路由守卫来实现权限控制,确保只有具备相应权限的用户才能访问特定的路由。

route和router的区别

在 Vue Router 中,route 和 router 是两个核心但易混淆的概念。它们的区别如下:

1. 定义与作用

route router
当前激活的路由信息对象 Vue Router 的实例(路由管理器)
表示当前页面对应的路由状态(路径、参数、查询等) 负责全局路由控制(跳转、守卫、模式配置等)
只读属性,不可直接修改 提供方法(如 push, replace)操作路由

2. 功能对比

route router
提供当前路由的详细信息:
path, params, query, hash, meta 等
提供路由的操作方法:
push(), replace(), go(), back() 等
被动获取路由状态 主动触发路由跳转或控制流程
例如:
/user/123?name=John → route.params.id = '123'
例如:
router.push('/user/456') 跳转到新路由

3. 在组件中的访问方式

Options API (Vue 2/3)

1
2
3
4
// 访问当前路由信息(route)
this.$route.params.id
// 访问路由实例(router)
this.$router.push('/home')

Composition API (Vue 3)

1
2
3
4
5
6
7
import { useRoute, useRouter } from 'vue-router';
const route = useRoute(); // 相当于 this.$route
const router = useRouter(); // 相当于 this.$router
// 获取参数
console.log(route.params.id);
// 跳转路由
router.push('/home');

4. 典型使用场景

route 的用途

  • 获取 URL 参数:route.params.id
  • 读取查询字符串:route.query.search
  • 访问路由元信息:route.meta.requiresAuth
  • 监听路由变化:
    1
    2
    3
    watch(() => route.path, (newPath) => {
    console.log('路径变化了:', newPath);
    });

router 的用途

  • 编程式导航:
    1
    2
    3
    router.push('/login');      // 跳转
    router.replace('/profile'); // 替换当前历史记录
    router.go(-1); // 返回上一页
  • 全局路由守卫:
    1
    2
    3
    router.beforeEach((to, from) => {
    // 权限控制逻辑
    });
  • 动态添加路由:
    1
    router.addRoute({ path: '/admin', component: Admin });

5. 类比理解

类比对象 route router
导航系统 当前车辆的位置、速度、方向 导航仪(规划路线、切换路径)
HTTP 请求 请求的 URL 和参数(如 /api/user?id=1) 发送请求的客户端(如 axios)

6. 常见错误

  • 误用 router.params:
    ❌ router.params.id → 不存在,应使用 route.params.id。
  • 混淆声明式与编程式导航:
    ❌ <router-link :to="router.push(...)"> → 应直接传 to 属性。

总结

  • route = 当前路由的“数据”(只读,反映当前状态)
  • router = 路由的“控制器”(可操作,改变路由状态)
    掌握两者的区别,是高效使用 Vue Router 的关键!
由 Hexo 驱动 & 主题 Keep
访客数 访问量