Vue面试题整理

web前端
文章发布日期
2025-06-15
热度
0
本文共计
4105字
预计阅读
21分钟

1. 谈谈你对Vue的理解?

官方: Vue 是一套用于构建用户界面的渐进式框架,Vue 的核心库只关注视图层

1.1 声明式框架

Vue 的核心特点,用起来简单。那我们就有必要知道命令式和声明式的区别!

  • 早在JQ 的时代编写的代码都是命令式的,命令式框架重要特点就是关注过程
  • 声明式框架更加关注结果

    命令式的代码封装到了 Vuejs 中,过程靠 vuejs 来实现声明式代码更加简单,不需要关注实现,按照要求填代码就可以(给上原材料就出结果)

js 复制代码
- 命令式编程:
let numbers = [1,2,3,4,5]
let total = 0
for(let i = 0; i < numbers.length; i++){
  total += numbers[i] //关注了过程
  }
console.log(total)

-声明式编程:
let total2 = numbers.reduce(function (memo,current) {
  return memo + current
},0)
console.log(total2)

1.2 MVVM模式

说起MVVM,就要知道另一个东东那就是 MVC。为什么要有这些模式呢?
目的:职责划分、分层管理.

对于前端而言就是如何将数据同步到页面上,也是借鉴后端思想。

  • MVC模式:Backbone + underscore + jquery

    对于前端而言,数据变化无法同步到视图中。需要将逻辑聚拢在 controller 层。

  • MVVM 模式:映射关系的简化(隐藏controller)

    虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm(ViewModel的缩写) 这个变量名表示 Vue 实例。

1.3 采用虚拟DOM

传统更新页面,拼接一个完整的字符串 innerHTML 全部重新渲染,添加虚拟 DOM 后,可以比较新旧虚拟节点,找到变化在进行更新。虚拟 DOM 就是一个对象,用来描述真实 DOM 的
https://github.com/vuejs/vue/blob/main/src/core/vdom/vnode.ts

1.4 区分编译时(打包)和运行(浏览器)时

  • Vue 的渲染核心就是调用渲染(render)方法将虚拟 DOM 渲染成真实 DOM(缺点就是虚拟 DOM 编写麻烦)
  • 专门写个编译时可以将模板编译成虚拟 DOM(在构建的时候进行编译性能更高,不需要再运行的时候进行编译)

1.5 组件化

实现高内聚、低耦合、单向数据流

  • 组件化开发能大幅提高应用开发效率、测试性、复用性等
  • 降低更新范围,只重新渲染变化的组件

2. 谈谈你对 SPA 的理解?

2.1 理解基本概念

  • SPA(single-page application)单页应用,默认情况下我们编写 Vue、React都只有一个htm1 页面,并且提供一个挂载点,最终打包后会再此页面中引入对应的资源。(页面的渲染全部是由IS 动态进行渲染的)。切换页面时通过监听路由变化,渲染对应的页面 Clientside Rendering,客户端渲染 CSR
  • MPA(Multi-page application)多页应用,多个html页面。每个页面必须重复加载,js,css 等相关资源。(服务端返回完整的 html,同时数据也可以再后端进行获取一并返回“模板引擎”)。多页应用跳转需要整页资源刷新。Server side Rendering,服务器端渲染 SSR

    如何分清在哪渲染:HTML是在前端动态生成的“客户端渲染",在服务端处理好并返回的是“服务端渲染”。

2.2 优缺点

单页面应用(SPA) 多页面应用(MPA)
组成 一个主页面和页面组件 多个完整的页面
刷新方式 局部刷新 整页刷新
SEO 搜索引擎优化 无法实现 容易实现
页面切换 速度快,用户体验良好 切换加载资源,速度慢,用户体验差
维护成本 相对容易 相对复杂
  • 用户体验好、快,内容的改变不需要重新加载整个页面,服务端压力小。
  • SPA 应用不利于搜索引擎的抓取。
  • 首次渲染速度相对较慢(第一次返回空的 html,需要再次请求首屏数据)白屏时间长。

2.3 解决方案

  • 静态页面预渲染(Static Site Generation) SSG,在构建时生成完整的 html 页面。(就是在打包的时候,先将页面放到浏览器中运行一下,将HTML保存起来),仅适合静态页面网站。变化率不高的网站
  • SSR + CSR 的方式,首屏采用服务端染的方式,后续交互采用客户端染方式。例如:Nuxtjs

3. Vue 为什么需要虚拟 DOM?

3.1 基本概念

基本上所有框架都引入了虚拟 DOM 来对真实 DOM 进行抽象,也就是现在大家所熟知的 VNode 和 VDOM

  • Virtual DOM 就是用 js 对象来描述真实 DOM,是对真实 DOM 的抽象,由于直接操作 DOM 性能低但是 js 层的操作效率高,可以将 DOM 操作转化成对象操作,最终通过 diff 算法比对差异进行更新 DOM(减少了对真实 DOM 的操作)。
  • 虚拟 DOM 不依赖真实平台环境从而也可以实现跨平台。

3.2 VDOM 是如何生成的?

  • 在 vue 中我们常常会为组件编写模板 - template
  • 这个模板会被编译器编译为渲染函数 - render
  • 在接下来的挂载过程中会调用 render 函数,返回的对象就是虚拟 dom。
  • 会在后续的 patch 过程中进一步转化为 真实 dom。

3.3 VDOM 如何做 diff 的?

  • 挂载过程结束后,会记录第一次生成的 VDOM-oldVnode
  • 当响应式数据发生变化时,将会引起组件重新 render ,此时就会生成新的 VDOM-newVnode
  • 使用 oldVnodenewnodediff 操作,将更改的部分应到真实 DOM 上,从而转换为最小量的 dom 操作,高效更新视图。

4. 谈一谈对 Vue 组件化的理解

webcomponent 组件化的核心组成:板、属性、事件、插、生命周期。

组件化好处: 高内聚、可重用、可组合

  • 组件化开发能大幅提高应用开发效率、测试性、复用性等,
  • 降低更新范围,只重新渲染变化的组件;

4.1 伴随组件化的其它问题

  • Vue 中的每个组件都有一个渲染函数 watcher、effect。
  • 数据是响应式的,数据变化后会执行watcher或者effect。
  • 组件要合理的划分,如果不拆分组件,那更新的时候整个页面都要重新更新。
  • 如果过分的拆分组件会导致watcher、effect产生过多也会造成性能浪费。

5. 既然 Vue 通过数据劫持可以精准探测数据变化,为什么还需要虚拟 DOM 进行 diff 检测差异?

Vue 内部设计原因导致,Vue 设计的是每个组件一个 watcher (渲染 watcher),没有采用一个属性对应一个 watcher。这样会导致大量 watcher 的产生而且浪费内存,如果粒度过低也无法精准检测变化。所以采用 diff 算法+组件级 watcher。

6. 请说一下你对响应式数据的理解?

6.1 如何实现响应式数据

数组和对象类型当值变化时如何劫持到。对象内部通过 defineReactive 方法,使用object.defineProperty 将属性进行劫持(只会劫持已经存在的属性),数组则是通过重写数组方法来实现。多层对象是通过递归来实现劫持。Vue3 则采用 proxy

6.2 Vue2 处理缺陷

  • 在 Vue2 的时候使用 defineProperty 来进行数据的劫持,需要对属性进行重写添加 gettersetter 性能差。
  • 当新增属性和删除属性时无法监控变化。需要通过 $set$delete 实现
  • 数组不采用 defineProperty 来进行劫持(浪费性能,对所有索引进行劫持会造成性能浪费)需要对数组单独进行处理。
  • 对于 ES6 中新产生的 Map、Set 这些数据结构不支持。

6.3 Vue2 与 Vue3 实现对比

js 复制代码
function defineReactive(target,key,value{
  observer(value);
  Object.defineProperty(target,key,{
    get(){
      return value;
    },
    set(newvalue){
      if(value !== newvalue){
        value = newalue;
        observer(newvalue)
      }
    }
  })
}
function observer(data){
  if(typeof data !=='object'){
    return data
  }
  for(let key in data){
    defineReactive(data,key,data[key]);
  }
}
js 复制代码
let handler ={
  get(target, key){
    if(typeof target[key]==="object"){
      return new Proxy(target[key], handler);
    }
    return Reflect.get(target, key);
  },
  set(target, key,value){
    let oldvalue = target[key];
    if(oldvalue !== value){
      return Reflect.set(target, key,value);
    }
    return true;
  },
};
let proxy =new Proxy(obj, handler);

7. Vue 中如何检测数组变化?

7.1 实现数组劫持

  • 数组考虑性能原因没有用 defineProperty 对数组的每一项进行拦截,而是选择重写数组( push , shift , pop , splice , unshift , sort , reverse )方法。
  • 数组中如果是对象数据类型也会进行递归劫持

7.2 数组的缺点

  • 数组的索引和长度变化是无法监控到的