数说
刀刀
8/8/2025
0 字
0 分钟
一面
Q:Vue3 ref 和 reactive 有什么区别?平时如何使用?
我的回答
reactive
给对象绑定响应式,通过proxy
代理实现;ref
通过new RefImpl
类实现,通过value
属性访问值。如果用ref
给对象绑定响应式,底层会帮我们换成reactive
。
Q:ref 为什么要 .value 来访问?
我的回答
ref
通过new RefImpl
类实现,通过value
属性访问值。所以要通过.value
才能访问到值。
Q:所以你的意思是,ref 底层分为两种情况,基本类型是get、set,对象类型是 reactive?
我的回答
是吧(我其实没听懂他在说啥)
Q:RefImpl 类和 Ref 有啥区别?
我的回答
RefImpl
类是Ref
的实现类,Ref 是一个函数接口,new RefImpl
类返回一个Ref
对象。
Q:key属性的作用是什么?
我的回答
key 是给每个节点一个唯一标识,
v-for
后续做 DOM 处理时,通过key
来判断是否是同一个节点,从而减少 DOM 操作。比如说标签不同,那么直接销毁创建新 DOM;如果是属性发生改变或者内容变化,那么就做对应修改。
Q:你说的是key一样的情况,那么key情况不一样如何操作?比如说一开始一个 DOM 的 key 是1,后面更新的时候 key 变成2,那么如何更新DOM?
我的回答
我理解是他认为是两个 DOM,会直接销毁创建新 DOM。
Q:如果不在 v-for 中使用 key,直接在一个组件上使用,有了解过么?
我的回答
我的理解是比如动态组件,如果
key
一样他会有一个缓存的效果。
Q:你是怎么能得知他被缓存了?
我的回答
不清楚。(面试官回复:组件的生命周期不一样)
Q:watchEffect是什么作用?
我的回答
watchEffect
会自动收集依赖,当依赖发生变化时,会重新执行回调函数。
Q:看一下这段代码,在业务上他做了啥?
我的回答
性别或年龄改变后,计算属性发生改变,然后调接口获取最新数据。
Q:如果性别和年龄多次改变,会多次调用接口么?为什么?
我的回答
不会,因为
watchEffect
内部会有判断,如果是同一个副作用函数,那么就不会重新执行。
Q:这段代码你觉得有什么问题?不考虑try catch报错,默认他不会报错。
我的回答
暂时没想到。
Q:那我来给一个问题吧,比如说我先改变了年龄为10岁,然后又修改了一次年龄为20次,然后会不会因为网络原因,导致最后20的返回速度比10快?
我的回答
会,我的处理方式是竞态取消。具体逻辑参考:
Q:你有了解过其他应对这种场景的实现方法么?
我的回答
我想到的是修改业务逻辑为点击保存按钮后再调接口;或者
fetch
有一个abort
方法,可以取消请求。
Q:你有了解过这个么?computedAsync。
我的回答
没有。
Deepseek 的回答
computedAsync
是 VueUse 库中一个强大的实用函数,它允许你创建基于异步计算的响应式引用(ref)。当需要处理异步操作(如 API 请求、定时器或任何返回 Promise
的操作)并将其结果绑定到响应式状态时,computedAsync
提供了优雅的解决方案。
基本用法:
import { computedAsync } from '@vueuse/core'
import { ref } from 'vue'
// 示例:异步获取用户数据
const userId = ref(1)
const userData = computedAsync(
async () => {
const response = await fetch(`https://api.example.com/users/${userId.value}`)
return response.json()
},
null, // 初始值
{
lazy: true, // 延迟执行直到被访问
evaluating: ref(false) // 跟踪加载状态
}
)
核心特性:
响应式依赖追踪
computedAsync
会自动追踪异步函数内部使用的响应式依赖,当这些依赖变化时,会自动重新执行异步计算:javascriptconst searchQuery = ref('') const page = ref(1) const searchResults = computedAsync( async () => { const response = await fetch( `https://api.example.com/search?q=${searchQuery.value}&page=${page.value}` ) return response.json() }, [] )
加载状态跟踪
通过
evaluating
选项可以轻松跟踪异步操作的状态:javascriptconst isLoading = ref(false) const userData = computedAsync( async () => { // 异步操作... }, null, { evaluating: isLoading } ) // 在模板中使用 <template> <div v-if="isLoading">Loading...</div> <div v-else>{{ userData }}</div> </template>
错误处理
computedAsync
不会自动处理错误,但你可以轻松添加错误处理逻辑:javascriptconst error = ref(null) const userData = computedAsync( async () => { try { const response = await fetch('/api/user') return response.json() } catch (err) { error.value = err return null } }, null )
配置选项:
选项 | 类型 | 默认值 | 描述 |
---|---|---|---|
lazy | Boolean | false | 是否延迟执行异步函数直到首次访问计算属性 |
evaluating | Ref | - | 用于跟踪异步操作状态的响应式引用 |
onError | Function | - | 错误处理回调函数 |
shallow | Boolean | false | 是否使用浅响应(不深度追踪嵌套对象) |
eventFilter | Function | - | 自定义事件过滤器,用于控制重新计算的频率 |
Q:看一下这段代码,说一下他想做什么?大概讲述一下怎么做?
我的回答
他要把多维数组保存为一维数组。思路是判断当前是否有 children,有的话扩展保存,没有的话直接保存。一直深度递归。
Q:说一下实现垂直居中的方式?
我的回答
- flex 布局
- position + transform
- position + top + bottom + margin
Q:了解过vertical-align么?
我的回答
了解,在修改图片和文字的基准线实现垂直居中效果
Q:了解tailwindcss么?
我的回答
有用过
Q:说一下他的优缺点
我的回答
- 优点:原子化、可复用、简洁、快速上手
- 缺点:学习成本、定制化程度低,多人协作开发时容易冲突
Q:你有了解 Uniapp 播放音频是 web 的能力还是原生的能力?
我的回答
我觉得是看在哪个端运行,如果是 web ,通过
audiocontext
上下文实现;如果是原生,通过原生能力实现。
Q:他那个全局唯一的背景音频上下文,你知道他是怎么实现的么?
我的回答
不清楚,我的理解是他内部会先判断当前是否存在,如果存在他内部就帮我们处理了。