Skip to content

百度

核心问题与回答

Q1:请简单介绍一下自己?

  • 我是百度文库的前端开发工程师,主要负责APP内嵌H5页面的开发,技术栈主要是Vue2和Vue3,也有少量项目使用React
  • 自我介绍:23年毕业于xxx理工学院,有3年前端开发经验,目前在xxx科技公司负责智慧停车业务线的前端开发工作,搭建了包含C端H5小程序和B端管理系统的智慧停车管理平台
  • 同时是uniapp开源组件库Wot UI的团队成员,参与开发了高拓展性的瀑布流组件和通用型脚手架

复盘后的标准回答:

  • 面试官你好,我叫XX,23年毕业于XX理工学院软件工程专业,有3年前端开发经验
  • 目前就职于XX科技公司,负责智慧停车业务线的前端开发,技术栈以Vue2/Vue3为主,也有React的实践经验
  • 工作期间搭建了覆盖C端H5小程序和B端管理系统的智慧停车管理平台,包含停车缴费、欠费追缴、设备运维等核心模块
  • 同时是uniapp开源组件库Wot UI的核心贡献者,主导开发了高拓展性的瀑布流组件和通用型脚手架,对组件库设计和工程化建设有较深理解

Q2:请说一下盒子模型?

  • 盒子模型主要有BFC、IFC和FFC布局
  • BFC会创建一个独立的空间,不受外部环境影响。触发BFC的条件包括:overflow:hidden/auto、绝对定位等
  • HTML的body标签也是一个BFC

复盘后的标准回答:

  • 盒子模型分为标准盒模型(content-box)和IE盒模型(border-box)
  • 标准盒模型:width/height 只包含 content,不包含 padding 和 border
  • IE盒模型:width/height 包含 content、padding、border
  • 通过 box-sizing 属性切换,项目中常用 border-box 更方便布局计算
  • 关于BFC(块级格式化上下文):它是一个独立的渲染区域,内部元素不影响外部
  • 触发BFC的条件:overflow: hidden/auto、浮动、绝对定位、display: inline-block/flex/grid
  • 应用场景:清除浮动、防止margin合并、自适应两栏布局

Q3:请解释一下弹性布局?

  • 弹性布局(Flex布局)是现在常用的布局方式,取代了传统的浮动布局
  • Flex布局有主轴和侧轴,主轴属性有justify-content(居中、space-between等),侧轴属性有align-items(start、center、end等)
  • 其他属性包括flex-grow、flex-shrink、flex-basis用于设置伸缩基准,order用于子元素排序,gap用于设置间距

复盘后的标准回答:

  • Flex布局是一维布局模型,适用于主轴方向的排列,是最常用的现代布局方案
  • 容器属性:flex-direction(主轴方向)、justify-content(主轴对齐)、align-items(侧轴对齐)、flex-wrap(换行)、gap(间距)
  • 项目属性:flexflex-growflex-shrinkflex-basis 的简写
  • flex: 1 等价于 flex-grow: 1; flex-shrink: 1; flex-basis: 0%,表示等分剩余空间
  • order 属性可以改变项目排列顺序,默认值为0
  • 相比浮动布局,Flex布局无需清除浮动,语义更清晰,控制更灵活

Q4:如何实现一个父元素内两个子元素水平垂直居中,其中左边子元素固定宽高100×100,右边子元素宽度自适应,高度100?

  • 父元素开启flex布局,设置justify-content:center和align-items:center
  • 左边子元素设置固定宽高100×100
  • 右边子元素设置flex:1,高度100

复盘后的标准回答:

  • 推荐使用Flex布局实现:
    • 父容器:display: flex; align-items: center;(垂直居中)
    • 左子元素:width: 100px; height: 100px; flex-shrink: 0;(固定宽高,防止被压缩)
    • 右子元素:flex: 1; height: 100px;(宽度自适应)
  • 如果不需要水平居中,父容器可以不加 justify-content: center,因为左右子元素已经自然排列
  • 注意 flex-shrink: 0 防止左子元素被压缩,这在移动端窄屏场景下很重要

Q5:实现一个四宫格布局有哪些方法?

  • 方法一:使用flex布局,设置每个元素宽高为1/2,开启flex-wrap实现自动换行
  • 方法二:使用grid布局,设置两列两行,现代浏览器基本都支持grid布局

复盘后的标准回答:

  • 方法一:Flexbox + wrap
    • 父容器:display: flex; flex-wrap: wrap;
    • 子项:width: 50%;flex: 0 0 50%;
    • 优点:兼容性好,简单直观
    • 缺点:如果需要严格2×2排列,需要控制每行刚好两个
  • 方法二:CSS Grid(推荐)
    • 父容器:display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr;
    • 无需设置子项宽度,更简洁
    • 优点:二维布局天然支持,代码更少,语义更强
    • 现代浏览器(Chrome、Firefox、Safari、Edge)均已支持,生产环境可放心使用
  • 方法三:传统float布局(了解即可,不推荐)
    • 父容器清除浮动,子项 float: left; width: 50%;

Q6:除了px单位外,还用过哪些单位?

  • 小程序常用rpx,H5可以用rem
  • 字体单位有em
  • 响应式布局常用vw、vh、dvh、dvw等
  • 移动端还有vmin、vmax

复盘后的标准回答:

  • 相对单位:
    • em:相对于父元素的字体大小,适合用于按钮、卡片等局部组件的间距和字体
    • rem:相对于根元素(html)的字体大小,适合做整体适配方案
    • %:相对于父元素的百分比,布局中常用
  • 视口单位:
    • vw/vh:相对于视口宽高的1%,适合做全屏自适应
    • dvh/dvw:动态视口单位,解决了移动端浏览器地址栏显示/隐藏导致的视口变化问题
    • vmin/vmax:取 vw 和 vh 中较小/较大的值
  • 移动端特有:
    • rpx:微信小程序单位,按750设计稿换算,1rpx = 屏幕宽度/750
  • 适配方案推荐:
    • H5项目:使用 vw/vh + rem 组合方案
    • 小程序:使用 rpx
    • 组件库开发:使用 em 做相对缩放

Q7:Vue中如何保证样式隔离?

  • 在style标签上添加scoped属性实现样式隔离
  • Vue3会在编译时生成data属性加上文件哈希值,每个组件的哈希值不同,通过属性选择器实现样式隔离

复盘后的标准回答:

  • Scoped方案(最常用):
    • <style scoped> 编译时会为组件所有元素添加一个唯一的 data-v-xxx 属性
    • CSS选择器会加上属性选择器,如 .foo[data-v-xxx],实现样式隔离
    • 缺点:父组件无法穿透修改子组件样式(除非使用 :deep()
  • CSS Modules:
    • 通过编译生成唯一的类名,如 .button_a3b4c,类名不冲突
    • 在 Vue 中通过 $style 对象或 useCssModule 使用
  • CSS-in-JS:
    • 运行时生成唯一类名,样式和组件逻辑绑定在一起
    • Vue 中不常用,更多见于 React 生态
  • BEM命名规范:
    • 通过约定式命名(Block__Element--Modifier)避免冲突
    • 适合大型项目,但需要团队规范约束
  • 穿透选择器(需要修改子组件样式时):
    • Vue2:>>>/deep/
    • Vue3:::v-deep() 或简写 :deep()

Q8:JavaScript的数据类型有哪些?

  • 基本类型:number、boolean、string、undefined、null、symbol
  • 引用类型:object(包括function、array等)

复盘后的标准回答:

  • 基本类型(7种): numberstringbooleanundefinednullsymbolbigint
    • 基本类型存储在栈内存中,值不可变
    • 注意 typeof null === 'object' 是历史遗留bug
    • symbol 用于创建唯一标识符,常用于对象私有属性
    • bigint 用于表示大于2^53的整数,后缀加 n
  • 引用类型: object
    • 包括普通对象、arrayfunctionDateRegExpMapSet
    • 引用类型存储在堆内存中,变量保存的是内存地址
    • 赋值时是引用传递,修改会影响原对象
  • 关键区别:
    • 基本类型比较的是值,引用类型比较的是引用地址
    • 基本类型使用 === 直接比较,引用类型需要深比较

Q9:如何判断一个数据的类型?

  • 使用typeof
  • 使用instanceof
  • 通过原型方法判断,检查对象的原型是否是某个类的原型

复盘后的标准回答:

  • typeof 适合判断基本类型
    • typeof 123 // 'number'
    • 局限性:typeof null // 'object'typeof [] // 'object'
  • instanceof 判断对象是否是某个构造函数的实例
    • [] instanceof Array // true
    • 原理:检查原型链
    • 局限性:不同iframe或window之间的实例会失效
  • Object.prototype.toString.call()(最准确):
    • Object.prototype.toString.call([]) // '[object Array]'
    • Object.prototype.toString.call(null) // '[object Null]'
    • 可以准确判断所有内置类型,推荐在工具函数中使用
  • Array.isArray() 专门判断数组
  • constructor 通过 xxx.constructor.name 获取构造函数名

Q10:请解释一下闭包?

  • 闭包是指函数返回另一个函数,且内部函数引用了外部函数的变量,形成闭包
  • 不当使用闭包可能导致内存泄漏
  • 合理使用闭包可以实现私有变量等功能
  • 工作中常用于防抖节流等场景

复盘后的标准回答:

  • 定义: 闭包是指一个函数引用了其外层作用域中的变量,即使外层函数已执行完毕,这些变量仍然保存在内存中
  • 形成条件: 函数嵌套 + 内部函数引用外部变量
  • 应用场景:
    • 防抖节流: 利用闭包保存定时器ID
    • 私有变量: 模拟私有属性,如 function createCounter() { let count = 0; return () => ++count; }
    • 柯里化: 固定部分参数,返回接受剩余参数的函数
    • 单例模式: 利用闭包保存实例引用
    • 循环中保存索引: 经典 var 问题,用闭包或 let 解决
  • 内存管理:
    • 闭包会导致外部变量无法被垃圾回收,需注意及时释放
    • 在不需要时,将引用置为 null 解除闭包

Q11:请手写一个深拷贝的实现?

  • 首先判断是否是基本类型,是则直接返回
  • 判断是否是特殊类型(函数、正则、日期等),需要特殊处理
  • 处理集合类型(Map、Set)
  • 处理数组和对象,递归拷贝所有属性
  • 使用属性描述符拷贝自有属性,可以处理Symbol等特殊属性

复盘后的标准回答:

  • 生产环境推荐: JSON.parse(JSON.stringify(obj))
    • 简单快速,但无法处理函数、undefined、symbol、循环引用、特殊对象(Date、RegExp、Map、Set等)
  • 手写完整版深拷贝:
javascript
function deepClone(target, map = new WeakMap()) {
  // 基本类型直接返回
  if (typeof target !== 'object' || target === null) return target;

  // 处理循环引用
  if (map.has(target)) return map.get(target);

  // 处理特殊类型
  const Ctor = target.constructor;
  if (Ctor === Date) return new Date(target);
  if (Ctor === RegExp) return new RegExp(target);
  if (Ctor === Map) {
    const result = new Map();
    map.set(target, result);
    target.forEach((val, key) => result.set(key, deepClone(val, map)));
    return result;
  }
  if (Ctor === Set) {
    const result = new Set();
    map.set(target, result);
    target.forEach(val => result.add(deepClone(val, map)));
    return result;
  }

  // 处理数组和对象
  const result = Array.isArray(target) ? [] : {};
  map.set(target, result);

  // 拷贝所有属性(包括Symbol)
  Reflect.ownKeys(target).forEach(key => {
    result[key] = deepClone(target[key], map);
  });

  return result;
}
  • 要点: 使用 WeakMap 处理循环引用,防止内存泄漏;使用 Reflect.ownKeys 可遍历 Symbol 属性;针对不同内置类型分别处理

Q12:Vue3的组件通信方式有哪些?

  • props/emit父子组件通信
  • 通过class等属性传递
  • 作用域插槽
  • provide/inject跨层级通信
  • 获取组件实例通信
  • 使用组合式函数或状态管理库(如Pinia)共享数据
  • 路由传参

复盘后的标准回答:

  • 父子组件通信:
    • props 父传子,emit 子传父(最基础方式)
    • v-model 双向绑定,Vue3支持多个v-model
    • $attrs 透传属性(class、style、事件监听器等)
  • 跨层级通信:
    • provide / inject:祖先组件提供数据,后代组件注入,适合主题、用户信息等全局状态
    • 注意:provide 默认不是响应式,需传入 refreactive
  • 插槽通信:
    • 作用域插槽 v-slot:子组件向父组件暴露数据,父组件决定渲染内容
    • 适合列表项、表格列等需要自定义渲染的场景
  • 实例通信:
    • $refs 获取子组件实例,调用其方法或访问属性
    • defineExpose(Vue3)显式暴露子组件的内容
  • 全局状态管理:
    • Pinia(推荐)或 Vuex:适合复杂的状态共享
    • 组合式函数(Composables):利用全局ref共享状态,轻量级方案
  • 路由传参:
    • queryparamsstate 传参

Q13:为什么选择Pinia而不是Vuex?

  • Vuex使用起来较繁琐,有mutation(不支持异步)和action需要配合使用
  • Pinia更简单扁平,只有state和action,且action支持异步
  • Pinia对TypeScript支持更友好

复盘后的标准回答:

  • API简洁度: Pinia 移除了 Vuex 的 mutation,只有 state、getter、action,action 同时支持同步和异步
  • TypeScript支持: Pinia 天然支持 TS,无需额外类型定义;Vuex 的 TS 支持较为繁琐
  • 模块化: Pinia 每个 store 独立定义,不需要像 Vuex 那样注册 modules,天然支持代码分割
  • DevTools: Pinia 支持 Vue DevTools,提供时间线调试、状态快照等功能
  • 体积: Pinia 体积约 1KB,比 Vuex 更轻量
  • 总结: Pinia 是 Vue 官方推荐的状态管理方案,Vuex 将不再有新的版本更新

Q14:Vue2和Vue3的响应式有什么区别?

  • Vue2使用Object.defineProperty,只能监听自有属性,需要重写数组方法
  • Vue3使用Proxy实现全对象代理,可以监听新增/删除属性
  • Vue3通过ref和reactive实现响应式,底层使用WeakMap建立依赖关系

复盘后的标准回答:

  • 实现原理差异:
    • Vue2:Object.defineProperty 递归遍历对象每个属性,设置 getter/setter
    • Vue3:Proxy 直接代理整个对象,拦截所有操作(增删改查)
  • 关键能力差异:
    • Vue2 无法检测「属性新增/删除」,需使用 Vue.set/delete
    • Vue2 无法检测「数组索引/长度变更」,需重写数组方法(push、pop 等7种)
    • Vue3 通过 Proxy 天然支持所有变更,无需额外处理
  • 性能优化:
    • Vue2 初始化时递归遍历所有属性,对象深度大时性能开销大
    • Vue3 使用 WeakMap 建立依赖关系,按需收集依赖,惰性监听
    • Vue3 的 ref 用于基本类型,reactive 用于对象类型,分工更清晰
  • 响应式API变化:
    • Vue2:data 选项中定义的数据自动响应式
    • Vue3:显式使用 ref() / reactive() 声明,toRefs() 保持解构后响应式

Q15:Vue3还有哪些优化?

  • 架构改为monorepo,源码体积更小
  • 更好的Tree Shaking支持
  • 编译优化:静态提升、预字符串化、patch flag标记静态节点
  • diff算法从双端diff改为快速diff
  • 组合式API更灵活
  • v-if和v-show优先级与Vue2相反

复盘后的标准回答:

  • 架构优化:
    • 采用 monorepo 架构,将核心功能拆分为独立包(reactivity、runtime-core、compiler-core 等),按需引入
    • 更好的 Tree Shaking 支持,未使用的API不会打包到最终产物中
  • 编译优化:
    • 静态提升(HoistStatic): 将静态节点提升到渲染函数外部,避免重复创建
    • 预字符串化(Pre-stringify): 连续静态节点合并为字符串,减少VNode创建
    • Patch Flag: 标记动态节点(如 text、class、props 有变化),diff时只对比有标记的节点,跳过静态内容
    • 缓存事件处理函数: 避免重复创建事件回调
  • Diff算法优化:
    • Vue2 使用双端 diff(4个指针交叉对比)
    • Vue3 使用快速 diff(基于最长递增子序列,移动DOM节点更少)
  • Composition API: 逻辑组合更灵活,相比 Options API 复用性更强
  • v-if / v-show 优先级: Vue2 中 v-if 优先级高于 v-for,Vue3 相反

Q16:请介绍一下你参与的wot UI组件库?

  • 主要负责瀑布流组件和全局提示(toast)组件
  • 瀑布流组件解决了uniapp社区现有组件在页面切换时布局异常的问题
  • 采用父子解耦架构,父组件维护列高和坐标,子组件通过作用域插槽支持任意内容
  • 实现了异步队列和超时熔断机制,支持并行加载串行排版
  • 做了增量更新优化,避免全部重排
  • toast组件实现了状态和UI分离,支持在纯JS文件中调用

复盘后的标准回答:

  • 我是 Wot UI 组件库的核心贡献者,主要负责瀑布流(Waterfall)组件和 Toast 组件的开发
  • 瀑布流组件设计亮点:
    • 采用父子解耦架构:父组件 wot-waterfall 维护列高和坐标计算,子组件 wot-waterfall-item 通过作用域插槽支持任意内容渲染
    • 异步队列 + 超时熔断机制:支持图片等资源并行加载、串行排版,设置超时阈值防止一个失败项阻塞整体
    • 增量更新优化:新增数据时只对新项进行排版,不触发全部重排,性能大幅提升
    • 解决了 uniapp 社区现有组件在页面切换时布局异常(布局错乱、白屏)的痛点
  • Toast 组件设计亮点:
    • 状态和 UI 分离:通过函数式调用(如 Toast.show()),支持在纯 JS 文件中使用,不依赖组件上下文
    • 支持队列管理,多个 Toast 依次显示
  • 技术收获: 深入理解了组件库设计模式、虚拟列表渲染优化、跨端兼容处理等

Q17:请介绍一下社区知识问答系统项目?

  • 主要负责对话的流式渲染
  • 后端处理用户提问优化,通过向量数据库匹配最相似答案
  • 前端负责将返回结果进行流式渲染展示

复盘后的标准回答:

  • 这是一个基于 RAG(检索增强生成)的社区知识问答系统
  • 整体架构:用户提问 → 后端优化问题 → 向量数据库检索最相似答案 → 大模型生成回答 → 前端流式渲染
  • 我主要负责前端部分:
    • 流式渲染: 使用 Fetch API 的 ReadableStream 或 SSE 接收后端逐块返回的数据,实现打字机效果
    • 状态管理: 管理流式输出的中间状态,支持暂停/继续/取消等操作
    • Markdown 渲染: 对 AI 返回的 Markdown 格式内容进行实时渲染,支持代码块高亮、公式等
  • 技术难点:流式渲染过程中如何处理 Markdown 的渐进式解析(如代码块未闭合时的展示策略)

Q18:你有什么问题想问我们?

  • 询问岗位具体工作内容(百度文库相关业务)
  • 询问团队规模和配置(前端团队约五六十人)
  • 寻求成长性建议(建议放松心态,准备好技术基础)

复盘后的标准回答:

  • 关于业务: 请问这个岗位主要负责哪些业务线?是百度文库的核心功能迭代还是新业务的探索?
  • 关于团队: 团队目前的规模和分工是怎样的?前端团队的前后端协作模式和后端如何配合?
  • 关于技术栈: 团队目前使用的技术栈是什么?有没有计划迁移到更新的技术?
  • 关于成长: 公司对新人的培养机制是怎样的?有没有技术分享或 Code Review 机制?
  • 关于期望: 对于这个岗位的候选人,最看重的三个能力是什么?
  • 注意: 提问时避开面试官已透露的信息,表现出对业务和团队的真实兴趣

访谈总结

本次面试全面考察了候选人的前端技术能力,包括CSS布局、JavaScript基础、Vue框架原理等。候选人展示了扎实的技术基础,特别是在组件开发方面有深入实践,如瀑布流组件的性能优化方案。同时,候选人也表现出对新技术的学习能力,如对Vue3响应式原理的理解。面试最后,候选人就岗位内容和职业发展提出了针对性的问题,表现出良好的职业态度。