Angular React Vue 比较 -状态篇

本篇内容我们将探讨以下内容。

  • 什么是状态
  • 为什么需要状态管理
  • Angular中如何实现状态管理
  • React中如何实现状态管理
  • Vue中如何实现状态管理

什么是状态

三大框架都遵循组件化的开发思想。状态在这里表示组件在应用中某一时刻的外观或内容变化,它更倾向于描述一种延续的变化。

外观和内容发生改变的组件

为什么需要状态管理

随着你的应用不断变大,更有意识的去关注应用状态如何组织,以及数据如何在组件之间流动会对你很有帮助。冗余或重复的状态往往是缺陷的根源。

状态管理指的是如何组织好状态,如何保持状态更新逻辑的可维护性,以及如何跨组件共享状态。

什么情况下需要状态管理呢?

  1. 多个视图可能都依赖于同一份状态。
  2. 来自不同视图的交互也可能需要更改同一份状态。

对于情景 1,一个可行的办法是将共享状态“提升”到共同的祖先组件上去,再通过 props 传递下来。然而在深层次的组件树结构中这么做的话,很快就会使得代码变得繁琐冗长。这会导致另一个问题:Prop 逐级透传问题。

对于情景 2,我们经常发现自己会直接通过模板引用获取父/子实例,或者通过触发的事件尝试改变和同步多个状态的副本。但这些模式的健壮性都不甚理想,很容易就会导致代码难以维护。

一个更简单直接的解决方案是抽取出组件间的共享状态,放在一个全局单例中来管理。

Angular中如何实现状态管理

在 Angular 中并没有状态管理这一概念,不过框架本身是一个很成熟的框架,使用 Service + RxJS 就能实现状态管理。

状态管理的示例:

user.service.ts文件

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

// 定义一个用户接口
export interface User {
  id: number;
  name: string;
  email: string;
}

// 定义一个初始的用户状态
const initialUser: User = {
  id: 0,
  name: 'Guest',
  email: 'guest@example.com'
};

@Injectable({
  providedIn: 'root'
})
export class UserService {
  // 创建一个 BehaviorSubject 来存储用户状态
  private userSubject = new BehaviorSubject<User>(initialUser);

  // 创建一个公开的 user$ 属性,用来订阅用户状态
  user$ = this.userSubject.asObservable();

  constructor() { }

  // 创建一个方法,用来更新用户状态
  updateUser(user: User) {
    this.userSubject.next(user);
  }
}

user.component.ts文件

import { Component, OnInit } from '@angular/core';
import { UserService, User } from '../user.service';

@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
  // 定义一个 user 属性,用来存储用户状态
  user: User;

  constructor(private userService: UserService) { }

  ngOnInit(): void {
    // 订阅 user$ 属性,获取用户状态
    this.userService.user$.subscribe(user => {
      this.user = user;
    });
  }

  // 定义一个方法,用来模拟修改用户状态
  changeUser() {
    // 随机生成一个新的用户对象
    const newUser: User = {
      id: Math.floor(Math.random() * 100),
      name: 'User' + Math.floor(Math.random() * 100),
      email: 'user' + Math.floor(Math.random() * 100) + '@example.com'
    };
    // 调用 updateUser 方法,更新用户状态
    this.userService.updateUser(newUser);
  }
}

user.component.html文件

<h1>User Component</h1>
<p>User ID: {{ user.id }}</p>
<p>User Name: {{ user.name }}</p>
<p>User Email: {{ user.email }}</p>
<button (click)="changeUser()">Change User</button>

React中如何实现状态管理

对于拥有许多状态更新逻辑的组件来说,过于分散的事件处理程序可能会令人不知所措。对于这种情况,你可以将组件的所有状态更新逻辑整合到一个外部函数中,这个函数叫作 reducer

状态管理的示例:

user.reducer.js 文件

// 定义一个初始的用户状态
const initialUser = {
  id: 0,
  name: 'Guest',
  email: 'guest@example.com'
};

// 定义一个 reducer 函数,接收一个 state 和一个 action
export default function userReducer(state = initialUser, action) {
  // 根据 action 的类型返回一个新的状态对象
  switch (action.type) {
    case 'UPDATE_USER':
      return action.payload;
    default:
      return state;
  }
}

user.component.jsx 文件

import React, { useReducer } from 'react';
import userReducer from './user.reducer';

function UserComponent() {
  // 使用 useReducer hook 创建一个 state 和一个 dispatch 函数
  const [user, dispatch] = useReducer(userReducer, initialUser);

  // 定义一个方法,用来模拟修改用户状态
  function changeUser() {
    // 随机生成一个新的用户对象
    const newUser = {
      id: Math.floor(Math.random() * 100),
      name: 'User' + Math.floor(Math.random() * 100),
      email: 'user' + Math.floor(Math.random() * 100) + '@example.com'
    };
    // 调用 dispatch 函数,发送一个类型为 UPDATE_USER 的 action,并将新的用户对象作为 payload
    dispatch({ type: 'UPDATE_USER', payload: newUser });
  }

  return (
    <div>
      <h1>User Component</h1>
      <p>User ID: {user.id}</p>
      <p>User Name: {user.name}</p>
      <p>User Email: {user.email}</p>
      <button onClick={changeUser}>Change User</button>
    </div>
  );
}

export default UserComponent;

Vue中如何实现状态管理

在 Vue 中可以抽取出组件间的共享状态,放在一个全局单例中来管理。

状态管理的示例:

store.ts 文件

import { ref } from 'vue';

// 定义一个用户接口
export interface User {
  id: number;
  name: string;
  email: string;
}

// 定义一个初始的用户状态
const initialUser: User = {
  id: 0,
  name: 'Guest',
  email: 'guest@example.com'
};

// 使用 ref 函数创建一个响应式的状态对象
export const user = ref<User>(initialUser);

// 定义一个函数,用来更新用户状态
export function updateUser(user: User) {
  // 将新的用户对象赋值给 user
  user.value = user;
}

user.component.vue 文件

<template>
  <div>
    <h1>User Component</h1>
    <p>User ID: {{ user.id }}</p>
    <p>User Name: {{ user.name }}</p>
    <p>User Email: {{ user.email }}</p>
    <button @click="changeUser">Change User</button>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';
import { user, updateUser, User } from './store.ts';

// 使用 computed 函数创建一个计算属性,用于返回 user 中的数据
const user = computed(() => user.value);

// 定义一个方法,用来模拟修改用户状态
function changeUser() {
  // 随机生成一个新的用户对象
  const newUser: User = {
    id: Math.floor(Math.random() * 100),
    name: 'User' + Math.floor(Math.random() * 100),
    email: 'user' + Math.floor(Math.random() * 100) + '@example.com'
  };
  // 调用 updateUser 函数,更新用户状态
  updateUser(newUser);
}
</script>

小结

在三大框架中, Angular 对状态管理并没有做过多的描述,这个可能与它是面向对象类的编程理念相关,可以通过封装类库来实现状态管理。在React与Vue中,状态是最核心的概念,所有组件中的交互与数据渲染都与状态有关。

文章参考链接:

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注