复盘行动-高频面试题【vue2篇】

564 阅读11分钟

image.png

借鉴了网上很多资料,整理了一些Vue2相关的高频面试题,方便自己随时查阅复习、查缺补漏,温故而知新!如有错误,欢迎评论区指正!

vue2八股文

你对 Vue 生命周期的理解

  • 生命周期是什么?

    Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载 Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。

  • vue2各(10)个生命周期的作用

    生命周期描述
    beforeCreate组件实例被创建之初,组件的属性生效之前
    created组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,$el 还不可用
    beforeMount在挂载开始之前被调用:相关的 render 函数首次被调用
    mountedel 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子
    beforeUpdate组件数据更新之前调用,发生在虚拟 DOM 打补丁之前
    update组件数据更新之后
    activitedkeep-alive 专属,组件被激活时调用
    deactivatedkeep-alive 专属,组件被销毁时调用
    beforeDestory组件销毁前调用
    destoryed组件销毁后调用

vue中的 ref 是什么?

ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。

new Vue() 做了什么

  • 合并配置
  • 初始化生命周期
  • 初始化事件
  • 初始化render函数
  • 调用 beforecreate钩子函数
  • 初始化state,包括datapropscomputed
  • 调用 created 钩子函数
  • 然后按照生命周期,调用 vm.$mount 挂载渲染;

Vue为什么要用虚拟Dom

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

Vue组件的 data 为什么必须是函数

组件中的 data 写成一个函数,数据以函数返回值形式定义。这样每复用一次组件,就会返回一份新的 data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。

而单纯的写成对象形式,就使得所有组件实例共用了一份 data,就会造成一个变了全都会变的结果。

谈谈对组件的理解

  • 组件化开发能大幅提高应用开发效率、测试性、复用性
  • 常用的组件化技术:属性、自定义事件、插槽
  • 降低更新范围,值重新渲染变化的组件
  • 高内聚、低耦合、单向数据流

Vue中的 Key 的作用是什么

key 的作用主要是为了高效的更新虚拟 DOM
另外 vue 中在使用相同标签名元素的过渡切换时,也会使用到 key 属性,其目的也是为了让 vue 可以区分它们,否则 vue 只会替换其内部属性而不会触发过渡效果。

watch和computed

watch 属性监听

是一个对象,键是需要观察的属性,值是对应回调函数,主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作。
监听属性的变化,需要在数据变化时执行异步或开销较大的操作时使用

computed 计算属性

属性的结果会被缓存,当computed中的函数所依赖的属性没有发生改变的时候,那么调用当前函数的时候结果会从缓存中读取。除非依赖的响应式属性变化时才会重新计算。
computed中的函数必须用return返回最终的结果
computed更高效,优先使用。data 不改变,computed 不更新

使用场景

  • watch:当一条数据影响多条数据的时候使用,例:搜索数据

  • computed:当一个属性受多个属性影响的时候使用,例:购物车商品结算功能

Vue2.x中如何检测数组的变化

Vue2.x 中实现检测数组变化的方法,是将数组的常用方法进行了重写。
Vue 将 data 中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组 api 时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。

vue组件的通信方式

  • props/$emit 父子组件通信

    父->子props,子->父 $on、$emit 获取父子组件实例 parent、children Ref 获取实例的方式调用组件的属性或者方法

    父->子孙 Provide、inject 官方不推荐使用,但是写组件库时很常用

  • $emit/$on 自定义事件 兄弟组件通信

    Event Bus 实现跨组件通信 Vue.prototype.$bus = new Vue() 自定义事件

  • vuex 跨级组件通信

    Vuex、$attrs、$listeners Provide、inject

插槽与作用域插槽的区别是什么

  • 插槽的作用是子组件提供了可替换模板,父组件可以更换模板的内容。

  • 作用域插槽给了子组件将数据返给父组件的能力,子组件一样可以复用,同时父组件也可以重新组织内容和样式。

vue2中的mixins是什么,存在什么问题?

项目变得复杂时,多个组件间有重复的逻辑时可以用 mixins 抽离出来。

  • 存在问题

mixins并不是完美的解决方案,会有一些问题
vue3提出的Composition API旨在解决这些问题【追求完美是要消耗一定的成本的,如开发成本】
场景:PC端新闻列表和详情页一样的右侧栏目,可以使用mixins进行混合
劣势:
1.变量来源不明确,不利于阅读
2.多mixins可能会造成命名冲突
3.mixins和组件可能出现多对多的关系,使得项目复杂度变高

Vue.use是干什么的

Vue.use是用来使用插件的。我们可以在插件中扩展全局组件、指令、原型方法等。 会调用install方法将Vue的构建函数默认传入,在插件中可以使用vue,无需依赖vue库

组件中写 name 选项有哪些好处

  • 可以通过名字找到对应的组件( 递归组件:组件自身调用自身 )
  • 可以通过 name 属性实现缓存功能(keep-alive
  • 可以通过 name 来识别组件(跨级组件通信时非常重要)
  • 使用 vue-devtools 调试工具里显示的组件名称是由 vue 中组件 name 决定的

 Vite 和Webpack的区别

Vite和Webpack都是现代化打包工具。vite相关生态没有webpack完善。

启动方式不一样,vite在启动的时候不需要打包。所以不用分析模块与模块之间的依赖关系,不用进行编译。
这种方式就类似于我们在使用某个UI框架的时候,可以对其进行按需加载。
vite也是这种机制,当浏览器请求某个模块时,再根据需要对模块内容进行编译。按需动态编译可以缩减编译时间,当项目越复杂,模块越多的情况下,vite明显优于webpack。

热更新方面,vite效率更高。当改动了某个模块的时候,也只用让浏览器重新请求该模块,不需要像webpack那样将模块以及模块依赖的模块全部编译一次。

原理相关

谈一谈对 MVVM 的理解

MVVM 是 Model-View-ViewModel 的缩写
我们都知道 Vue 是数据双向绑定的框架,双向绑定由三个重要部分构成:
数据层(Model):应用的数据及业务逻辑
视图层(View):应用的展示效果,各类UI组件
业务逻辑层(ViewModel):框架封装的核心,它负责将数据与视图关联起来

而上面的这个分层的架构方案,可以用一个专业术语进行称呼:MVVM 这里的控制层的核心功能便是 “数据双向绑定” 。

ViewModel 它的主要职责就是: 数据变化后更新视图、视图变化后更新数据。它还有两个主要部分组成:

  • 监听器(Observer):对所有数据的属性进行监听
  • 解析器(Compiler):对每个元素节点的指令进行扫描跟解析,根据指令模板替换数据,以及绑定相应的更新函数

Vue2 实现双向数据绑定原理是什么?

Vue2.x 采用数据劫持结合发布订阅模式(PubSub 模式)的方式,通过 Object.defineProperty 来劫持各个属性的 setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。

Vue 的数据双向绑定整合了 ObserverCompileWatcher 三者。
通过 Observer 来监听自己的 model 的数据变化,
通过 Compile 来解析编译模板指令,
最终利用 Watcher 搭起 ObserverCompile 之间的通信桥梁,达到数据变化->视图更新
视图交互变化(例如 input 操作)->数据 model 变更的双向绑定效果。

v-model双向绑定指令的原理是什么?

v-model 指令本质就是: value + input 方法的语法糖。可以通过 model 属性的 prop 和 event 属性来进行自定义。

原生的 v-model,会根据标签的不同生成不同的事件和属性:

  • text 和 textarea 元素使用 value 属性和 input 事件
  • checkbox 和 radio 使用 checked 属性和 change 事件
  • select 字段将 value 作为 prop 并将 change 作为事件

Vue的diff算法原理是什么

Vue的diff算法是平级比较,不考虑跨级比较的情况。内部采用深度递归的方式+双指针方式比较

  • 先比较两个节点是不是相同节点
  • 相同节点比较属性,复用老节点
  • 先比较儿子节点,考虑老节点和新节点儿子的情况
  • 优化比较:头头、尾尾、头尾、尾头
  • 比对查找,进行复用

nextTick 是什么?实现原理

在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后立即使用 nextTick 来获取更新后的 DOM。

使用场景:在于响应式数据变化后想获取DOM更新后的情况

原理:nextTick主要使用了宏任务和微任务。 根据执行环境分别尝试采用Promise、MutationObserver、setImmediate,如果以上都不行则采用setTimeout定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列。

Vue 的异步更新策略原理

  • Vue的 DOM 更新是异步的,当数据变化时,Vue 就会开启一个队列,然后把在同一个 事件循环 中观察到数据变化的 watcher 推送进这个队列;
  • 同时如果这个 watcher 被触发多次,只会被推送到队列一次;
  • 而在下一个 事件循环 时,Vue会清空这个队列,并进行必要的 DOM 更新;
  • 这也就是响应式的数据 for 循环改变了100次视图也只更新一次的原因;

Vue.set方法是如何实现的

  • vue给对象和数组本身都增加了dep属性

  • 当给对象新增不存在的属性的时候,就会触发对象依赖的watcher去更新

  • 当修改数组索引的时候,就调用数组本身的splice方法去更新数组

keep-alive的实现原理

作用:实现组件缓存,保持这些组件的状态,以避免反复渲染导致的性能问题。 需要缓存组件 频繁切换,不需要重复渲染

场景:tabs标签页 后台导航,vue性能优化

原理:Vue.js内部将DOM节点抽象成了一个个的VNode节点,keep-alive组件的缓存也是基于VNode节点的而不是直接存储DOM结构。它将满足条件(pruneCache与pruneCache)的组件在cache对象中缓存起来,在需要重新渲染的时候再将vnode节点从cache对象中取出并渲染。

scoped 的实现原理

vue 中的 scoped 属性的效果主要通过 PostCSS 转译实现的。PostCSS 给一个组件中的所有 DOM 添加了一个独一无二的动态属性,然后,给 CSS 选择器额外添加一个对应的属性选择器来选择该组件中 DOM,这种做法使得样式只作用于含有该属性的 DOM,即组件内部 DOM。

vue中从 template 到 render 函数的处理过程(compiler的工作原理)

Vue中有个独特的编译器模块,称为“compiler”,它的主要作用是将用户编写的 template 编译为js中可执行的 render函数

之所以需要这个编译过程是为了便于前端程序员能高效的编写视图模板。相比而言,我们还是更愿意用HTML来编写视图,直观且高效。手写render函数不仅效率低下,而且失去了编译期的优化能力。

在Vue中编译器会先对 template 进行解析,这一步称为 parse 结束之后会得到一个jS对象,我们称为:抽象语法树AST
然后是对AST进行深加工的转换过程,这一步称为 transform(转换)
最后将前面得到的AST生成为JS代码,也就是render函数