一、技术栈与编码能力(10min)
1. Vue 3 & Composition API
Q1:请解释一下 ref
和 reactive
的区别?你在项目中是如何使用的?
答:ref是包装一个原始值或对象,通过.value访问,reactive是对对象的深度响应式处理,不能用于原始值。 在模板中使用,ref会自动解包,不需要写.value,而reactive直接访问属性即可。
加分点:toRefs的作用及如何在组合函数中使用它。提到<script setup>中自动解包机制。
toRefs是Vue3组合式API中的一个工具函数。当解构reactive创建的响应式对象时,直接解构会失去响应式,toRefs可解决这个问题。toRefs用于将响应式对象的每个属性转换为独立的ref,保持属性的响应式并避免解构时丢失响应式。toRefs转换后的属性是ref。需通过.value访问值,toRefs只能处理reactive创建的对象。
组合函数返回的状态:return toRefs(reactiveObj)
Q2:<script setup>
和传统 <script>
有什么不同?它的优势和局限是什么?
答:<script setup>是vue3的语法糖,简化了组合式API的使用,优势是不需要显式的setup()函数,默认导出变量为组件的公开API ,自动引入defineProps、defineEmits等编译器宏。不利于代码复用(可结合自定义的Hook解决)
Q3:Vue 3 中的响应式原理和 Vue 2 有何不同?
答:vue2使用Object.defineProperty实现响应式,只能拦截对象已有属性的变化,无法检测新增、删除属性。通过data()函数返回对象,无法单独暴露响应式对象,必须依赖组件实例,通过this访问。不支持Map、Set等ES6集合类型 vue2中的map不是响应式的
Vue3用Proxy代理整个对象,动态拦截所有操作(包括新增、删除属性,数组索引修改),支持深层响应式。使用ref和reactive创建独立响应式变量,可将响应式状态和逻辑提取到组合函数中。map是响应式的,需用ref和reactive包裹进行显示转换(const map = reactive(new Map())
功能 | Vue 2 | Vue 3 |
---|---|---|
创建响应式对象 | data() + Vue.observable() | reactive() / ref() |
新增响应式属性 | Vue.set() | 直接赋值(Proxy 自动支持) |
删除响应式属性 | Vue.delete() | 直接 delete |
数组响应式 | 重写方法(如 push ) | 原生支持索引修改 |
响应式工具 | 无 | toRefs 、toRef 、isReactive |
2. TypeScript
Q4:你是如何组织 /types/
下的类型的?有使用过 type 还是 interface
?为什么?
答:通常将接口模型放在/types下,按模块分目录结构
使用interface定义对象结构,方便扩展和继承,使用type定义联合类型,交叉类型、泛型等复杂结构
泛型:用类型变量(如<T>)表示动态类型,T由调用时决定。
联合类型:允许一个变量或参数同时支持多种类型,通过 | 符号连接,扩展类型的灵活性。
交叉类型:用 & 符号将多个类型合并为一个包含所有成员的新类型,“与”的关系,一个值满足多个类型的约束。
-
3. 组件通信与复用
Q5:举个例子说明你使用过 provide/inject
或 Pinia/Vuex
的场景。
provide/inject
:用于跨层级传递主题配置、语言设置等上下文信息。
// 父组件
provide('theme', 'dark');// 子组件
inject('theme');
Pinia
:用于全局状态管理,如用户登录状态、收藏学校列表。
const userStore = useUserStore();
userStore.login();
二、项目架构与工程实践(10min)
4. 模块划分与组织结构
Q7:请介绍一下项目的整体结构(如 /pages
, /components
, /stores
, /types
等目录的作用)。
答:
/pages
:页面级组件,对应路由。/components
:通用组件库,如按钮、标签、导航栏等。/stores
:状态管理模块,使用 Pinia。/types
:类型定义文件,按模块划分。/common
:公共工具类和网络请求封装。
5. 接口调用与网络层设计
Q8:你是如何封装网络请求模块的?是否统一处理了错误、拦截器、Token 刷新?
- 使用 Axios 或 UniApp 原生
uni.request
封装了一个统一的http.ts
。 - 添加了请求拦截器(添加 Token)、响应拦截器(统一错误提示)。
- 错误码集中处理(如 401 登录失效跳转登录页)。
- Token 刷新机制(如使用 refresh token)。
6. 状态管理
Q9:项目中使用的是 Pinia 吗?为什么选择它而不是 Vuex?
- 优点:
- 更简洁的 API,无需
mutations
。 - 支持模块化、命名空间。
- 更好的 TypeScript 支持。
- 性能更优,体积更小。
- 更简洁的 API,无需
Vuex需要定义mutation/actions/getters,基于Vue2的响应式系统
Pinia直接使用Composition API风格,无需mutations,直接通过actions修改状态,基于Vue3的响应式系统(Proxy实现),按需加载store ,仅支持Vue3
三、性能与调试(5min)
7. 性能优化
Q10:你是如何做页面加载优化的?比如懒加载、骨架屏、分包等。
- 使用 Vue 的异步组件实现懒加载。
- 页面首屏采用骨架屏减少白屏时间。
- 使用 uni.preloadPages 实现页面预加载。
- 对大型功能模块进行分包(subpackages)。
- 图片懒加载、CDN 加速。
四、协作与测试(5min)
8. Git 协作流程
Q11:团队中是如何进行 Git 分支管理的?有使用 PR 流程吗?
答:有一个主分支develop ,再每个人建立自己的功能分支,开发完成后提交pr,合并,处理冲突
Q12:有没有使用过自定义 Hook 来封装逻辑复用?
答:有,在多个组件中复用筛选、排序、搜索等功能,使用自定义的Hook.
function useFilter(list) {const filteredList = computed(() => filterLogic(list.value));return { filteredList };
}
Hook(钩子)是编程中的一种拦截或扩展程序执行的流程技术,允许开发者在特定阶段插入自定义的代码,不同场景Hook的实现和用途不同