微前端不同系统之前如何共享状态?
微前端架构中,不同子系统(应用)通常是独立开发、部署的,但在实际场景中常需要共享状态(如用户信息、全局配置、权限数据等)。由于子系统可能使用不同的技术栈(Vue、React、Angular 等),共享状态需要兼顾跨框架兼容性、安全性和低耦合性。以下是常用的实现方案及原理:
一、基于全局变量(Window)共享
这是最简单直接的方案,通过浏览器的全局对象 window 存储共享状态,所有子应用都可访问。
实现方式:
-
主应用定义全局状态:在主应用中初始化共享状态,并挂载到
window上。javascript
运行
// 主应用 window.sharedState = { userInfo: { id: 1, name: '张三' }, theme: 'light', setUserInfo: (info) => { window.sharedState.userInfo = info; // 触发全局事件通知状态变化 window.dispatchEvent(new CustomEvent('userInfoChange', { detail: info })); } }; -
子应用读取 / 修改状态:子应用通过
window.sharedState访问状态,修改时可触发全局事件通知其他应用。javascript
运行
// Vue 子应用 export default { mounted() { // 读取全局状态 this.userInfo = window.sharedState.userInfo; // 监听状态变化 window.addEventListener('userInfoChange', (e) => { this.userInfo = e.detail; }); }, methods: { updateUser() { // 修改全局状态 window.sharedState.setUserInfo({ id: 1, name: '李四' }); } } };
优缺点:
-
优点:实现简单,跨框架兼容(任何技术栈都能访问
window)。 -
缺点:
- 全局变量污染,可能存在命名冲突。
- 状态修改缺乏管控,容易引发不可预期的副作用。
- 不支持状态变更的细粒度监听(需手动实现事件机制)。
适用场景:
简单场景(如共享用户信息、基础配置),子应用数量少且信任度高。
二、基于自定义事件(EventBus)通信
通过浏览器的 CustomEvent 机制实现状态共享,主应用作为事件中枢,子应用通过 “发布 - 订阅” 模式传递状态。
实现方式:
-
主应用提供事件总线:封装事件监听和触发方法,避免直接操作
window。javascript
运行
// 主应用:event-bus.js const eventBus = { on: (name, callback) => window.addEventListener(name, callback), off: (name, callback) => window.removeEventListener(name, callback), emit: (name, data) => window.dispatchEvent(new CustomEvent(name, { detail: data })) }; window.eventBus = eventBus; -
子应用通过事件共享状态:
-
子应用 A 发布状态变更事件:
javascript
运行
// 子应用 A(React) function updateTheme(theme) { window.eventBus.emit('themeChange', theme); // 发布事件 } -
子应用 B 订阅事件获取状态:
javascript
运行
// 子应用 B(Vue) mounted() { window.eventBus.on('themeChange', (e) => { this.theme = e.detail; // 订阅事件,更新本地状态 }); }, beforeDestroy() { window.eventBus.off('themeChange'); // 解绑事件,避免内存泄漏 }
-
优缺点:
-
优点:解耦性好(子应用无需知道彼此存在),跨框架兼容。
-
缺点:
- 状态是 “一次性传递”,新挂载的子应用无法获取历史状态(需额外处理初始化)。
- 复杂状态管理困难(如状态依赖、回溯)。
适用场景:
简单的跨应用通信(如主题切换、通知提示),状态变更频率低。
三、基于全局状态管理库(跨框架)
使用支持跨框架的状态管理库(如 Redux、Pinia 或自定义 Store),将状态抽离到独立的库中,主应用和子应用共同访问。
实现方式:
-
封装全局 Store:将状态管理逻辑封装为独立的
umd模块(如global-store.js),暴露getState、dispatch等方法。javascript
运行
// global-store.js(基于 Redux 简化) let state = { user: null, theme: 'light' }; const listeners = []; export const globalStore = { getState: () => ({ ...state }), // 返回状态副本,避免直接修改 dispatch: (action) => { switch (action.type) { case 'SET_USER': state.user = action.payload; break; case 'SET_THEME': state.theme = action.payload; break; } // 通知所有监听者 listeners.forEach(listener => listener(state)); }, subscribe: (listener) => { listeners.push(listener); return () => { const index = listeners.indexOf(listener); listeners.splice(index, 1); }; // 返回解绑函数 } }; -
主应用初始化 Store:
javascript
运行
// 主应用 import { globalStore } from './global-store'; window.globalStore = globalStore; // 初始化状态 globalStore.dispatch({ type: 'SET_USER', payload: { id: 1, name: '张三' } }); -
子应用使用 Store:
javascript
运行
// Vue 子应用 export default { data() { return { user: null }; }, mounted() { // 初始化状态 this.user = window.globalStore.getState().user; // 订阅状态变化 this.unsubscribe = window.globalStore.subscribe((state) => { this.user = state.user; }); }, beforeDestroy() { this.unsubscribe(); // 解绑订阅 }, methods: { updateUser() { // 修改全局状态 window.globalStore.dispatch({ type: 'SET_USER', payload: { id: 1, name: '李四' } }); } } };
优缺点:
-
优点:
- 状态管理规范(有明确的修改方式
dispatch),避免混乱。 - 支持状态变更监听,新挂载的子应用可获取最新状态。
- 可集成中间件(如日志、持久化)。
- 状态管理规范(有明确的修改方式
-
缺点:
- 需要额外引入状态管理库,增加复杂度。
- 需保证子应用使用相同的状态库 API。
适用场景:
中大型微前端应用,需要共享复杂状态(如用户权限、多应用共享数据)。
四、基于本地存储(localStorage/sessionStorage)
利用浏览器的本地存储(localStorage 持久化,sessionStorage 会话级)共享状态,通过监听存储事件感知变化。
实现方式:
-
写入状态到本地存储:
javascript
运行
// 主应用或子应用 function setGlobalUser(user) { localStorage.setItem('globalUser', JSON.stringify(user)); } -
读取状态并监听变化:
javascript
运行
// 其他子应用 export default { data() { return { user: null }; }, mounted() { // 初始化读取 this.user = JSON.parse(localStorage.getItem('globalUser')); // 监听存储变化 window.addEventListener('storage', (e) => { if (e.key === 'globalUser') { this.user = JSON.parse(e.newValue); } }); } };
优缺点:
-
优点:持久化存储(
localStorage),页面刷新后状态不丢失;跨框架兼容。 -
缺点:
- 存储容量有限(通常 5MB),不适合大量数据。
- 仅支持字符串类型,需手动序列化 / 反序列化(
JSON.stringify/parse)。 storage事件有延迟,且同一页面内修改不会触发(需额外处理)。
适用场景:
需要持久化的全局状态(如用户登录状态、主题设置)。
五、基于微前端框架的内置能力
主流微前端框架(如 qiankun、single-spa)提供了内置的状态共享方案,简化跨应用通信。
以 qiankun 为例:
qiankun 提供了 initGlobalState 方法创建全局状态池,支持主应用与子应用、子应用之间的状态共享。
-
主应用初始化全局状态:
javascript
运行
// 主应用 import { initGlobalState } from 'qiankun'; // 初始化状态 const initialState = { user: { id: 1, name: '张三' } }; const globalState = initGlobalState(initialState); // 监听状态变化 globalState.onGlobalStateChange((newState, prev) => { console.log('状态变化:', newState, prev); }); // 修改状态 globalState.setGlobalState({ user: { id: 1, name: '李四' } }); -
子应用接入全局状态:
javascript
运行
// Vue 子应用(在入口文件中) export async function mount(props) { // 接收全局状态 const { onGlobalStateChange, setGlobalState } = props; // 监听状态变化 onGlobalStateChange((newState) => { console.log('子应用收到状态:', newState); // 更新 Vuex/Pinia 状态 store.dispatch('setUser', newState.user); }, true); // 第二个参数为 true 表示立即执行一次 // 子应用修改全局状态 setGlobalState({ theme: 'dark' }); }
优缺点:
-
优点:
- 由微前端框架原生支持,适配框架的生命周期(如子应用挂载 / 卸载时自动清理)。
- 避免全局变量污染,状态隔离性更好。
-
缺点:
- 依赖特定框架(如 qiankun),迁移成本高。
适用场景:
使用 qiankun 等成熟微前端框架的项目。
六、方案选择建议
- 简单场景:优先用 全局变量 + 自定义事件,实现快、成本低。
- 复杂状态:使用 跨框架状态管理库(如封装 Redux)或 微前端框架内置能力(如 qiankun),保证状态可控。
- 持久化需求:结合 localStorage 存储,配合事件监听同步状态。
- 跨技术栈:避免依赖特定框架的状态库(如 Vuex 仅支持 Vue),优先用原生 JS 实现的方案。
总结
微前端状态共享的核心是在独立部署的应用间建立可靠的通信渠道,关键考虑因素:跨框架兼容性、状态可控性、性能和易用性。实际项目中,常结合多种方案(如全局 Store + 本地存储持久化)满足不同场景需求。
websocket和http的区别?有哪些特性?
这个问题抓得很关键,WebSocket 和 HTTP 虽同属应用层协议,但核心设计目标和通信模式完全不同。核心结论:HTTP 是单向、无状态的请求 - 响应协议,WebSocket 是双向、持久化的全双工通信协议,二者特性和适用场景差异显著。
一、核心区别(核心差异点)
- 通信模式
- HTTP:请求 - 响应模式,客户端主动发起请求,服务器被动响应,响应后连接断开(短连接)。
- WebSocket:握手后建立持久连接,客户端和服务器可双向主动发送数据(全双工),连接持续存在直到主动关闭。
- 连接状态
- HTTP:无状态,每次请求需重新携带身份信息(如 Cookie、Token),服务器不保存连接上下文。
- WebSocket:有状态,连接建立后保持会话,服务器可识别同一客户端的连续通信。
- 连接建立
- HTTP:基于 TCP 三次握手,直接建立连接,使用
http://或https://协议头。 - WebSocket:先通过 HTTP 协议发起握手请求(携带
Upgrade: websocket头),服务器响应后切换为 WebSocket 协议,使用ws://或wss://协议头。
- 数据传输效率
- HTTP:每次请求 / 响应需携带完整头部(如 Cookie、请求头),冗余数据多,适合短周期数据交互。
- WebSocket:握手后传输仅含少量帧头部,数据 payload 占比高,且无需重复建立连接,适合高频、实时数据传输。
二、各自核心特性
1. HTTP 特性
- 单向通信:仅客户端可发起请求,服务器无法主动推送数据。
- 无状态:服务器不记录请求间的关联,需通过 Cookie、Session 等机制维持状态。
- 短连接:默认响应后关闭连接,可通过
Keep-Alive实现长连接(本质是复用连接,仍为请求 - 响应模式)。 - 兼容性强:所有浏览器、服务器原生支持,应用场景广泛。
- 支持缓存:可通过
Cache-Control、ETag等头信息实现数据缓存,减少重复请求。
2. WebSocket 特性
- 全双工通信:客户端和服务器可同时发送数据,实时性强。
- 持久连接:连接建立后持续存在,无需频繁握手,降低延迟。
- 二进制 + 文本支持:可传输文本(UTF-8)或二进制数据(如文件、视频流),帧结构灵活。
- 心跳机制:通过
ping/pong帧维持连接,检测对方在线状态,避免连接被防火墙断开。 - 跨域支持:握手阶段可通过 CORS 机制实现跨域通信,无需额外配置。
三、适用场景对比
- HTTP 适用场景:普通网页请求、接口调用、文件下载、表单提交等非实时场景(如电商商品查询、登录验证)。
- WebSocket 适用场景:实时聊天、实时数据推送(如股票行情、监控数据)、多人协作工具(如在线文档)、游戏实时交互等需要低延迟双向通信的场景。
总结
HTTP 是 “请求 - 响应” 的短连接协议,侧重简单、通用的一次性数据交互;WebSocket 是 “双向持久” 的全双工协议,侧重实时、高频的双向数据传输。二者并非替代关系,而是互补 —— 很多项目中会同时使用(如通过 HTTP 完成登录,再通过 WebSocket 接收实时通知)。
| 对比维度 | HTTP | WebSocket |
|---|---|---|
| 核心通信模式 | 客户端主动请求 → 服务器被动响应(单向请求 - 响应) | 握手后双向主动发送数据(全双工通信) |
| 连接状态 | 无状态,不保存连接上下文 | 有状态,维持会话关联 |
| 连接类型 | 短连接(默认响应后关闭),可通过 Keep-Alive 复用连接 | 持久连接(建立后持续存在,直到主动关闭) |
| 连接建立方式 | 基于 TCP 三次握手,直接建立连接 | 先通过 HTTP 握手(携带 Upgrade: websocket 头),再切换协议 |
| 协议标识 | http://(明文)、https://(加密) | ws://(明文)、wss://(加密) |
| 数据传输效率 | 每次交互携带完整请求 / 响应头,冗余数据多 | 仅帧头部少量开销,payload 占比高,无重复握手损耗 |
| 服务器主动推送 | 不支持,需通过轮询、长轮询模拟 | 原生支持,服务器可主动向客户端推送数据 |
| 数据传输格式 | 文本(如 JSON、HTML)、二进制(文件等) | 文本(UTF-8)、二进制数据(帧结构支持) |
| 状态维持方式 | 依赖 Cookie、Session、Token 等外部机制 | 连接本身维持状态,无需额外携带身份信息 |
| 缓存支持 | 原生支持(Cache-Control、ETag 等) | 不支持缓存 |
| 跨域支持 | 依赖 CORS 机制 | 握手阶段通过 CORS 实现,原生支持跨域 |
| 心跳机制 | 无原生支持,需手动实现 | 原生支持 ping/pong 帧,维持连接活性 |
| 适用场景 | 普通接口调用、网页请求、文件下载、表单提交等非实时场景 | 实时聊天、股票行情、监控数据推送、多人协作、游戏交互等实时场景 |
| 兼容性 | 所有浏览器、服务器原生支持,兼容性极强 | 现代浏览器(IE10+)、主流服务器均支持 |
vue如何实现动态路由
Vue 中的动态路由指的是根据后端数据(如用户权限、后端返回的路由配置)动态生成路由规则,实现不同用户看到不同页面的需求(如管理员和普通用户的路由权限不同)。动态路由的核心是通过 Vue Router 的 API 动态添加路由规则,而非在初始化时写死。
一、动态路由的实现步骤
1. 初始化基础路由
首先定义无需权限的基础路由(如登录页、404 页),作为路由的初始配置。
javascript
运行
// router/index.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import Login from '../views/Login.vue';
import NotFound from '../views/NotFound.vue';
Vue.use(VueRouter);
// 基础路由(无需权限)
const baseRoutes = [
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '*',
name: 'NotFound',
component: NotFound
}
];
const router = new VueRouter({
routes: baseRoutes
});
export default router;
2. 从后端获取动态路由配置
用户登录后,通过接口获取该用户对应的路由权限配置(通常是一个路由规则数组)。后端返回的格式一般包含 path、name、component、children 等字段,例如:
json
// 后端返回的动态路由示例
[
{
"path": "/dashboard",
"name": "Dashboard",
"meta": { "title": "仪表盘", "requiresAuth": true },
"component": "Dashboard", // 组件名(前端需映射到实际组件)
"children": [
{
"path": "analysis",
"name": "Analysis",
"component": "Analysis",
"meta": { "title": "数据分析" }
}
]
},
{
"path": "/user",
"name": "User",
"meta": { "title": "用户管理", "requiresAuth": true },
"component": "User"
}
]
3. 动态路由映射(组件路径转换)
后端返回的 component 通常是字符串(如 "Dashboard"),需要转换为前端实际的组件路径(如 () => import('../views/Dashboard.vue'))。因此,需创建一个 “组件映射表”:
javascript
运行
// router/componentMap.js
// 映射组件名到实际组件
export const componentMap = {
Dashboard: () => import('../views/Dashboard.vue'),
Analysis: () => import('../views/Analysis.vue'),
User: () => import('../views/User.vue')
// 其他组件...
};
然后编写一个递归函数,将后端返回的路由配置转换为 Vue Router 可识别的格式:
javascript
运行
// router/utils.js
import { componentMap } from './componentMap';
// 递归转换路由配置
export function convertRoutes(routes) {
return routes.map(route => {
const newRoute = {
path: route.path,
name: route.name,
meta: route.meta || {},
// 转换 component 为实际组件
component: componentMap[route.component]
};
// 处理子路由(递归)
if (route.children && route.children.length > 0) {
newRoute.children = convertRoutes(route.children);
}
return newRoute;
});
}
4. 动态添加路由
在用户登录成功后,获取后端路由配置,转换后通过 Vue Router 的 addRoutes(Vue Router 3)或 addRoute(Vue Router 4)方法动态添加路由。
Vue Router 3 示例:
javascript
运行
// store/index.js(Vuex 中处理登录逻辑)
import router from '../router';
import { convertRoutes } from '../router/utils';
import api from '../api';
const store = new Vuex.Store({
state: {
userRoutes: [] // 存储动态路由
},
mutations: {
setUserRoutes(state, routes) {
state.userRoutes = routes;
}
},
actions: {
async login({ commit }, { username, password }) {
// 1. 登录获取 token
const { token } = await api.login(username, password);
localStorage.setItem('token', token);
// 2. 获取用户路由配置
const userRouteConfig = await api.getUserRoutes();
// 3. 转换路由配置
const dynamicRoutes = convertRoutes(userRouteConfig);
// 4. 动态添加路由(通常添加到一个公共的父路由下,如 /layout)
// 假设存在一个布局路由,动态路由作为其子路由
dynamicRoutes.forEach(route => {
router.addRoute('Layout', route); // 'Layout' 是父路由的 name
});
// 5. 存储路由到 Vuex,用于渲染侧边栏
commit('setUserRoutes', dynamicRoutes);
// 6. 跳转到首页
router.push('/dashboard');
}
}
});
注意:Vue Router 3 中
addRoutes可直接添加数组,但更推荐用addRoute逐个添加(支持嵌套路由)。
Vue Router 4 示例(Vue 3):
Vue Router 4 中 addRoutes 已废弃,需用 addRoute 逐个添加,且支持链式调用:
javascript
运行
// 动态添加路由(Vue Router 4)
const dynamicRoutes = convertRoutes(userRouteConfig);
dynamicRoutes.forEach(route => {
router.addRoute(route); // 直接添加顶级路由
// 或添加到父路由:router.addRoute('Layout', route)
});
5. 处理路由刷新丢失问题
动态添加的路由在页面刷新后会丢失(因为 addRoute 是运行时动态添加的,刷新后初始化代码会重新执行,只加载基础路由)。解决方案:
- 在
router.beforeEach导航守卫中,判断用户是否已登录且动态路由未加载,若未加载则重新获取并添加:
javascript
运行
// router/index.js
import store from '../store';
import { convertRoutes } from './utils';
import api from '../api';
router.beforeEach(async (to, from, next) => {
const token = localStorage.getItem('token');
const isLogin = !!token;
// 未登录且访问非登录页,跳转到登录页
if (!isLogin && to.name !== 'Login') {
return next('/login');
}
// 已登录且动态路由未加载,重新添加路由
if (isLogin && store.state.userRoutes.length === 0) {
try {
const userRouteConfig = await api.getUserRoutes();
const dynamicRoutes = convertRoutes(userRouteConfig);
// 动态添加路由
dynamicRoutes.forEach(route => {
router.addRoute('Layout', route);
});
// 存储到 Vuex
store.commit('setUserRoutes', dynamicRoutes);
// 重新导航到目标路由(避免因路由未加载导致的 404)
return next({ ...to, replace: true });
} catch (error) {
// 获取路由失败,可能是 token 过期,退出登录
localStorage.removeItem('token');
return next('/login');
}
}
next();
});
二、动态路由的核心原理
动态路由的本质是通过 Vue Router 提供的 API 动态修改路由表,核心依赖:
addRoute(Vue Router 3/4):向路由表中添加新的路由规则(支持嵌套路由,需指定父路由name)。- 导航守卫(
beforeEach):在路由跳转前检查动态路由是否已加载,确保刷新后重新添加。 - 组件映射:将后端返回的组件名转换为前端实际的组件路径(通过
import动态导入)。
三、注意事项
-
404 路由的位置:404 路由(
path: '*')必须放在最后加载,否则会拦截动态添加的路由。动态路由添加后,404 路由会正确匹配未定义的路径。 -
路由权限的细粒度控制:除了动态添加路由,还需在导航守卫中检查用户是否有权限访问某个路由(结合
meta中的权限标识):javascript
运行
router.beforeEach((to, from, next) => { if (to.meta.requiresAuth && !hasPermission(to.name)) { return next('/403'); // 无权限跳转到 403 页 } next(); }); -
Vue Router 版本差异:
- Vue Router 3 用
addRoutes(routes)批量添加,addRoute(route)单个添加。 - Vue Router 4 仅支持
addRoute(route)或addRoute(parentName, route),且添加后需通过next({ ...to, replace: true })重新导航。
- Vue Router 3 用
-
侧边栏渲染:动态路由通常需要在侧边栏展示,可直接使用 Vuex 中存储的
userRoutes遍历渲染(配合v-for和路由元信息meta.title)。
总结
Vue 动态路由的实现流程可概括为:初始化基础路由 → 登录后获取后端路由配置 → 转换为前端可识别的路由格式 → 动态添加到路由表 → 处理刷新丢失问题。
核心依赖 Vue Router 的 addRoute 方法和导航守卫,通过后端返回的权限配置动态生成路由,实现不同用户的路由权限隔离。
vue的路由和页面内容是如何切换的
Vue 中路由与页面内容的切换,本质是通过 Vue Router 管理 URL 与组件的映射关系,并在 URL 变化时动态渲染对应的组件。整个过程由路由实例、路由匹配、组件渲染三个核心部分协同完成,具体流程如下:
一、核心原理:URL 与组件的映射
Vue Router 的核心是建立 URL 路径与组件的对应关系(路由规则),当 URL 变化时,Vue Router 会找到匹配的路由规则,渲染对应的组件到页面指定位置(通常是 <router-view> 标签)。
1. 路由规则的定义
首先在路由配置中定义 path(URL 路径)与 component(组件)的映射,例如:
javascript
运行
// router/index.js
import VueRouter from 'vue-router';
import Home from '../views/Home.vue';
import About from '../views/About.vue';
const routes = [
{ path: '/home', component: Home },
{ path: '/about', component: About }
];
const router = new VueRouter({ routes });
2. <router-view>:内容渲染的占位符
在根组件(如 App.vue)中使用 <router-view> 标签,它是路由匹配到的组件的渲染出口。当 URL 变化时,Vue Router 会将匹配到的组件渲染到 <router-view> 位置。
vue
<!-- App.vue -->
<template>
<div>
<!-- 导航链接 -->
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<!-- 组件渲染出口:URL 匹配的组件会在这里显示 -->
<router-view></router-view>
</div>
</template>
二、路由切换的完整流程
当用户点击 <router-link> 或调用 this.$router.push() 时,路由切换的流程如下:
1. 触发 URL 变化
-
通过
<router-link>:点击<router-link to="/about">时,会自动调用$router.push('/about'),修改 URL(通过 HTML5 History API 或 hash 模式)。 -
通过编程式导航:调用
this.$router.push('/about')直接修改 URL。注意:Vue Router 默认使用 HTML5 History 模式(
history.pushState)修改 URL,不会触发页面刷新;若浏览器不支持,则降级为 hash 模式(#/about)。
2. 路由匹配:找到对应的组件
URL 变化后,Vue Router 会遍历路由规则(routes 数组),通过路径匹配算法找到与当前 URL 匹配的路由对象(route)。
- 匹配规则:优先精确匹配,再尝试模糊匹配(如动态路由
:id、通配符*)。 - 例如:URL 为
/about时,匹配{ path: '/about', component: About }。
3. 组件卸载与挂载
找到匹配的路由后,Vue Router 会:
-
卸载旧组件:触发旧组件的
beforeRouteLeave导航守卫 → 执行beforeDestroy、destroyed生命周期(若未被<keep-alive>缓存)。 -
挂载新组件:触发新组件的
beforeRouteEnter导航守卫 → 执行beforeCreate、created、mounted生命周期 → 将新组件渲染到<router-view>位置。若组件被
<keep-alive>缓存,则触发deactivated(旧组件失活)和activated(新组件激活),而非销毁 / 创建。
4. 导航守卫的作用(可选拦截)
在路由切换过程中,导航守卫(如 beforeEach、beforeEnter)可以拦截切换过程,实现权限校验、登录判断等逻辑:
javascript
运行
// 全局前置守卫:判断是否登录
router.beforeEach((to, from, next) => {
if (to.path !== '/login' && !isLogin()) {
next('/login'); // 未登录,强制跳转到登录页
} else {
next(); // 允许路由切换
}
});
三、嵌套路由的切换逻辑
当路由存在嵌套关系(如父路由包含子路由)时,切换逻辑会递归执行:
- 定义嵌套路由:
javascript
运行
const routes = [
{
path: '/user',
component: UserLayout, // 父组件
children: [
{ path: 'profile', component: UserProfile }, // 子路由1
{ path: 'settings', component: UserSettings } // 子路由2
]
}
];
- 嵌套
<router-view>:父组件(UserLayout)中需包含<router-view>,用于渲染子路由对应的组件:
vue
<!-- UserLayout.vue -->
<template>
<div class="user-layout">
<h2>用户中心</h2>
<router-link to="/user/profile">个人资料</router-link>
<router-link to="/user/settings">设置</router-link>
<!-- 子组件渲染出口 -->
<router-view></router-view>
</div>
</template>
-
切换过程:当 URL 从
/user/profile切换到/user/settings时:- 父组件
UserLayout保持挂载状态(不销毁)。 - 子路由匹配到
UserSettings,卸载UserProfile并挂载UserSettings,渲染到父组件的<router-view>中。
- 父组件
四、URL 变化不刷新页面的原因
Vue Router 切换路由时不会导致页面刷新,核心原因是:
- 使用 HTML5 History API(
history.pushState)或 hash 模式(location.hash)修改 URL,这两种方式都只会改变 URL 而不触发浏览器的默认页面刷新行为。 - 路由切换本质是单页应用(SPA)的内部视图更新,通过替换
<router-view>中的组件实现内容变化,整个过程在同一个 HTML 页面中完成。
总结
Vue 路由与页面内容的切换流程可概括为:URL 变化 → 路由匹配找到对应组件 → 卸载旧组件 / 挂载新组件 → 在 <router-view> 中渲染新组件。
核心依赖:
- 路由规则定义
path与component的映射。 <router-view>作为组件渲染的出口。- Vue Router 对 URL 变化的监听和组件切换的管理。
这种机制实现了单页应用的无刷新页面切换,提升了用户体验和应用性能。