怎样通过一个用户认证系统案例对比三大框架的实现方式

访客 全栈框架 1

本文目录导读:

  1. 案例需求定义
  2. React (基于 Hooks + Context API / 或 Zustand)
  3. Vue 3 (基于 Composition API + Pinia)
  4. Angular (基于 Service + HttpClient + Route Guards)
  5. 横向对比总结表
  6. 决定性的选择建议

这是一个非常经典且具有实际意义的命题,通过一个用户认证系统(包含注册、登录、Token验证、权限路由)的案例,可以清晰地对比 React(Next.js/SPA)Vue(Nuxt.js/SPA)Angular(独立框架) 这三者在 工程化思维、响应式机制、状态管理、以及路由守卫 上的核心差异。

下面我将用一个统一的业务需求来拆解对比:

案例需求定义

  1. API 接口POST /api/login (返回 JWT Token),GET /api/user/profile (需 Bearer Token 验证)。
  2. UI 界面:登录页(Login)、仪表盘页(Dashboard,需登录)、404 页。
  3. 核心逻辑
    • 用户登录成功后,Token 存入 localStorage
    • 页面刷新后,自动从 localStorage 读取 Token 并验证用户身份。
    • 未登录用户访问 Dashboard 时,自动重定向到登录页(路由守卫)。
    • 支持用户登出。

React (基于 Hooks + Context API / 或 Zustand)

实现哲学函数式、单向数据流、显式状态提升,没有官方的路由和 HTTP 模块,通常依赖 react-router-domfetch/axios

核心代码思路

  1. 身份状态管理(Context + useReducer)

    // AuthContext.js
    const AuthContext = createContext();
    const authReducer = (state, action) => {
      switch (action.type) {
        case 'LOGIN_SUCCESS': return { ...state, user: action.payload, token: action.token };
        case 'LOGOUT': return { ...state, user: null, token: null };
        default: return state;
      }
    };
    export const AuthProvider = ({ children }) => {
      const [state, dispatch] = useReducer(authReducer, { user: null, token: localStorage.getItem('token') });
      // 初始化时检查Token
      useEffect(() => {
        if (state.token) {
          fetch('/api/user/profile', { headers: { Authorization: `Bearer ${state.token}` } })
            .then(res => res.json())
            .then(data => dispatch({ type: 'LOGIN_SUCCESS', payload: data, token: state.token }))
            .catch(() => dispatch({ type: 'LOGOUT' }));
        }
      }, []);
      return <AuthContext.Provider value={{ state, dispatch }}>{children}</AuthContext.Provider>;
    };
  2. 路由守卫(Wrapper 组件)

    // PrivateRoute.js
    const PrivateRoute = ({ children }) => {
      const { state } = useContext(AuthContext);
      const location = useLocation();
      // 若用户为null且Token存在代表正在加载,显示loading;若用户为null且无Token则重定向
      if (!state.user && state.token) return <div>Loading...</div>;
      return state.user ? children : <Navigate to="/login" state={{ from: location }} replace />;
    };
  • 灵活性高:路由守卫通过组件包裹实现,任何逻辑都可插入。
  • 状态显式useReducer 强制显式处理 action。
  • 依赖外部:需要自己选择 react-router 和 HTTP 库。

Vue 3 (基于 Composition API + Pinia)

实现哲学响应式代理、声明式、渐进增强,Vue 的响应式系统(refreactive)让状态管理变得非常直觉,官方推荐 vue-routerPinia

核心代码思路

  1. 身份状态管理(Pinia Store)

    // stores/auth.js
    export const useAuthStore = defineStore('auth', () => {
      const user = ref(null);
      const token = ref(localStorage.getItem('token'));
      const isAuthenticated = computed(() => !!user.value); // 响应式计算属性
      async function init() {
        if (token.value) {
          try {
            const res = await fetch('/api/user/profile', { headers: { Authorization: `Bearer ${token.value}` } });
            user.value = await res.json();
          } catch {
            logout();
          }
        }
      }
      async function login(credentials) { /* ... */ }
      function logout() { user.value = null; token.value = null; localStorage.removeItem('token'); }
      return { user, token, isAuthenticated, init, login, logout };
    });
  2. 路由守卫(Navigation Guard)

    // router/index.js
    import { useAuthStore } from '@/stores/auth';
    router.beforeEach(async (to, from, next) => {
      const authStore = useAuthStore();
      if (to.meta.requiresAuth) {
        // 关键:如果还没初始化用户数据,等待init完成
        if (!authStore.user && authStore.token) {
          await authStore.init(); 
        }
        if (authStore.user) {
          next();
        } else {
          next({ name: 'Login', query: { redirect: to.fullPath } });
        }
      } else {
        next();
      }
    });
  • 响应式极其自然computed 自动追踪依赖。
  • 路由守卫是“函数”:Vue Router 的 beforeEach 是全局前置守卫,逻辑集中且支持 async/await
  • Store 零样板:Pinia 的 Composition API 写法非常接近普通 JS 逻辑。

Angular (基于 Service + HttpClient + Route Guards)

实现哲学强类型、面向对象、依赖注入(DI),Angular 提供全栈式官方方案:HttpClientModuleRouterModuleGuardsInterceptors

核心代码思路

  1. 身份状态管理(Service + RxJS Subject)

    // auth.service.ts
    @Injectable({ providedIn: 'root' })
    export class AuthService {
      private userSubject = new BehaviorSubject<User | null>(null);
      user$ = this.userSubject.asObservable(); // 供组件订阅
      constructor(private http: HttpClient) {
        const token = localStorage.getItem('token');
        if (token) {
          this.http.get<User>('/api/user/profile')
            .subscribe({ next: user => this.userSubject.next(user), error: () => this.logout() });
        }
      }
      get currentUserValue(): User | null { return this.userSubject.value; }
      login(creds: { username: string; password: string }) {
        return this.http.post<{ token: string }>('/api/login', creds).pipe(
          tap(res => { localStorage.setItem('token', res.token); })
        );
      }
    }
  2. 路由守卫(Guard Class)

    // auth.guard.ts
    @Injectable({ providedIn: 'root' })
    export class AuthGuard implements CanActivate {
      constructor(private authService: AuthService, private router: Router) {}
      canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        if (this.authService.currentUserValue) {
          return true;
        }
        this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
        return false;
      }
    }
    // app-routing.module.ts
    const routes: Routes = [
      { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] }
    ];
  3. HTTP 拦截器(自动注入 Token)

    // jwt.interceptor.ts
    @Injectable()
    export class JwtInterceptor implements HttpInterceptor {
      intercept(req: HttpRequest<any>, next: HttpHandler) {
        const token = localStorage.getItem('token');
        if (token) {
          req = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
        }
        return next.handle(req);
      }
    }
  • 强类型与 DI:Service 通过 DI 注入,代码清晰可测。
  • 拦截器机制:自动处理 Token 注入和 401 错误,是三大框架中内置最完善的。
  • 路由守卫是“类”:强制分离关注点,通过 canActivate 接口实现。
  • RxJS 生态:异步操作(如 user$)完全基于 Observable,上手门槛稍高。

横向对比总结表

维度 React (SPA) Vue 3 (SPA) Angular
状态管理 Context + useReducer / Zustand(手动) Pinia / Vuex(官方推荐) Service + BehaviorSubject(官方内置)
路由守卫 组件式 <PrivateRoute>(灵活但分散) 全局 beforeEach(集中、异步友好) 类式 Guard(canActivate、可复用)
HTTP 拦截 无原生,需 axios 拦截器 无原生,需 axios 拦截器 原生 HttpInterceptor(非常强大)
响应式机制 通过 useState/useReducer 触发重新渲染 基于 Proxy 的响应式代理(自动追踪) Zone.js + ChangeDetection(脏检查)
TypeScript 支持(可选,通过 JSDoc/TSX) 支持(优秀,Vue 3 内置) 强制使用(深度集成)
单元测试 Jest + React Testing Library Vitest + Vue Test Utils Jasmine + TestBed(官方集成)
学习曲线 低(JS 基础即可),但生态选择多 低(API 简洁直观) 中高(RxJS、DI、装饰器)
代码量(认证系统) 中等,需要手动组织 较少,语法糖多 较多,但结构一致性强

决定性的选择建议

  • 选 React:团队熟悉 JS/TS,追求灵活性和最大的生态(如想要任何状态管理库、任何路由方案)。
  • 选 Vue:团队小型或快速原型,追求开发效率和直观性,Vue 的响应式 + Pinia 在认证这类“实时状态”场景中写起来最愉悦
  • 选 Angular:大型企业级项目,需要强类型约束、HTTP 拦截器、路由 Guard 等“开箱即用”的规范,团队需要长期维护,Angular 的架构一致性最佳。

最终的结论:如果只看“用户认证”这个具体案例的实现优雅度,Vue 3 + Pinia 由于其响应式自动追踪和 Pinia 的简洁性,代码最为清爽;Angular 因其强有力的拦截器和 Guard 类的分离,最适合需要严格工程规范的团队;React 则提供了最大的组合自由,但也需要自己搭建更多的脚手架。

标签: js React Angular

抱歉,评论功能暂时关闭!