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

如何理解js中的副作用

生活中的副作用:
生活中, 我们听到副作用第一反应就是吃药。老话说的好,是药三分毒,吃了药最怕就是有副作用,假设我们感冒了,吃感冒药好了,但是发现自己秃了,这个秃顶了就是副作用。

程序中的副作用:
在I/O模型中, 我们希望在I到O之间只有计算,如果在这个过程中还有其他的操作,我们把这种跟计算不相关的事情,都称之为副作用。
副作用这个词,听起来是模糊的,不可控制的,如果在I/O之间发生了一些我们不能控制、不可预估的事情,那么这个过程就会变的很复杂,测试过程也很复杂,整个程序就会一发不可控制,这样就违背了我们程序的原则。

无副作用:
与有副作用相对应的就是无副作用,我们感冒吃了感冒药就好了, 没有任何副作用,这样就是单一的I/O模型
就是我们输入,不依赖于我们除了传递参数之外的任何东西,这就是无副作用。

函数中的副作用:函数或者表达式在执行过程中对外部环境产生的可观察的变化,而不仅仅是一个返回值,简单来说,任何于外部环境产生交互的操作都可以被称之为副作用

函数式编程是基于没有副作用的一个前提,在这种范例下,副作用是排斥的。于是就有了纯函数:没有任何副作用
纯函数:满足以下两个条件
1、输入相同,输出也必须相同,且没有任何副作用的函数
2、纯函数不会修改外部状态或对外部产生任何影响

无副作用的函数

// 纯函数
function add(a, b) {return a + b;
}

有副作用的函数

// 有副作用的函数
let counter = 0;
function increment() {counter++; // 修改了外部变量
}
// 有副作用的函数
function getRandomNumber() {return Math.random(); // 每次调用结果不同
}

vue和react中的副作用:
副作用(Side Effects)在 Vue 中指的是会对外部世界产生影响的操作,Vue 提供了多种机制来管理和控制这些副作用。以下是

Vue 中副作用的全面解析:
1、数据获取(API 调用)
2、手动 DOM 操作
3、定时器设置
4、事件监听
5、全局状态修改
6、浏览器存储操作(localStorage 等)

vue中副作用的处理
1、组合式API中的副作用处理:
watch和watchEffect: 专门用来处理副作用的

watchEffect

import { watchEffect } from 'vue'
// 自动追踪响应式依赖
watchEffect(() => {console.log(state.count) // 自动追踪 state.count// 在这里执行副作用
})

特点:
1、自动追踪函数内部的所有依赖
2、立即执行回调函数
3、返回一个停止监听的函数

watch

import { watch } from 'vue'// 明确指定监听源
watch(() => state.count, // 监听源(newVal, oldVal) => {// 副作用逻辑},{ immediate: true, deep: true } // 选项
)

特点:
1、更精确的控制呀监听的数据
2、可以访问修旧值
3、提供可配置选项

2、生命周期中的副作用
vue中组件的生命周期也是处理副作用常见的位置:

import { onMounted, onUpdated, onUnmounted } from 'vue'export default {setup() {onMounted(() => {// 组件挂载时的副作用})onUpdated(() => {// 组件更新时的副作用})onUnmounted(() => {// 组件卸载时的清理工作})}
}

3、选项式API中的副作用

export default {data() {return { count: 0 }},watch: {count(newVal, oldVal) {// 监听 count 变化的副作用}},mounted() {// 组件挂载时的副作用},updated() {// 组件更新时的副作用},beforeUnmount() {// 组件卸载前的清理}
}

4、副作用的清理机制

watchEffect清理

const stop = watchEffect(() => {// 副作用逻辑
})// 手动停止监听
stop()

清理函数

watchEffect((onCleanup) => {const timer = setInterval(() => {// 定时器逻辑}, 1000)onCleanup(() => {clearInterval(timer) // 清理定时器})
})

生命周期中的清理

onMounted(() => {const handler = () => { /* 事件处理 */ }window.addEventListener('resize', handler)onUnmounted(() => {window.removeEventListener('resize', handler)})
})

5、异步中的副作用处理
直接用async和await来处理

// watchEffect
watchEffect(async () => {const data = await fetchData()state.value = data
})// watch
watch(() => state.id,async (newId) => {try {state.loading = truestate.data = await fetchData(newId)} finally {state.loading = false}}
)

为什么watch可以处理副作用
watch能够处理副作用的核心是因为它的设计理念和工作机制与vue的响应式系统深度集成
1、watch的定位就是为副作用而生的,watch设计是为响应式依赖变化时候执行副作用的工具,它的核心作用:
1.1、监听响应式数据的变化
1.2、在变化时候指定你定义的副作用逻辑(如 API调用、DOM操作等)

watch(() => state.count, (newVal, oldVal) => {// 这里就是副作用执行的位置fetch(`/api?count=${newVal}`) // 副作用示例:API调用document.title = `Count: ${newVal}` // 副作用示例:DOM操作}
)

2、vue响应式系统为watch提供基础
依赖追踪:当你在watch的第一个参数(getter函数)中访问响应式数据时,vue会自动建立依赖关系
变化追踪:当这些响应式数据变化时候,vue响应式系统会自动触发watch的回调函数

const state = reactive({ count: 0 })watch(() => state.count, // 访问响应式数据,建立依赖() => { /* 副作用 */ } // 当state.count变化时执行
)

3、watch提供了完善的副作用处理能力
清理机制: 通过onCleanup函数,可以在下次副作用执行之前或监听器停止之前,清理掉之前的副作用

watch(() => state.id,(newId, oldId, onCleanup) => {const timer = setTimeout(() => {fetchData(newId) // 副作用}, 1000)onCleanup(() => clearTimeout(timer)) // 清理}
)

执行机制:通过配置项精确控制副作用何时执行

watch(() => state.count,() => { /* 副作用 */ },{ immediate: true,  // 立即执行一次flush: 'post'     // DOM更新后执行}
)

4、与watchEffect相比
虽然两者都能处理副作用,但 watch 更适用于:
4.1、需要明确知道新旧值的场景
4.2、只关心特定数据的变化(而非自动追踪所有依赖)
4.3、需要更精细的执行控制(如 immediate、deep 等配置)

为什么computed中处理副作用
vue中computed设置是一个纯计算属性。强制要求:
1、必须返回一个返回值
2、不能包含异步操作
2.1、computed的核心定位是同步派生状态,它的值是应该立即被计算出来,异步操作会破坏这种即时性
2.2、vue的响应式系统需要再计算过程中同步收集依赖,如果是异步代码,vue无法确定那些数据应该被追踪为依赖
2.3、缓存失效问题:computed的缓存机制依赖同步计算。异步操作会导致缓存状态管理复杂化
3、不应该修改其他状态

computed的设定应该是一个纯函数,修改其他状态会引入隐式耦合

// 反例:计算属性中修改状态(代码难以维护)
const invalid = computed(() => {otherState.value = 'changed' // ❌ 副作用return state.value * 2
})

问题: 当invalid 被多次访问,otherState状态会被多次修改

避免无限更新循环:修改其他响应式数据可能触发连锁更新

const a = ref(1)
const b = computed(() => {a.value++      // ❌ 修改依赖项return a.value * 2
})
// 访问 b.value 会导致无限循环

可预测性要求:计算属性的值应该仅由它的依赖决定

// 正确示例:纯计算
const valid = computed(() => state.value * 2)

无论调用多少次,只要 state.value 不变,valid 的值就绝对一致

computed vs watch

特性computedwatch
返回值必须返回派生值无返回值
异步禁止允许
副作用禁止专门用于副作用
执行时机惰性计算(缓存)立即/响应式执行
用途模板使用的派生状态响应变化执行操作
http://www.dtcms.com/a/304737.html

相关文章:

  • 沪铝本周想法
  • docker docker与swarm入门笔记
  • 云原生周刊:2025年的服务网格
  • 【Linux基础知识系列】第七十篇 - 了解swap分区的概念
  • 【Linux篇】补充:消息队列和systemV信号量
  • 【图像噪点消除】——图像预处理(OpenCV)
  • 以太坊十年:智能合约与去中心化的崛起
  • 第5课 sort函数
  • Couchbase 详解
  • HBase 详解
  • 使用jQuery时的注意事项
  • Java 大视界 -- Java 大数据机器学习模型在金融信用评级模型优化与信用风险动态管理中的应用(371)
  • 每日学习笔记记录(分享更新版-凌乱)
  • 亚马逊欧洲站流量破局:多维策略重构与运营效能升级
  • 社区版 2025.7 | CQ-Mate V1.3发布啦!
  • python线性回归:从原理到实战应用
  • 广告投放平台:从痛点解决到高效管理的全解析
  • 数据库02 网页html01 day44
  • 浅析MCP (1)+ 【小智 AI 机器人MCP案例分析】
  • Python的垃圾回收机制
  • PyTorch 数据类型和使用
  • 【C++算法】72.队列+宽搜_二叉树的最大宽度
  • Qt 多线程数据库操作优化
  • 图像认知与OpenCV | Day5:图像预处理(4)
  • 关于 Apache Ignite 中 Job 调度(Job Scheduling)与冲突控制(Collision Control) 的机制说明
  • 嵌入式中间件-uorb解析
  • GC8870刷式直流电机驱动器详解:3.6A高功率PWM控制芯片
  • 中间件二进制部署文档
  • java导出pdf(使用html)
  • kotlin StateFlow的两个问题和使用场景探讨