vue2-computed、JS事件循环、try/catch、响应式依赖追踪知识点整理

需求点:我需要根据某些条件下去异步请求判断某个计算属性返回值

最终进行拆分

js 复制代码
export default {
	data(){
		// 放置需要异步赋值的变量
		formId: null,
		allPerms: [],
		userDept: null,
		isLoading: true,
	},
	computed: {
		rootNode(){
			// 初始化:isLoading true
			if(this.isLoading) return []
			// 计算属性只做同步逻辑
			const {	hierarchicalByCurrentDepart } = this.SdFormItemContext?.field?.attr || {}
			if(hierarchicalByCurrentDepart === 'true'){
				const authorityIds = (this.$attrs.authorityIds || '').split(',').map(item => item.trim())
				const hasMatch = authorityIds.some(item => this.allPerms.includes(item))
				if(hasMatch){
					return []
				}else{
					return this.userDept ? [this.userDept] : []
				}
			}else{
				return []
			}
		}
	},
	mounted(){
		// 同步 初始化用户部门信息
    const t = getUserInfo()
    this.userDept = {
      code: t.deptId.toString(),
      id: t.deptId.toString(),
      name: t.deptName
    }
    // 处理异步逻辑
		this.initFormIdAndPerms()
	},
	methods: {
		async initFormIdAndPerms(){
			try{
				// 等待DOM执行完毕
				await this.$nextTick()
				this.initFormId()
				if(this.formId && this.$attrs.authority === '1'){
					await this.fetchPerms()
				}
			}catch(error){
				console.error(error)
			}finally{
				this.isLoading = false
			}
		},
		initFormId(){
			if(/某条件/){
				this.formId = 'XXXXX'
			}
		},
		async fetchPerms(){
			const res = await axios({
				url: '',
				method: 'get',
				params: {formId: this.formId}
			})
			this.allPerms = res.data
		}
	}
}

Vue组件初始化&&异步执行

  • 组件初始化:创建 datacomputed 依赖关系
    • data 被初始化响应式对象:isLoading:true, allPerms: true
    • computed计算属性 rootNode 执行第一次
    • 读取 this. isLoading 为true 返回 []
    • Vue 记录依赖 :rootNode 依赖 isLoading 和 allPerms
  • 执行mounted钩子
  • 进入 initFormIdAndPerms() 异步流程
    • await this.$nextTick() → 加入微任务队列,等待 DOM 更新
    • 同步代码执行完毕,事件循环开始处理微任务
    • $nextTick 回调执行 → 调用 initFormId() 获取 formId
    • 如果有权限且 formId 存在,调用 fetchPerms()
      • await axios.get() → 发送请求,加入宏任务队列
  • 网络请求完成,axios 回调执行
    • 拿到后端返回的权限数据,赋值给 this.allPerms
    • Vue 检测到 allPerms 变化 → 触发 computed 重新执行
    • 此时 isLoading 仍为 true,所以计算属性还是返回 []
  • 进入 finally 块
    • 执行 this.isLoading = false
    • Vue 检测到 isLoading 变化 → 再次触发 computed 重新执行

关键点

  • 初始计算属性执行:组件初始化就执行了,此时还没有异步数据
  • 事件循环的异步执行:$nextTick 和 axios,等同步代码执行后再处理异步操作
  • 响应式更新触发:当 isLoading 或 allPerms 变化时,Vue 会自动重新执行计算属性,这就是 computed 能拿到最新值的核心原理。
  • finally 的保障作用:无论异步操作成功还是失败,finally 块都会执行,确保 isLoading 被设置为 false,避免界面一直处于加载状态。
bash 复制代码
async initFormIdAndPerms() {
  await this.$nextTick() // 等待 DOM 更新完成,再执行后面的代码
  this.initFormId()
  if (this.$attrs.authority === '1' && this.formId) {
    await this.fetchPerms() // 等待接口请求完成,再执行后续逻辑
  }
}
  • async/await:让异步代码 "变同步",
  • async :加在函数前面,标记这个函数是异步函数 ,它的返回值会自动变成一个 Promise。
  • await :只能用在 async 函数里,作用是等待后面的 Promise 执行完成,再继续往下走。
  • 如果没有await,initFormId可能会在DOM没更新时执行,导致拿不到实例对象
  • await this.fetchPerms()确保接口请求返回数据后,再执行下一步,避免数据没拿到就执行计算属性

javascript事件循环

JavaScript 是单线程语言 (同一时间只能做一件事),但为了处理异步操作如网络请求、定时器、DOM 事件),设计了事件循环机制。

  • 执行栈:同步代码执行的地方,遵循"先进后出"
  • 任务队列:存放异步任务的回调函数
    • 宏任务:script 整体代码、setTimeout/setInterval、AJAX 请求、DOM 事件、setImmediate(Node 环境)。
    • 微任务:Promise.then/catch/finally、async/await(本质是 Promise 语法糖)、queueMicrotask、process.nextTick(Node 环境,优先级最高)。
  • 事件循环规则
    • 先执行执行栈的同步代码
    • 同步代码执行完后,清空所有微任务队列,按顺序执行
    • 执行一个宏任务,执行完后再次清空所有微任务队列
    • 重复以上步骤

try catch异常捕获

  • try catch用于捕获代码执行过程中的同步异常避免程序崩溃
  • try catch无法捕获异步异常,比如setTimeout,需在异步回调内部单独捕获

async await

  • async/await 是 ES2017 推出的Promise 语法糖让异步代码看起来像同步代码,更易读、易维护。
  • async:修饰函数,使函数返回一个 Promise 对象(即使函数内部返回普通值,也会被包装成 Promise.resolve (值))
  • await:只能在 async 函数内部使用,等待 Promise 状态变为 resolved/rejected:
  • 若 Promise 成功(resolved),await 返回 Promise 的结果。
  • 若 Promise 失败(rejected),await 会抛出异常,需用 try/catch 捕获。

事件循环与 Vue 的 computed/watch 结合的核心规则:

更新时机:computed 重新计算、watch 回调执行都属于微任务,在同步代码执行完毕后才会触发。

执行顺序:同步代码 → 普通微任务(Promise.then) → Vue 微任务(computed/watch) → 宏任务(setTimeout / 接口请求)。

实际开发技巧:
想要获取修改响应式数据后的最新 computed 值 / 更新后的 DOM,必须用 nextTick 等待微任务执行。
避免在同步代码中频繁修改响应式数据(Vue 会批量处理微任务,优化性能)。

watch 回调内的异步操作(宏任务)会在所有微任务执行完后才执行,不会阻塞当前的响应式更新。

watch 能写 await 异步请求、computed 不能的核心原因:

  • 设计初衷:computed 是 "同步计算派生值" 的纯函数,必须立即返回值;watch 是 "处理数据变化副作用的监听器,无返回值要求,可执行异步。
  • 底层逻辑:computed 的缓存、依赖收集依赖同步执行,异步返回的 Promise 无法参与响应式系统watch仅触发回调,不参与值的派生,异步不影响核心逻辑
  • 最佳实践:watch 负责异步请求、数据赋值等副作用,computed 负责基于已有响应式数据的同步格式化 / 计算,两者配合是 Vue异步场景的标准写法。
相关推荐
郝学胜-神的一滴2 小时前
机器学习特征选择:深入理解移除低方差特征与sklearn的VarianceThreshold
开发语言·人工智能·python·机器学习·概率论·sklearn
多多*2 小时前
计算机网络相关 讲一下rpc与传统http的区别
java·开发语言·网络·jvm·c#
码农水水2 小时前
阿里Java面试被问:Online DDL的INSTANT、INPLACE、COPY算法差异
java·服务器·前端·数据库·mysql·算法·面试
Coder_Boy_2 小时前
基于SpringAI的在线考试系统-知识点管理与试题管理模块联合回归测试文档
前端·人工智能·spring boot·架构·领域驱动
小旭95272 小时前
【Java 基础】IO 流 全面详解
java·开发语言
吃吃喝喝小朋友2 小时前
JavaScript事件
开发语言·前端·javascript
ONExiaobaijs2 小时前
Java jdk运行库合集
java·开发语言·python
wangjialelele2 小时前
二刷C语言后,一万字整理细碎知识点
c语言·开发语言·数据结构·c++·算法·cpp
EEEzhenliang2 小时前
CSS的注释
前端·css