网站首页 > 技术文章 正文
学习在哪里保存你的变量,以改善你的应用设计、性能和可读性。
什么是状态?
在编程中,状态指的是程序中的所有变量。变量表示存储在内存中的数据。
全局状态指的是全局作用域的变量。它们可以从整个应用程序的任何地方访问。
本地状态指的是局部作用域的变量。它们只能在声明它们的文件、组件或函数内访问。
派生状态指的是基于其他变量计算的变量。
const person = {
firstName: "Paul",
lastName: "Posey", id: 12
};
// displayName 是派生状态
const displayName = person.lastName + ", " + person.firstName;
当人们谈论前端Web应用中的状态时,他们通常指的是响应式状态。响应式状态跟踪更新并在发生时触发效果。响应式状态管理工具存在于所有框架中。在其中,状态变量本质上是一组getter和setter。当你访问值时,你是在获取它。当你重新赋值时,你是在设置它。创建响应式状态的工具还将有用于实例化状态变量、更新它、访问其先前值等方法。
本地状态管理
当你创建本地状态变量时,问问自己:
- o 这个变量需要重复更新吗?
- o 对这个变量的更新需要触发其他地方的变化吗?
如果不是,普通的let或const就足够了。响应式状态管理工具更强大,但会消耗更多资源。为什么要维护一辆法拉利,而自行车就能满足你的需求呢?
如果你确实需要更新变量并基于更新触发效果,你会想要创建响应式状态。
- o React: useState
- o Vue: ref
- o Angular: signals
在所有三个框架中,你向方法传递一个初始值。当你改变值时,效果将被触发。任何使用这些响应式状态变量的东西都会在更新时收到通知。所以如果你正在显示响应式状态变量的值,新值将被显示。
框架还提供了在更新后触发复杂效果的方法。例如,用户点击"下一页"按钮,所以你的pageNumber状态变量的值递增,你需要进行API调用来获取下一页的结果。你会在React中使用useReducer或useEffect,在Vue中使用watchers,在Angular中使用effects。
在Angular中,你可以使用signal创建响应式状态。
const person = signal({
firstName: "Paul",
lastName: "Posey",
id: 12
});
如果你只想分配一个新值,你可以使用set()来更新你的signal。如果你想基于旧值计算一个新值,你使用update()。
在Vue中,你可以使用ref创建响应式状态。
const person = ref({
firstName: "Paul",
lastName: "Posey",
id: 12
});
ref维护对原始值的引用并创建一个Proxy。你使用.value在Proxy内访问和重新赋值。
if (person.value.firstName === "Paul") {
person.value = {
firstName: "Pauline",
lastName: "Person", id: 14
};
}
在React中,你使用useState钩子创建一个状态值和一个更新函数。(钩子是一个函数,让你在函数组件内"钩入"React功能。)
const [person, setPerson] = useState({
firstName: "Paul",
lastName: "Posey",
id: 12
});
派生状态管理
- React: const
- Vue: computed
- Angular: computed signals
尽可能在功能最弱的状态工具中保持派生状态。
在React中,用const声明派生状态更高效。对响应式状态变量的更新将触发组件重新渲染。如果你更新一个用useState声明的变量并触发另一个用useState声明的变量的更新,组件将重新渲染两次。如果你只使用const,值无论如何都会在第一次重新渲染期间重新计算。
// 触发第二次重新渲染
const [person, setPerson] = useState({
firstName: "Paul",
lastName: "Posey",
id: 12
});
const [displayName, setDisplayName] = useState(() => person.firstName + " " + person.lastName);
// 不触发第二次重新渲染
const [person, setPerson] = useState({
firstName: "Paul",
lastName: "Posey",
id: 12
});
const displayName = person.firstName + " " + person.lastName;
在Vue和Angular中,这是你使用computed()的时候。
const displayName = computed(() => {
return person.value.lastName + ", " + person.value.firstName;
});
简单地说,每当person更新时,displayName就会更新。
Vue和Angular使用观察者模式并跟踪哪些响应式变量触发哪些效果。而不是重新渲染整个组件,只有需要更新的东西才会更新。
在观察者模式术语中,响应式变量person是主体。当我们在传递给computed()的回调函数中使用person时,该函数被添加到观察者列表中。当主体更新时,观察者被通知,函数将再次运行。这应该听起来很熟悉——addEventListener()也使用观察者模式。使用addEventListener(),主体是一个事件,你的监听器(你传递的事件处理回调函数)是观察者/效果。
依赖注入
- React: Context
- Vue: Provide/Inject
- Angular: Dependency Injection
你的应用程序越复杂,prop-drilling就越成问题。在小型应用程序中,将状态传递给子组件甚至孙组件是可以的。作为经验法则,如果你必须将状态作为prop传递给曾孙组件,是时候重新评估设计了。当多个组件需要使用来自API调用的相同数据时,通常会遇到这个问题。
在你求助于全局状态之前,考虑一个中间解决方案——依赖注入。它仍然是本地状态,但不是使用props,父组件提供状态,它们的子组件可以消费它。这就是为什么"依赖注入"和"provider/consumer"可以互换使用的原因。
这种模式不仅仅用于响应式状态。如果你有派生状态或复杂计算,你可以使用依赖注入作为一种记忆化。你只需要调用一次昂贵的函数,然后你可以注入结果。
在Vue中,组件使用provide方法创建一个键值对。然后,任何子组件都可以使用inject方法访问该值。
// 父组件
const isLoggedIn = ref(false);
provide("key", isLoggedIn);
// 子组件
const isLoggedIn = inject("key");
if (isLoggedIn) showData();
你可以将更新函数注入到消费者组件中。为了避免意外的副作用,更新应该保留在提供者组件中。
Angular也有一个用于依赖注入的inject方法。它提供了多种创建提供者的方法。一个常见的模式是创建一个类服务,但你也可以使用像字符串、布尔值或Date这样的值。如果你需要从消费者触发更新,你的服务类可以定义getter和setter。
依赖注入是React Context的基础。Context提供者包装你的组件树,任何子组件都可以使用useContext钩子访问提供的值。
// 父组件
const [isLoggedIn, setIsLoggedIn] = useState(false);
return (
<AuthContext.Provider value={{ isLoggedIn, setIsLoggedIn }}>
<ChildComponent />
</AuthContext.Provider>
);
// 子组件
const { isLoggedIn } = useContext(AuthContext);
if (isLoggedIn) showData();
何时使用全局状态
- React: Redux, Zustand, Context
- Vue: Pinia, Vuex
- Angular: NgRx, Akita
全局状态应该用于:
- 用户认证状态
- 主题偏好
- 购物车内容
- 应用程序设置
全局状态不应该用于:
- 表单数据
- 组件特定的UI状态
- 临时数据
全局状态管理工具通常使用发布-订阅模式。组件订阅状态变化,当状态更新时,所有订阅者都会收到通知。
总结
状态管理是前端开发的核心概念。理解不同类型的状态以及何时使用它们将帮助你构建更好的应用程序。
关键要点:
- 本地状态用于组件特定的数据
- 派生状态应该尽可能简单
- 依赖注入是prop-drilling的替代方案
- 全局状态应该谨慎使用
- 选择正确的状态管理工具可以提高性能和可维护性
猜你喜欢
- 2025-07-03 前端开发必备!Swiper 轮播与 Lightbox 灯箱组件深度解析
- 2025-07-03 第二篇 前端框架Vue.js(web前端框架vue)
- 2025-07-03 后端开发者如何学习前端(后端开发前端开发什么意思)
- 2025-07-03 如何快速生成可供前端开发的文档(一键生成前端代码)
- 2024-10-09 小程序开发和前端开发有什么区别?小程序开发需要学习那些内容
- 2024-10-09 计算机类专业详解之“前后端开发和移动开发岗位”
- 2024-10-09 前端开发教程-前端开发教程 前端开发 入门
- 2024-10-09 前端开发 后端开发 前端开发后端开发 职位
- 2024-10-09 产品经理的工作流程 产品经理工作流程和内容
- 2024-10-09 游戏开发中前后端开发主要是哪些工作内容?
你 发表评论:
欢迎- 586℃几个Oracle空值处理函数 oracle处理null值的函数
- 580℃Oracle分析函数之Lag和Lead()使用
- 567℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 564℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 560℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 553℃【数据统计分析】详解Oracle分组函数之CUBE
- 539℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 533℃Oracle有哪些常见的函数? oracle中常用的函数
- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端路由 (61)
- 前端数组 (73)
- 前端js面试题 (50)
- 前端定时器 (59)
- 前端懒加载 (49)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle 中文 (51)
- oracle的函数 (57)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)