前端面试


Vue与前端开发面试题集合

一、Vue核心基础

1. Vue响应式原理

问题: Vue 2和Vue 3的响应式原理有什么区别?

答案:

  • Vue 2:使用Object.defineProperty()劫持对象属性的getter和setter,通过依赖收集和派发更新实现响应式。但无法检测对象属性的添加和删除,数组索引和length的修改。
  • Vue 3:使用Proxy代理整个对象,可以监听对象属性的添加、删除,数组索引和length的变化,性能更好且更完整。

2. computed vs watch

问题: computed和watch的区别和使用场景?

答案:

  • computed:计算属性,有缓存,依赖的响应式数据变化时才重新计算,适合模板渲染、多个数据联合计算的场景
  • watch:侦听器,无缓存,每次数据变化都执行,适合数据变化时执行异步操作或开销较大的操作

3. Vue生命周期

问题: 详细说明Vue的生命周期钩子及应用场景?

答案:

  • beforeCreate:实例初始化后,数据观测和事件配置之前
  • created:实例创建完成,可访问data、methods,常用于数据初始化、API调用
  • beforeMount:挂载开始前,render函数首次被调用
  • mounted:实例挂载完成,可访问DOM,常用于DOM操作、第三方库初始化
  • beforeUpdate:数据更新时,虚拟DOM重新渲染前
  • updated:虚拟DOM重新渲染后
  • beforeUnmount/beforeDestroy:实例销毁前,常用于清理定时器、解绑事件
  • unmounted/destroyed:实例销毁后

4. 组件通信方式

问题: Vue组件间有哪些通信方式?

答案:

  • Props / $emit:父子组件通信
  • $parent / $children:访问父子组件实例(不推荐)
  • ref:获取组件实例
  • provide / inject:跨层级组件通信
  • EventBus:事件总线(Vue 3中需自行实现)
  • Vuex / Pinia:状态管理
  • $attrs / $listeners:透传属性和事件

5. v-if vs v-show

问题: v-if和v-show的区别?

答案:

  • v-if:条件渲染,切换时会销毁和重建DOM,有更高的切换开销,适合条件不频繁改变的场景
  • v-show:条件展示,通过CSS的display属性切换,有更高的初始渲染开销,适合频繁切换的场景

二、Vue进阶

6. 虚拟DOM与Diff算法

问题: 什么是虚拟DOM?Vue的Diff算法是如何工作的?

答案:
虚拟DOM是用JavaScript对象描述真实DOM的树形结构。Diff算法采用同层比较策略:

  • 只比较同一层级的节点
  • 使用key优化列表对比(就地复用或移动元素)
  • Vue 3的优化:静态标记、静态提升、事件缓存、Block Tree

7. keep-alive原理

问题: keep-alive的作用和实现原理?

答案:
keep-alive是Vue内置组件,用于缓存不活动的组件实例。原理:

  • 使用LRU缓存策略存储组件实例
  • 通过include/exclude控制缓存范围
  • 缓存的组件有activated和deactivated生命周期钩子
  • 不会在DOM中留下任何痕迹

8. nextTick原理

问题: $nextTick的作用和实现原理?

答案:
作用:在下次DOM更新循环结束后执行回调,用于获取更新后的DOM。

实现原理:使用微任务(Promise.then、MutationObserver)或宏任务(setImmediate、setTimeout)降级方案,将回调推入异步队列,在当前执行栈清空后执行。

9. Vue 3 Composition API

问题: Composition API相比Options API有什么优势?

答案:

  • 更好的逻辑复用:通过composables函数封装和复用逻辑
  • 更好的类型推导:对TypeScript更友好
  • 更灵活的代码组织:相关逻辑可以组织在一起,不必分散在不同选项中
  • 更小的打包体积:tree-shaking友好

10. 自定义指令

问题: 如何实现一个自定义指令?

答案:

// Vue 3
app.directive('focus', {
  mounted(el) {
    el.focus()
  }
})

// Vue 2
Vue.directive('focus', {
  inserted(el) {
    el.focus()
  }
})

三、前端基础

11. 闭包

问题: 什么是闭包?有什么作用和缺点?

答案:
闭包是指有权访问另一个函数作用域中变量的函数。

作用:数据私有化、函数柯里化、模块化、回调函数。

缺点:内存占用较大,可能导致内存泄漏。

12. 原型链

问题: 解释JavaScript的原型链?

答案:
每个对象都有proto属性指向其构造函数的prototype,形成原型链。当访问对象属性时,会沿着原型链向上查找,直到找到该属性或到达链顶端(null)。

13. 事件循环

问题: 解释浏览器的事件循环机制?

答案:

  • 执行同步代码(执行栈)
  • 清空微任务队列(Promise.then、MutationObserver)
  • 执行一个宏任务(setTimeout、setInterval、I/O)
  • 渲染页面
  • 重复以上步骤

14. 跨域解决方案

问题: 常见的跨域解决方案有哪些?

答案:

  • CORS:服务端设置Access-Control-Allow-Origin
  • JSONP:利用script标签不受同源策略限制
  • 代理:开发环境配置devServer proxy,生产环境使用nginx反向代理
  • postMessage:跨窗口通信

15. HTTP缓存

问题: HTTP缓存机制有哪些?

答案:

  • 强缓存:Cache-Control(max-age)、Expires
  • 协商缓存:Last-Modified/If-Modified-Since、ETag/If-None-Match

四、性能优化

16. Vue性能优化

问题: Vue应用有哪些性能优化方法?

答案:

  • 合理使用v-if和v-show
  • 使用key优化列表渲染
  • 路由懒加载、组件异步加载
  • 使用computed缓存计算结果
  • 使用keep-alive缓存组件
  • 虚拟滚动处理长列表
  • 图片懒加载
  • 避免不必要的组件抽象
  • 使用Object.freeze()冻结不需要响应式的数据

17. 浏览器渲染优化

问题: 如何优化浏览器渲染性能?

答案:

  • 减少重排(reflow)和重绘(repaint)
  • 使用transform和opacity实现动画
  • 使用will-change提示浏览器优化
  • 避免强制同步布局
  • 使用requestAnimationFrame
  • 减少DOM操作,使用文档片段
  • 使用CSS3硬件加速

18. 首屏加载优化

问题: 如何优化首屏加载速度?

答案:

  • 代码分割(Code Splitting)
  • 路由懒加载
  • 压缩资源(Gzip、Brotli)
  • 使用CDN
  • 图片优化(WebP、懒加载、雪碧图)
  • 服务端渲染(SSR)
  • 预加载关键资源(preload、prefetch)
  • 骨架屏提升感知性能

五、工程化

19. Webpack配置

问题: 说说Webpack的核心概念?

答案:

  • Entry:入口,指示Webpack从哪个文件开始构建
  • Output:输出,指示Webpack在哪里输出打包后的文件
  • Loader:转换器,处理非JavaScript文件
  • Plugin:插件,执行更广泛的任务(打包优化、压缩等)
  • Mode:模式,development/production

20. 前端安全

问题: 常见的前端安全问题及防御措施?

答案:

  • XSS(跨站脚本攻击):对用户输入进行转义,使用CSP,HttpOnly Cookie
  • CSRF(跨站请求伪造):使用Token验证,SameSite Cookie
  • 点击劫持:使用X-Frame-Options或CSP的frame-ancestors
  • SQL注入:使用参数化查询,后端验证
  • 敏感信息泄露:不在前端存储敏感信息,使用HTTPS

六、实战问题

21. 实现双向绑定

问题: 手写实现一个简单的双向绑定?

答案:

class Observer {
  constructor(data) {
    this.observe(data)
  }

  observe(data) {
    if (!data || typeof data !== 'object') return

    Object.keys(data).forEach(key => {
      this.defineReactive(data, key, data[key])
    })
  }

  defineReactive(obj, key, value) {
    this.observe(value)
    const dep = new Dep()

    Object.defineProperty(obj, key, {
      get() {
        Dep.target && dep.addSub(Dep.target)
        return value
      },
      set(newVal) {
        if (newVal === value) return
        value = newVal
        dep.notify()
      }
    })
  }
}

class Dep {
  constructor() {
    this.subs = []
  }

  addSub(watcher) {
    this.subs.push(watcher)
  }

  notify() {
    this.subs.forEach(watcher => watcher.update())
  }
}

22. 防抖与节流

问题: 实现防抖和节流函数?

答案:

// 防抖:延迟执行,多次触发只执行最后一次
function debounce(fn, delay) {
  let timer = null
  return function(...args) {
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(this, args)
    }, delay)
  }
}

// 节流:固定时间内只执行一次
function throttle(fn, delay) {
  let lastTime = 0
  return function(...args) {
    const now = Date.now()
    if (now - lastTime >= delay) {
      fn.apply(this, args)
      lastTime = now
    }
  }
}

23. 深拷贝

问题: 实现一个深拷贝函数?

答案:

function deepClone(obj, hash = new WeakMap()) {
  if (obj === null) return obj
  if (obj instanceof Date) return new Date(obj)
  if (obj instanceof RegExp) return new RegExp(obj)
  if (typeof obj !== 'object') return obj

  // 处理循环引用
  if (hash.has(obj)) return hash.get(obj)

  const cloneObj = new obj.constructor()
  hash.set(obj, cloneObj)

  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloneObj[key] = deepClone(obj[key], hash)
    }
  }

  return cloneObj
}

24. Promise实现

问题: 简单实现Promise的核心功能?

答案:

class MyPromise {
  constructor(executor) {
    this.state = 'pending'
    this.value = undefined
    this.reason = undefined
    this.onResolvedCallbacks = []
    this.onRejectedCallbacks = []

    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled'
        this.value = value
        this.onResolvedCallbacks.forEach(fn => fn())
      }
    }

    const reject = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.reason = reason
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }

    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }

  then(onFulfilled, onRejected) {
    if (this.state === 'fulfilled') {
      onFulfilled(this.value)
    }
    if (this.state === 'rejected') {
      onRejected(this.reason)
    }
    if (this.state === 'pending') {
      this.onResolvedCallbacks.push(() => {
        onFulfilled(this.value)
      })
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason)
      })
    }
  }
}

七、场景题

25. 大文件上传

问题: 如何实现大文件上传?

答案:

  • 文件切片(File.slice)
  • 计算文件hash(使用Web Worker + spark-md5)
  • 并发上传切片,控制并发数
  • 服务端合并切片
  • 断点续传(记录已上传的切片)
  • 进度显示

26. 长列表优化

问题: 如何优化包含数万条数据的列表渲染?

答案:

  • 虚拟滚动(只渲染可视区域的数据)
  • 分页加载或无限滚动
  • 使用Web Worker处理数据
  • 使用Object.freeze冻结数据
  • 合理使用key
  • 避免不必要的组件更新

八、准备建议

  1. 理解原理:不要只记答案,要理解背后的原理
  2. 动手实践:对于代码题,最好能手写一遍
  3. 关注实战:结合实际项目经验回答
  4. 准备问题:准备一些要问面试官的问题
  5. 最新技术:关注Vue 3、Vite等新技术动向


扫描二维码,在手机上阅读
收藏

设计师审美提升

表结构设计:权益流水、平台账单、支付订单、订单退款

评 论
请登录后再评论