跳转到内容

数说

刀刀

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 的回答

computedAsyncVueUse 库中一个强大的实用函数,它允许你创建基于异步计算的响应式引用(ref)。当需要处理异步操作(如 API 请求、定时器或任何返回 Promise 的操作)并将其结果绑定到响应式状态时,computedAsync 提供了优雅的解决方案。

基本用法:

javascript
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) // 跟踪加载状态
  }
)

核心特性:

  1. 响应式依赖追踪

    computedAsync 会自动追踪异步函数内部使用的响应式依赖,当这些依赖变化时,会自动重新执行异步计算:

    javascript
    const 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()
      },
      []
    )
  2. 加载状态跟踪

    通过 evaluating 选项可以轻松跟踪异步操作的状态:

    javascript
    const isLoading = ref(false)
    
    const userData = computedAsync(
      async () => {
        // 异步操作...
      },
      null,
      {
        evaluating: isLoading
      }
    )
    
    // 在模板中使用
    <template>
      <div v-if="isLoading">Loading...</div>
      <div v-else>{{ userData }}</div>
    </template>
  3. 错误处理

    computedAsync 不会自动处理错误,但你可以轻松添加错误处理逻辑:

    javascript
    const 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
    )

配置选项:

选项类型默认值描述
lazyBooleanfalse是否延迟执行异步函数直到首次访问计算属性
evaluatingRef-用于跟踪异步操作状态的响应式引用
onErrorFunction-错误处理回调函数
shallowBooleanfalse是否使用浅响应(不深度追踪嵌套对象)
eventFilterFunction-自定义事件过滤器,用于控制重新计算的频率

Q:看一下这段代码,说一下他想做什么?大概讲述一下怎么做?

这段代码

我的回答

他要把多维数组保存为一维数组。思路是判断当前是否有 children,有的话扩展保存,没有的话直接保存。一直深度递归。

Q:说一下实现垂直居中的方式?

我的回答

  1. flex 布局
  2. position + transform
  3. position + top + bottom + margin

Q:了解过vertical-align么?

我的回答

了解,在修改图片和文字的基准线实现垂直居中效果

Q:了解tailwindcss么?

我的回答

有用过

Q:说一下他的优缺点

我的回答

  • 优点:原子化、可复用、简洁、快速上手
  • 缺点:学习成本、定制化程度低,多人协作开发时容易冲突

Q:你有了解 Uniapp 播放音频是 web 的能力还是原生的能力?

我的回答

我觉得是看在哪个端运行,如果是 web ,通过 audiocontext 上下文实现;如果是原生,通过原生能力实现。

Q:他那个全局唯一的背景音频上下文,你知道他是怎么实现的么?

我的回答

不清楚,我的理解是他内部会先判断当前是否存在,如果存在他内部就帮我们处理了。