当前位置: 首页 > news >正文

前端核心框架vue之(路由核心案例篇3/5)

写在前面的话:这是一个综合的案例,涉及到守卫,重定向。入门初级的兄弟们可以逐个技术栈速通,不要沉迷知识点的学习过程,实操可能更加的重要主包写了两三天

建议用时(2天-5天)

📘 Vue CLI 路由专项训练_前端开发文档

🧭 项目目标

开发一个具有完整前端路由功能的单页应用,涵盖权限控制、路由跳转、动态嵌套路由、查询参数、懒加载、重定向与 404 页面处理,用于 Vue Router 的专项训练。

📦 技术要求

  • Vue 2.x
  • Vue Router 3.x
  • Vue CLI 构建
  • Element UI 用于页面 UI
  • 项目目录结构清晰、组件模块划分合理

📁 项目结构建议

src/
├── main.js
├── router/
│   ├── index.js          // 路由配置
│   └── guards.js         // 路由守卫
├── views/
│   ├── Home.vue
│   ├── About.vue
│   ├── Products.vue
│   ├── Dashboard.vue
│   ├── Admin.vue
│   ├── RedirectInfo.vue
│   ├── NotFound.vue
│   └── user/
│       ├── UserDetail.vue
│       ├── UserProfile.vue
│       └── UserSettings.vue
└── App.vue

📍 路由功能要求

✅ 基础页面

  • / 首页
  • /about 关于页面
  • 页面中按钮跳转需通过路由控制

🔐 路由权限控制

  • /dashboard:登录后才能访问
  • /admin:仅管理员才能访问
  • 配置路由元信息 meta.requiresAuth / meta.requiresAdmin

🔗 动态 & 嵌套路由

  • /user/:id:用户详情页
  • 嵌套:/user/:id/profile/user/:id/settings
  • 子页面通过 <router-view> 展示

🔍 查询参数

  • /products?category=xxx&page=1
  • 页面应能读取并展示查询条件

📦 懒加载路由

  • /lazy:以异步组件形式加载页面
  • 页面应模拟加载延迟(2秒左右)

🔁 重定向

  • /redirect-test/about
  • /old-dashboard/dashboard

🚫 404 页面

  • 无匹配路径 → 显示 NotFound 页面

总览:

模块要求说明
基础路由配置所有页面路径与组件绑定正确
页面跳转页面内跳转通过 $router 实现
动态路由能读取用户 ID 并展示相关内容
嵌套路由子页面通过嵌套路由显示
查询参数处理页面可读写查询参数,刷新保持状态
路由守卫登录/权限控制逻辑合理,跳转提示完整
懒加载实现页面实现异步加载逻辑并显示加载提示
重定向与 404能正常重定向,404 页面显示无误
路由导航栏高亮状态导航菜单可识别当前路由状态

这里是参考答案,直接运行单个HTML文件即可

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Vue Router 核心功能训练 - 修复版</title><!-- Element Plus CSS --><link rel="stylesheet" href="https://unpkg.com/element-plus@2.4.4/dist/index.css"><!-- Vue 3 --><script src="https://unpkg.com/vue@3.3.8/dist/vue.global.js"></script><!-- Vue Router 4 --><script src="https://unpkg.com/vue-router@4.2.5/dist/vue-router.global.js"></script><!-- Element Plus --><script src="https://unpkg.com/element-plus@2.4.4/dist/index.full.js"></script><!-- Element Plus Icons --><script src="https://unpkg.com/@element-plus/icons-vue@2.1.0/dist/index.iife.min.js"></script>
</head>
<body><div id="app"></div><script>const { createApp, ref, reactive, computed } = Vue;const { createRouter, createWebHashHistory } = VueRouter;const { ElMessage, ElMessageBox } = ElementPlus;// 权限状态管理 - 修复响应式问题const authState = reactive({isLoggedIn: false,  // 直接使用普通值,不需要refisAdmin: false,     // 直接使用普通值,不需要ref  username: '',       // 直接使用普通值,不需要refasync login() {try {// 模拟登录过程const { value: credentials } = await ElMessageBox.prompt('请输入用户名 (admin/user)', '登录', {confirmButtonText: '登录',cancelButtonText: '取消',inputPattern: /\S+/,inputErrorMessage: '用户名不能为空'});// 预设账号逻辑if (credentials === 'admin') {this.isLoggedIn = true;this.isAdmin = true;this.username = 'admin';ElMessage.success('🎉 管理员登录成功!');} else if (credentials === 'user') {this.isLoggedIn = true;this.isAdmin = false;this.username = 'user';ElMessage.success('✅ 普通用户登录成功!');} else {// 其他用户名默认为普通用户this.isLoggedIn = true;this.isAdmin = false;this.username = credentials;ElMessage.success(`✅ 用户 ${credentials} 登录成功!`);}console.log('登录状态已更新 - 登录:', this.isLoggedIn, '管理员:', this.isAdmin, '用户名:', this.username);} catch {ElMessage.info('取消登录');}},logout() {console.log('开始退出登录...');this.isLoggedIn = false;this.isAdmin = false;this.username = '';ElMessage.success('退出登录成功');console.log('退出登录完成 - 登录:', this.isLoggedIn, '管理员:', this.isAdmin, '用户名:', this.username);},setAdmin() {console.log('尝试设为管理员 - 当前登录状态:', this.isLoggedIn, '用户名:', this.username);if (!this.isLoggedIn) {ElMessage.warning('请先登录');return;}this.isAdmin = true;ElMessage.success(`${this.username} 已设置为管理员`);console.log('设为管理员完成 - 管理员状态:', this.isAdmin);},setUser() {console.log('尝试设为普通用户 - 当前登录状态:', this.isLoggedIn, '用户名:', this.username);if (!this.isLoggedIn) {ElMessage.warning('请先登录');return;}this.isAdmin = false;ElMessage.success(`${this.username} 已设置为普通用户`);console.log('设为普通用户完成 - 管理员状态:', this.isAdmin);},getStatusText() {if (!this.isLoggedIn) return '未登录';const role = this.isAdmin ? '管理员' : '普通用户';return `${this.username} (${role})`;}});// 用户设置数据(持久化)const userSettings = reactive({data: {},save(userId, settings) {this.data[userId] = { ...settings };ElMessage.success('设置保存成功');},load(userId) {return this.data[userId] || {emailNotify: true,smsNotify: false,privacy: 'public'};}});// 路由组件// 首页const Home = {template: `<div><el-alert title="Vue Router 核心功能训练 - 修复版" type="success" show-icon /><!-- 权限控制面板 --><el-card style="margin-top: 20px;"><template #header><span>权限控制面板 - 路由守卫测试</span></template><!-- 添加账号说明 --><el-alert title="测试账号说明" type="info" :closable="false"style="margin-bottom: 15px;"><p><strong>管理员账号:</strong> admin</p><p><strong>普通用户:</strong> user</p><p><strong>其他用户名:</strong> 默认为普通用户</p></el-alert><div style="margin-bottom: 15px;">当前状态: <el-tag :type="getTagType()">{{ authState.getStatusText() }}</el-tag></div><el-space><el-button @click="handleLogin" type="success" :disabled="authState.isLoggedIn"><el-icon><User /></el-icon>登录</el-button><el-button @click="handleLogout" type="info" :disabled="!authState.isLoggedIn"><el-icon><SwitchButton /></el-icon>退出登录</el-button><el-button @click="handleSetAdmin" type="danger" :disabled="authState.isAdmin || !authState.isLoggedIn"><el-icon><Crown /></el-icon>设为管理员</el-button><el-button @click="handleSetUser" type="warning" :disabled="!authState.isAdmin"><el-icon><Avatar /></el-icon>设为普通用户</el-button></el-space></el-card><el-row :gutter="20" style="margin-top: 20px;"><el-col :span="12"><el-card header="基础路由"><el-button @click="$router.push('/about')" block><el-icon><InfoFilled /></el-icon>关于页面</el-button></el-card></el-col><el-col :span="12"><el-card header="动态路由"><el-button @click="$router.push('/user/123')" block><el-icon><User /></el-icon>用户 123</el-button></el-card></el-col></el-row><el-row :gutter="20" style="margin-top: 20px;"><el-col :span="12"><el-card header="查询参数"><el-button @click="$router.push('/products?category=books')" block><el-icon><Reading /></el-icon>图书分类</el-button></el-card></el-col><el-col :span="12"><el-card header="路由守卫测试"><el-space direction="vertical" style="width: 100%;"><el-button @click="$router.push('/dashboard')" type="primary" block><el-icon><DataBoard /></el-icon>需要登录</el-button><el-button @click="$router.push('/admin')" type="danger" block><el-icon><Setting /></el-icon>需要管理员</el-button></el-space></el-card></el-col></el-row><el-row :gutter="20" style="margin-top: 20px;"><el-col :span="24"><el-card header="其他功能"><el-space><el-button @click="$router.push('/lazy')" type="warning"><el-icon><Loading /></el-icon>懒加载</el-button><el-button @click="testRedirect" type="info"><el-icon><Right /></el-icon>重定向测试</el-button><el-button @click="$router.push('/notfound')" type="danger"><el-icon><WarningFilled /></el-icon>404测试</el-button></el-space></el-card></el-col></el-row></div>`,computed: {authState() {return authState;}},methods: {async handleLogin() {console.log('点击登录按钮');await authState.login();this.$forceUpdate(); // 强制更新组件},handleLogout() {console.log('点击退出登录按钮');authState.logout();this.$forceUpdate(); // 强制更新组件},handleSetAdmin() {console.log('点击设为管理员按钮');authState.setAdmin();this.$forceUpdate(); // 强制更新组件},handleSetUser() {console.log('点击设为普通用户按钮');authState.setUser();this.$forceUpdate(); // 强制更新组件},getTagType() {if (!authState.isLoggedIn) return 'info';return authState.isAdmin ? 'danger' : 'success';},testRedirect() {ElMessage.info('即将重定向到关于页面...');setTimeout(() => {this.$router.push('/redirect-test');}, 1000);}}};// 关于页面const About = {template: `<div><el-alert title="关于页面 - 基础路由演示" type="info" show-icon /><el-card style="margin-top: 20px;"><p>这是一个基础路由页面,演示了简单的页面跳转功能。</p><el-divider /><p><strong>路由信息:</strong></p><ul><li>路径: {{ $route.path }}</li><li>名称: {{ $route.name || '未命名' }}</li><li>来源: {{ $route.meta.from || '直接访问' }}</li></ul><el-space style="margin-top: 20px;"><el-button @click="$router.back()" icon="ArrowLeft">返回上页</el-button><el-button @click="$router.push('/')" type="primary" icon="House">返回首页</el-button></el-space></el-card></div>`};// 重定向说明页面const RedirectInfo = {template: `<div><el-alert title="重定向演示页面" type="warning" show-icon /><el-card style="margin-top: 20px;"><p>🎯 <strong>重定向逻辑说明:</strong></p><ol><li>当您访问 <code>/redirect-test</code> 时</li><li>路由会自动重定向到 <code>/about</code> 页面</li><li>但会在 about 页面的 meta 中标记来源为 "重定向"</li></ol><el-divider /><p><strong>测试重定向:</strong></p><el-space direction="vertical" style="width: 100%;"><el-button @click="testRedirect1" type="primary" block>测试重定向 1: /redirect-test → /about</el-button><el-button @click="testRedirect2" type="success" block>测试重定向 2: /old-dashboard → /dashboard (需要登录)</el-button><el-button @click="testRedirect3" type="warning" block>测试重定向 3: /legacy-admin → /admin (需要管理员)</el-button></el-space><el-space style="margin-top: 20px;"><el-button @click="$router.back()" icon="ArrowLeft">返回上页</el-button><el-button @click="$router.push('/')" type="primary" icon="House">返回首页</el-button></el-space></el-card></div>`,methods: {testRedirect1() {ElMessage.info('即将重定向: /redirect-test → /about');setTimeout(() => {this.$router.push('/redirect-test');}, 1000);},testRedirect2() {ElMessage.info('即将重定向: /old-dashboard → /dashboard');setTimeout(() => {this.$router.push('/old-dashboard');}, 1000);},testRedirect3() {ElMessage.info('即将重定向: /legacy-admin → /admin');setTimeout(() => {this.$router.push('/legacy-admin');}, 1000);}}};// 用户详情页面 - 动态路由const UserDetail = {template: `<div><el-alert :title="'用户详情 - ID: ' + $route.params.id + ' (动态路由)'" type="success" show-icon /><el-card style="margin-top: 20px;"><p><strong>用户ID:</strong> {{ $route.params.id }}</p><p><strong>用户名:</strong> 用户{{ $route.params.id }}</p><el-divider content-position="left">嵌套路由</el-divider><el-space><el-button @click="$router.push('/user/' + $route.params.id + '/profile')" type="primary"><el-icon><User /></el-icon>个人资料</el-button><el-button @click="$router.push('/user/' + $route.params.id + '/settings')" type="success"><el-icon><Setting /></el-icon>账户设置</el-button></el-space><!-- 嵌套路由出口 --><div style="margin-top: 20px;"><router-view></router-view></div><el-divider /><el-space><el-button @click="$router.back()" icon="ArrowLeft">返回上页</el-button><el-button @click="$router.push('/')" type="primary" icon="House">返回首页</el-button></el-space></el-card></div>`};// 用户资料 - 嵌套路由const UserProfile = {template: `<el-card><template #header><el-icon><User /></el-icon>个人资料 (嵌套路由)</template><p><strong>真实姓名:</strong> 用户{{ $route.params.id }}</p><p><strong>邮箱:</strong> user{{ $route.params.id }}@example.com</p><p><strong>注册时间:</strong> 2024-01-01</p><p><strong>最后登录:</strong> {{ new Date().toLocaleString() }}</p></el-card>`};// 用户设置 - 嵌套路由const UserSettings = {template: `<el-card><template #header><el-icon><Setting /></el-icon>账户设置 (嵌套路由)</template><el-form label-width="100px"><el-form-item label="邮件通知"><el-switch v-model="settings.emailNotify" /></el-form-item><el-form-item label="短信通知"><el-switch v-model="settings.smsNotify" /></el-form-item><el-form-item label="隐私设置"><el-select v-model="settings.privacy"><el-option label="公开" value="public" /><el-option label="仅好友" value="friends" /><el-option label="私密" value="private" /></el-select></el-form-item><el-form-item><el-button type="primary" @click="save">保存设置</el-button><el-button @click="reset">重置</el-button></el-form-item></el-form><el-divider /><p><strong>当前设置:</strong></p><pre>{{ JSON.stringify(settings, null, 2) }}</pre></el-card>`,data() {return {settings: {}};},created() {this.loadSettings();},watch: {'$route'() {this.loadSettings();}},methods: {loadSettings() {this.settings = userSettings.load(this.$route.params.id);},save() {userSettings.save(this.$route.params.id, this.settings);},reset() {this.settings = {emailNotify: true,smsNotify: false,privacy: 'public'};ElMessage.info('设置已重置');}}};// 商品页面 - 查询参数const Products = {template: `<div><el-alert title="商品列表 - 查询参数演示" type="warning" show-icon /><el-card style="margin-top: 20px;"><el-alert :title="'当前URL: ' + $route.fullPath" type="info" :closable="false" /><el-form :inline="true" style="margin-top: 20px;"><el-form-item label="分类"><el-select v-model="category" @change="updateQuery"><el-option label="全部" value="" /><el-option label="图书" value="books" /><el-option label="电子产品" value="electronics" /><el-option label="服装" value="clothing" /></el-select></el-form-item><el-form-item label="页码"><el-input-number v-model="page" :min="1" @change="updateQuery" /></el-form-item><el-form-item><el-button @click="resetQuery">重置</el-button></el-form-item></el-form><p style="margin-top: 20px;"><strong>筛选结果:</strong> {{ category ? '分类: ' + category : '全部商品' }}{{ page > 1 ? ', 第' + page + '页' : '' }}</p><el-space style="margin-top: 20px;"><el-button @click="$router.back()">返回上页</el-button><el-button @click="$router.push('/')" type="primary">返回首页</el-button></el-space></el-card></div>`,data() {return {category: '',page: 1};},created() {const query = this.$route.query;this.category = query.category || '';this.page = parseInt(query.page) || 1;},watch: {'$route'() {const query = this.$route.query;this.category = query.category || '';this.page = parseInt(query.page) || 1;}},methods: {updateQuery() {const query = {};if (this.category) query.category = this.category;if (this.page > 1) query.page = this.page;this.$router.push({ path: '/products', query: query });},resetQuery() {this.$router.push('/products');}}};// 数据面板 - 需要登录const Dashboard = {template: `<div><el-alert title="数据面板 - 路由守卫演示 (需要登录)" type="success" show-icon /><el-card style="margin-top: 20px;"><p>🎉 恭喜!您已通过登录验证,可以访问此页面。</p><p>当前用户状态: <el-tag type="success">{{ authState.getStatusText() }}</el-tag></p><p>访问时间: {{ new Date().toLocaleString() }}</p><el-space style="margin-top: 20px;"><el-button @click="$router.back()">返回上页</el-button><el-button @click="$router.push('/')" type="primary">返回首页</el-button></el-space></el-card></div>`,computed: {authState() {return authState;}}};// 管理员页面 - 需要管理员权限const Admin = {template: `<div><el-alert title="管理员页面 - 路由守卫演示 (需要管理员权限)" type="danger" show-icon /><el-card style="margin-top: 20px;"><p>🎉 恭喜!您拥有管理员权限,可以访问此页面。</p><p>当前用户状态: <el-tag type="danger">{{ authState.getStatusText() }}</el-tag></p><p>访问时间: {{ new Date().toLocaleString() }}</p><el-space style="margin-top: 20px;"><el-button @click="$router.back()">返回上页</el-button><el-button @click="$router.push('/')" type="primary">返回首页</el-button></el-space></el-card></div>`,computed: {authState() {return authState;}}};// 懒加载页面const LazyPage = {template: `<div><el-alert title="懒加载页面 - 异步组件演示" type="warning" show-icon /><el-card style="margin-top: 20px;"><p>🎉 这个页面通过懒加载方式加载,模拟了2秒加载时间。</p><p>加载完成时间: {{ new Date().toLocaleString() }}</p><el-space style="margin-top: 20px;"><el-button @click="$router.back()">返回上页</el-button><el-button @click="$router.push('/')" type="primary">返回首页</el-button></el-space></el-card></div>`};// 404页面const NotFound = {template: `<el-resulticon="warning"title="404"sub-title="页面未找到"><template #extra><el-space><el-button @click="$router.back()">返回上页</el-button><el-button type="primary" @click="$router.push('/')">返回首页</el-button></el-space></template></el-result>`};// 主应用组件const App = {template: `<el-container style="min-height: 100vh;"><!-- 头部导航 --><el-header><el-menu :default-active="$route.path" mode="horizontal"><el-menu-item index="/" @click="$router.push('/')"><el-icon><House /></el-icon>首页</el-menu-item><el-menu-item index="/about" @click="$router.push('/about')"><el-icon><InfoFilled /></el-icon>关于</el-menu-item><el-menu-item index="/products" @click="$router.push('/products')"><el-icon><Goods /></el-icon>商品</el-menu-item><el-menu-item index="/redirect-info" @click="$router.push('/redirect-info')"><el-icon><Right /></el-icon>重定向</el-menu-item><div style="flex: 1;"></div><!-- 状态显示 --><div style="display: flex; align-items: center; margin-right: 20px;"><span style="margin-right: 10px;">当前状态:</span><el-tag :type="getStatusTagType()">{{ authState.getStatusText() }}</el-tag></div></el-menu></el-header><!-- 主要内容 --><el-main><!-- 路由信息 --><el-alert :title="getRouteInfo()" type="info" :closable="false"style="margin-bottom: 20px;"/><!-- 页面内容 --><router-view></router-view></el-main></el-container>`,computed: {authState() {return authState;}},methods: {getStatusTagType() {if (!authState.isLoggedIn) return 'info';return authState.isAdmin ? 'danger' : 'success';},getRouteInfo() {let info = `当前路由: ${this.$route.path}`;if (Object.keys(this.$route.params).length > 0) {info += ` | 参数: ${JSON.stringify(this.$route.params)}`;}if (Object.keys(this.$route.query).length > 0) {info += ` | 查询: ${JSON.stringify(this.$route.query)}`;}if (this.$route.meta.from) {info += ` | 来源: ${this.$route.meta.from}`;}return info;}}};// 路由配置const routes = [{ path: '/', component: Home },{ path: '/about', component: About,beforeEnter: (to, from, next) => {// 检查是否来自重定向if (from.path === '/redirect-test') {to.meta.from = '重定向';}next();}},{ path: '/redirect-info', component: RedirectInfo },{path: '/user/:id',component: UserDetail,children: [{ path: 'profile', component: UserProfile },{ path: 'settings', component: UserSettings }]},{ path: '/products', component: Products },{ path: '/dashboard', component: Dashboard, meta: { requiresAuth: true } },{ path: '/admin', component: Admin, meta: { requiresAuth: true, requiresAdmin: true } },{path: '/lazy',component: () => {return new Promise(resolve => {ElMessage.info('正在加载页面,请稍候...');setTimeout(() => resolve(LazyPage), 2000);});}},// 重定向路由 - 更明显的重定向逻辑{ path: '/redirect-test', redirect: to => {ElMessage.success('重定向成功: /redirect-test → /about');return '/about';}},{ path: '/old-dashboard', redirect: '/dashboard'},{ path: '/legacy-admin', redirect: '/admin'},{ path: '/:pathMatch(.*)*', component: NotFound }];// 创建路由const router = createRouter({history: createWebHashHistory(),routes});// 路由守卫 - 改进的守卫逻辑router.beforeEach((to, from, next) => {console.log('=== 路由守卫检查 ===');console.log('从:', from.path, '到:', to.path);console.log('登录状态:', authState.isLoggedIn);console.log('管理员状态:', authState.isAdmin);console.log('路由元信息:', to.meta);// 检查是否需要登录if (to.meta.requiresAuth && !authState.isLoggedIn) {ElMessage.error('🔒 请先登录才能访问此页面!');console.log('❌ 访问被拒绝: 需要登录');next('/');return;}// 检查是否需要管理员权限if (to.meta.requiresAdmin && (!authState.isLoggedIn || !authState.isAdmin)) {if (!authState.isLoggedIn) {ElMessage.error('🔒 请先登录!');console.log('❌ 访问被拒绝: 需要登录');} else {ElMessage.error('🔒 需要管理员权限才能访问此页面!');console.log('❌ 访问被拒绝: 需要管理员权限');}next('/');return;}console.log('✅ 路由守卫通过');next();});// 路由后置守卫 - 记录访问日志router.afterEach((to, from) => {console.log(`✅ 路由跳转完成: ${from.path}${to.path}`);});// 创建应用const app = createApp(App);// 注册图标for (const [key, component] of Object.entries(ElementPlusIconsVue)) {app.component(key, component);}app.use(ElementPlus);app.use(router);app.mount('#app');console.log('🚀 Vue Router 训练应用启动成功!');</script>
</body>
</html>
http://www.dtcms.com/a/419392.html

相关文章:

  • vue中不同的watch方法的坑
  • 网站首页排版设计广州网络公关公司
  • 批量重命名技巧:使用PowerShell一键整理图片文件命名规范
  • 手机版网站怎么做的企业解决方案架构师
  • 网站企业备案改个人备案专业微网站制作
  • 新天力科技以创新驱动发展,铸就食品包装容器行业领军者
  • crew AI笔记[7] - flow特性示例
  • 广州制作网站公司网站开发收税
  • 二阶可降阶微分方程的求解方法总结
  • 纯静态企业网站模板免费下载手机app编程
  • Redis在高并发场景中的核心优势
  • 教育网站 网页赏析网络营销推广的优缺点
  • 金溪县建设局网站品牌网站怎么建立
  • 中国气候政策不确定性数据(2000-2022)
  • 大发快三网站自做青海省城乡建设厅网站
  • 800G DR8与其他800G光模块的对比分析
  • 第四部分:VTK常用类详解(第100章 vtkHandleWidget句柄控件类)
  • Kafka 和 RabbitMQ 使用:消息队列的强大工具
  • 网站注册信息网站的建设有什么好处
  • 物理层-传输介质
  • npm 包构建与发布
  • 第四部分:VTK常用类详解(第99章 vtkBorderWidget边框控件类)
  • 如何播放 M3U8 格式的视频
  • 视频推拉流EasyDSS如何运用无人机直播技术高效排查焚烧烟火?
  • 常规网站服务器cms程序
  • tomcat创建bat启动,结合任务计划实现自动重启tomcat服务
  • 滨海网站建设wordpress .htaccess下载
  • CCS主题配置,
  • 08网站建设自己做电商网站吗
  • Nginx 入门:高性能 Web 服务器与反向代理的利器